Change RouterFleXML so that filters element gets optional
[metaproxy-moved-to-github.git] / src / router_flexml.cpp
1 /* $Id: router_flexml.cpp,v 1.15 2006-01-18 10:30:58 adam Exp $
2    Copyright (c) 2005, Index Data.
3
4 %LICENSE%
5  */
6
7 #include "config.hpp"
8 #include "xmlutil.hpp"
9 #include "router_flexml.hpp"
10 #include "factory_filter.hpp"
11 #include "factory_static.hpp"
12
13 #include <iostream>
14 #include <map>
15 #include <list>
16
17 #include <boost/shared_ptr.hpp>
18
19 #include <libxml/xmlversion.h>
20 #include <libxml/parser.h>
21 #include <libxml/tree.h>
22
23
24 namespace yp2 {
25     class RouterFleXML::Route {
26         friend class RouterFleXML::Rep;
27         friend class RouterFleXML::Pos;
28         friend class RouterFleXML;
29         std::list<boost::shared_ptr<const yp2::filter::Base> > m_list;
30     };
31     class RouterFleXML::Rep {
32         friend class RouterFleXML;
33         friend class RouterFleXML::Pos;
34         Rep();
35
36         void base(xmlDocPtr doc, yp2::FactoryFilter &factory);
37
38         typedef std::map<std::string,
39                          boost::shared_ptr<const yp2::filter::Base > >
40                          IdFilterMap ;
41
42         IdFilterMap m_id_filter_map;
43
44         std::map<std::string,RouterFleXML::Route> m_routes;
45
46         std::string m_start_route;
47
48         void parse_xml_config_dom(xmlDocPtr doc);
49
50         void parse_xml_filters(xmlDocPtr doc, const xmlNode *node);
51         void parse_xml_routes(xmlDocPtr doc, const xmlNode *node);
52
53         bool m_xinclude;
54     private:
55         FactoryFilter *m_factory; // TODO shared_ptr
56     };
57
58     class RouterFleXML::Pos : public RoutePos {
59     public:
60         virtual const filter::Base *move(const char *route);
61         virtual RoutePos *clone();
62         virtual ~Pos();
63         yp2::RouterFleXML::Rep *m_p;
64
65         std::map<std::string, 
66                  RouterFleXML::Route>::iterator m_route_it;
67         std::list<boost::shared_ptr <const yp2::filter::Base> >::iterator m_filter_it;
68     };
69 }
70
71 void yp2::RouterFleXML::Rep::parse_xml_filters(xmlDocPtr doc,
72                                                const xmlNode *node)
73 {
74     unsigned int filter_nr = 0;
75     while(node && yp2::xml::check_element_yp2(node, "filter"))
76     {
77         filter_nr++;
78
79         const struct _xmlAttr *attr;
80         std::string id_value;
81         std::string type_value;
82         for (attr = node->properties; attr; attr = attr->next)
83         {
84             std::string name = std::string((const char *) attr->name);
85             std::string value;
86
87             if (attr->children && attr->children->type == XML_TEXT_NODE)
88                 value = std::string((const char *)attr->children->content);
89
90             if (name == "id")
91                 id_value = value;
92             else if (name == "type")
93                 type_value = value;
94             else
95                 throw yp2::XMLError("Only attribute id or type allowed"
96                                     " in filter element. Got " + name);
97         }
98
99         yp2::filter::Base* filter_base = m_factory->create(type_value);
100
101         filter_base->configure(node);
102
103         if (m_id_filter_map.find(id_value) != m_id_filter_map.end())
104             throw yp2::XMLError("Filter " + id_value + " already defined");
105
106         m_id_filter_map[id_value] =
107             boost::shared_ptr<yp2::filter::Base>(filter_base);
108
109         node = yp2::xml::jump_to_next(node, XML_ELEMENT_NODE);
110     }
111 }
112
113 void yp2::RouterFleXML::Rep::parse_xml_routes(xmlDocPtr doc,
114                                               const xmlNode *node)
115 {
116     yp2::xml::check_element_yp2(node, "route");
117
118     unsigned int route_nr = 0;
119     while(yp2::xml::is_element_yp2(node, "route"))
120     {
121         route_nr++;
122
123         const struct _xmlAttr *attr;
124         std::string id_value;
125         for (attr = node->properties; attr; attr = attr->next)
126         {
127             std::string name = std::string((const char *) attr->name);
128             std::string value;
129             
130             if (attr->children && attr->children->type == XML_TEXT_NODE)
131                 value = std::string((const char *)attr->children->content);
132             
133             if (name == "id")
134                 id_value = value;
135             else
136                 throw yp2::XMLError("Only attribute 'id' allowed for"
137                                          " element 'route'."
138                                          " Got " + name);
139         }
140
141         Route route;
142
143         // process <filter> nodes in third level
144         const xmlNode* node3 = yp2::xml::jump_to_children(node, XML_ELEMENT_NODE);
145
146         unsigned int filter3_nr = 0;
147         while(node3 && yp2::xml::check_element_yp2(node3, "filter"))
148         {
149             filter3_nr++;
150             
151             const struct _xmlAttr *attr;
152             std::string refid_value;
153             std::string type_value;
154             for (attr = node3->properties; attr; attr = attr->next)
155             {
156                 std::string name = std::string((const char *) attr->name);
157                 std::string value;
158                 
159                 if (attr->children && attr->children->type == XML_TEXT_NODE)
160                     value = std::string((const char *)attr->children->content);
161                 
162                 if (name == "refid")
163                     refid_value = value;
164                 else if (name == "type")
165                     type_value = value;
166                 else
167                     throw yp2::XMLError("Only attribute 'refid' or 'type'"
168                                         " allowed for element 'filter'."
169                                         " Got " + name);
170             }
171             if (refid_value.length())
172             {
173                 std::map<std::string,
174                     boost::shared_ptr<const yp2::filter::Base > >::iterator it;
175                 it = m_id_filter_map.find(refid_value);
176                 if (it == m_id_filter_map.end())
177                     throw yp2::XMLError("Unknown filter refid "
178                                         + refid_value);
179                 else
180                     route.m_list.push_back(it->second);
181             }
182             else if (type_value.length())
183             {
184                 yp2::filter::Base* filter_base = m_factory->create(type_value);
185
186                 filter_base->configure(node3);
187                 
188                 route.m_list.push_back(
189                     boost::shared_ptr<yp2::filter::Base>(filter_base));
190             }
191             node3 = yp2::xml::jump_to_next(node3, XML_ELEMENT_NODE);
192             
193         }
194         std::map<std::string,RouterFleXML::Route>::iterator it;
195         it = m_routes.find(id_value);
196         if (it != m_routes.end())
197             throw yp2::XMLError("Route id='" + id_value
198                                 + "' already exist");
199         else
200             m_routes[id_value] = route;
201         node = yp2::xml::jump_to_next(node, XML_ELEMENT_NODE);
202     }
203 }
204
205 void yp2::RouterFleXML::Rep::parse_xml_config_dom(xmlDocPtr doc)
206 {
207     if (!doc)
208         throw yp2::XMLError("Empty XML Document");
209     
210     const xmlNode* root = xmlDocGetRootElement(doc);
211     
212     yp2::xml::check_element_yp2(root,  "yp2");
213
214     // process <start> node which is expected first element node
215     const xmlNode* node = yp2::xml::jump_to_children(root, XML_ELEMENT_NODE);
216     if (yp2::xml::check_element_yp2(node, "start"))
217     {
218         const struct _xmlAttr *attr;
219         std::string id_value;
220         for (attr = node->properties; attr; attr = attr->next)
221         {
222             std::string name = std::string((const char *) attr->name);
223             std::string value;
224
225             if (attr->children && attr->children->type == XML_TEXT_NODE)
226                 value = std::string((const char *)attr->children->content);
227
228             if (name == "route")
229                 m_start_route = value;
230             else
231                 throw yp2::XMLError("Only attribute start allowed"
232                                     " in element 'start'. Got " + name);
233         }
234         node = yp2::xml::jump_to_next(node, XML_ELEMENT_NODE);
235     }
236     // process <filters> node if given
237     if (yp2::xml::is_element_yp2(node, "filters"))
238     {
239         parse_xml_filters(doc, yp2::xml::jump_to_children(node,
240                                                           XML_ELEMENT_NODE));
241                       
242         node = yp2::xml::jump_to_next(node, XML_ELEMENT_NODE);
243     }
244     // process <routes> node which is expected third element node
245     yp2::xml::check_element_yp2(node, "routes");
246     
247     parse_xml_routes(doc, yp2::xml::jump_to_children(node, XML_ELEMENT_NODE));
248
249     node = yp2::xml::jump_to_next(node, XML_ELEMENT_NODE);
250     if (node)
251     {
252         throw yp2::XMLError("Unexpected element " 
253                             + std::string((const char *)node->name));
254     }
255 }        
256
257 yp2::RouterFleXML::Rep::Rep() : m_xinclude(false)
258 {
259 }
260
261 void yp2::RouterFleXML::Rep::base(xmlDocPtr doc, yp2::FactoryFilter &factory)
262 {
263     m_factory = &factory;
264     parse_xml_config_dom(doc);
265     m_start_route = "start";
266 }
267
268 yp2::RouterFleXML::RouterFleXML(xmlDocPtr doc, yp2::FactoryFilter &factory)
269     : m_p(new Rep)
270 {
271     m_p->base(doc, factory);
272 }
273
274 yp2::RouterFleXML::RouterFleXML(std::string xmlconf, yp2::FactoryFilter &factory) 
275     : m_p(new Rep)
276 {            
277     LIBXML_TEST_VERSION;
278     
279     xmlDocPtr doc = xmlParseMemory(xmlconf.c_str(),
280                                    xmlconf.size());
281     if (!doc)
282         throw yp2::XMLError("xmlParseMemory failed");
283     else
284     {
285         m_p->base(doc, factory);
286         xmlFreeDoc(doc);
287     }
288 }
289
290 yp2::RouterFleXML::~RouterFleXML()
291 {
292 }
293
294 const yp2::filter::Base *yp2::RouterFleXML::Pos::move(const char *route)
295 {
296     if (route && *route)
297     {
298         std::cout << "move to " << route << "\n";
299         m_route_it = m_p->m_routes.find(route);
300         if (m_route_it == m_p->m_routes.end())
301         {
302             std::cout << "no such route " << route << "\n";
303             throw yp2::XMLError("bad route " + std::string(route));
304         }
305         m_filter_it = m_route_it->second.m_list.begin();
306     }
307     if (m_filter_it == m_route_it->second.m_list.end())
308         return 0;
309     const yp2::filter::Base *f = (*m_filter_it).get();
310     m_filter_it++;
311     return f;
312 }
313
314 yp2::RoutePos *yp2::RouterFleXML::createpos() const
315 {
316     yp2::RouterFleXML::Pos *p = new yp2::RouterFleXML::Pos;
317
318     p->m_route_it = m_p->m_routes.find(m_p->m_start_route);
319     if (p->m_route_it == m_p->m_routes.end())
320     {
321         delete p;
322         return 0;
323     }
324     p->m_filter_it = p->m_route_it->second.m_list.begin();
325     p->m_p = m_p.get();
326     return p;
327 }
328
329 yp2::RoutePos *yp2::RouterFleXML::Pos::clone()
330 {
331     yp2::RouterFleXML::Pos *p = new yp2::RouterFleXML::Pos;
332     p->m_filter_it = m_filter_it;
333     p->m_route_it = m_route_it;
334     p->m_p = m_p;
335     return p;
336 }
337
338 yp2::RouterFleXML::Pos::~Pos()
339 {
340 }
341
342
343 /*
344  * Local variables:
345  * c-basic-offset: 4
346  * indent-tabs-mode: nil
347  * c-file-style: "stroustrup"
348  * End:
349  * vim: shiftwidth=4 tabstop=8 expandtab
350  */