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