808a9878a16a0d83e2afd623f2da745cdbc1af7b
[ginac.git] / ginac / class_info.h
1 /** @file class_info.h
2  *
3  *  Helper templates to provide per-class information for class hierarchies. */
4
5 /*
6  *  GiNaC Copyright (C) 1999-2003 Johannes Gutenberg University Mainz, Germany
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #ifndef __GINAC_CLASS_INFO_H__
24 #define __GINAC_CLASS_INFO_H__
25
26 #include <cstddef> // for size_t
27 #include <vector>
28 #include <map>
29 #include <string>
30 #include <iostream>
31 #include <iomanip>
32
33 namespace GiNaC {
34
35 // OPT is the class that stores the actual per-class data. It must provide
36 // get_name(), get_parent_name() and get_id() members.
37
38 template <class OPT>
39 class class_info {
40 public:
41         class_info(const OPT & o) : options(o), next(first), parent(NULL)
42         {
43                 first = this;
44                 parents_identified = false;
45         }
46
47         /** Get pointer to class_info of parent class (or NULL). */
48         class_info *get_parent() const
49         {
50                 identify_parents();
51                 return parent;
52         }
53
54         /** Find class_info by name. */
55         static const class_info *find(const std::string &class_name)
56         {
57                 // Use a map for faster lookup. The registered_class_info list doesn't
58                 // change at run-time, so it's sufficient to construct the map once
59                 // on the first trip through this function.
60                 typedef std::map<std::string, const class_info *> name_map_type;
61                 static name_map_type name_map;
62                 static bool name_map_initialized = false;
63
64                 if (!name_map_initialized) {
65                         // Construct map
66                         const class_info *p = first;
67                         while (p) {
68                                 name_map[p->options.get_name()] = p;
69                                 p = p->next;
70                         }
71                         name_map_initialized = true;
72                 }
73
74                 typename name_map_type::const_iterator it = name_map.find(class_name);
75                 if (it == name_map.end())
76                         throw (std::runtime_error("class '" + class_name + "' not registered"));
77                 else
78                         return it->second;
79         }
80
81 private:
82         struct tree_node {
83                 tree_node(class_info *i) : info(i) {}
84                 void add_child(tree_node *n) { children.push_back(n); }
85
86                 std::vector<tree_node *> children;
87                 class_info *info;
88         };
89
90         static void dump_tree(tree_node *n, const std::string & prefix, bool verbose)
91         {
92                 std::string name = n->info->options.get_name();
93                 std::cout << name;
94                 if (verbose)
95                         std::cout << " [ID 0x" << std::hex << std::setw(8) << std::setfill('0') << n->info->options.get_id() << std::dec << "]" << std::endl;
96
97                 size_t num_children = n->children.size();
98                 if (num_children) {
99                         for (size_t i = 0; i < num_children; ++i) {
100                                 if (verbose) {
101                                         std::cout << prefix << " +- ";
102                                         if (i == num_children - 1)
103                                                 dump_tree(n->children[i], prefix + "    ", verbose);
104                                         else
105                                                 dump_tree(n->children[i], prefix + " |  ", verbose);
106                                 } else {
107                                         std::string spaces(name.size(), ' ');
108                                         if (i > 0)
109                                                 std::cout << prefix << spaces;
110                                         if (num_children == 1)
111                                                 std::cout << " --- ";
112                                         else if (i > 0)
113                                                 std::cout << "  +- ";
114                                         else
115                                                 std::cout << " -+- ";
116                                         if (i == num_children - 1)
117                                                 dump_tree(n->children[i], prefix + spaces + "     ", verbose);
118                                         else
119                                                 dump_tree(n->children[i], prefix + spaces + "  |  ", verbose);
120                                 }
121                         }
122                 } else if (!verbose)
123                         std::cout << std::endl;
124         }
125
126 public:
127         /** Dump class hierarchy to std::cout. */
128         static void dump_hierarchy(bool verbose = false)
129         {
130                 identify_parents();
131
132                 // Create tree nodes for all class_infos
133                 std::vector<tree_node> tree;
134                 for (class_info *p = first; p; p = p->next)
135                         tree.push_back(tree_node(p));
136
137                 // Identify children for all nodes and find the root
138                 tree_node *root = NULL;
139                 for (typename std::vector<tree_node>::iterator i = tree.begin(); i != tree.end(); ++i) {
140                         class_info *p = i->info->get_parent();
141                         if (p) {
142                                 for (typename std::vector<tree_node>::iterator j = tree.begin(); j != tree.end(); ++j) {
143                                         if (j->info == p) {
144                                                 j->add_child(&*i);
145                                                 break;
146                                         }
147                                 }
148                         } else
149                                 root = &*i;
150                 }
151
152                 // Print hierarchy tree starting at the root
153                 dump_tree(root, "", verbose);
154         }
155
156         OPT options;
157
158 private:
159         static void identify_parents()
160         {
161                 if (!parents_identified) {
162                         for (class_info *p = first; p; p = p->next) {
163                                 const char *parent_name = p->options.get_parent_name();
164                                 for (class_info *q = first; q; q = q->next) {
165                                         if (strcmp(q->options.get_name(), parent_name) == 0) {
166                                                 p->parent = q;
167                                                 break;
168                                         }
169                                 }
170                         }
171                         parents_identified = true;
172                 }
173         }
174
175         static class_info *first;
176         class_info *next;
177         mutable class_info *parent;
178
179         static bool parents_identified;
180 };
181
182 template <class OPT> class_info<OPT> *class_info<OPT>::first = NULL;
183 template <class OPT> bool class_info<OPT>::parents_identified = false;
184
185 } // namespace GiNaC
186
187 #endif // ndef __GINAC_CLASS_INFO_H__