1 /*
2 * This source file is part of libRocket, the HTML/CSS Interface Middleware
3 *
4 * For the latest information, see http://www.librocket.com
5 *
6 * Copyright (c) 2008-2010 CodePoint Ltd, Shift Technology Ltd
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 *
26 */
27
28 #include "precompiled.h"
29 #include "../../Include/Rocket/Core/XMLParser.h"
30 #include "DocumentHeader.h"
31 #include "../../Include/Rocket/Core/Log.h"
32 #include "../../Include/Rocket/Core/XMLNodeHandler.h"
33
34 namespace Rocket {
35 namespace Core {
36
37 typedef std::map< String, XMLNodeHandler* > NodeHandlers;
38 static NodeHandlers node_handlers;
39 static XMLNodeHandler* default_node_handler = NULL;
40
XMLParser(Element * root)41 XMLParser::XMLParser(Element* root)
42 {
43 RegisterCDATATag("script");
44
45 // Add the first frame.
46 ParseFrame frame;
47 frame.node_handler = NULL;
48 frame.child_handler = NULL;
49 frame.element = root;
50 frame.tag = "";
51 stack.push(frame);
52
53 active_handler = NULL;
54
55 header = new DocumentHeader();
56 }
57
~XMLParser()58 XMLParser::~XMLParser()
59 {
60 delete header;
61 }
62
63 // Registers a custom node handler to be used to a given tag.
RegisterNodeHandler(const String & _tag,XMLNodeHandler * handler)64 XMLNodeHandler* XMLParser::RegisterNodeHandler(const String& _tag, XMLNodeHandler* handler)
65 {
66 String tag = _tag.ToLower();
67
68 // Check for a default node registration.
69 if (tag.Empty())
70 {
71 if (default_node_handler != NULL)
72 default_node_handler->RemoveReference();
73
74 default_node_handler = handler;
75 default_node_handler->AddReference();
76 return default_node_handler;
77 }
78
79 NodeHandlers::iterator i = node_handlers.find(tag);
80 if (i != node_handlers.end())
81 (*i).second->RemoveReference();
82
83 node_handlers[tag] = handler;
84 handler->AddReference();
85
86 return handler;
87 }
88
89 // Releases all registered node handlers. This is called internally.
ReleaseHandlers()90 void XMLParser::ReleaseHandlers()
91 {
92 if (default_node_handler != NULL)
93 {
94 default_node_handler->RemoveReference();
95 default_node_handler = NULL;
96 }
97
98 for (NodeHandlers::iterator i = node_handlers.begin(); i != node_handlers.end(); ++i)
99 (*i).second->RemoveReference();
100
101 node_handlers.clear();
102 }
103
GetDocumentHeader()104 DocumentHeader* XMLParser::GetDocumentHeader()
105 {
106 return header;
107 }
108
GetSourceURL() const109 const URL& XMLParser::GetSourceURL() const
110 {
111 return xml_source->GetSourceURL();
112 }
113
114 // Pushes the default element handler onto the parse stack.
PushDefaultHandler()115 void XMLParser::PushDefaultHandler()
116 {
117 active_handler = default_node_handler;
118 }
119
PushHandler(const String & tag)120 bool XMLParser::PushHandler(const String& tag)
121 {
122 NodeHandlers::iterator i = node_handlers.find(tag.ToLower());
123 if (i == node_handlers.end())
124 return false;
125
126 active_handler = (*i).second;
127 return true;
128 }
129
130 /// Access the current parse frame
GetParseFrame() const131 const XMLParser::ParseFrame* XMLParser::GetParseFrame() const
132 {
133 return &stack.top();
134 }
135
136 /// Called when the parser finds the beginning of an element tag.
HandleElementStart(const String & _name,const XMLAttributes & _attributes)137 void XMLParser::HandleElementStart(const String& _name, const XMLAttributes& _attributes)
138 {
139 String name = _name.ToLower();
140 XMLAttributes attributes;
141
142 String key;
143 Variant* value;
144 int pos = 0;
145 while (_attributes.Iterate(pos, key, value))
146 {
147 attributes.Set(key.ToLower(), *value);
148 }
149
150 // Check for a specific handler that will override the child handler.
151 NodeHandlers::iterator itr = node_handlers.find(name);
152 if (itr != node_handlers.end())
153 active_handler = (*itr).second;
154
155 // Store the current active handler, so we can use it through this function (as active handler may change)
156 XMLNodeHandler* node_handler = active_handler;
157
158 Element* element = NULL;
159
160 // Get the handler to handle the open tag
161 if (node_handler)
162 {
163 element = node_handler->ElementStart(this, name, attributes);
164 }
165
166 // Push onto the stack
167 ParseFrame frame;
168 frame.node_handler = node_handler;
169 frame.child_handler = active_handler;
170 frame.element = element != NULL ? element : stack.top().element;
171 frame.tag = name;
172 stack.push(frame);
173 }
174
175 /// Called when the parser finds the end of an element tag.
HandleElementEnd(const String & _name)176 void XMLParser::HandleElementEnd(const String& _name)
177 {
178 String name = _name.ToLower();
179
180 // Copy the top of the stack
181 ParseFrame frame = stack.top();
182 // Pop the frame
183 stack.pop();
184 // Restore active handler to the previous frame's child handler
185 active_handler = stack.top().child_handler;
186
187 // Check frame names
188 if (name != frame.tag)
189 {
190 Log::Message(Log::LT_ERROR, "Closing tag '%s' mismatched on %s:%d was expecting '%s'.", name.CString(), GetSourceURL().GetURL().CString(), GetLineNumber(), frame.tag.CString());
191 }
192
193 // Call element end handler
194 if (frame.node_handler)
195 {
196 frame.node_handler->ElementEnd(this, name);
197 }
198 }
199
200 /// Called when the parser encounters data.
HandleData(const String & data)201 void XMLParser::HandleData(const String& data)
202 {
203 if (stack.top().node_handler)
204 stack.top().node_handler->ElementData(this, data);
205 }
206
207 }
208 }
209