1 using System;
2 using System.Collections;
3
4
5
6
7
8 namespace System.Xml
9 {
10 public class XmlProcessingException : Exception
11 {
12 public XmlProcessingException(const string& message_) :
13 base(message_)
14 {
15 }
16 }
17 public class Entity
18 {
19 public virtual ~Entity()
20 {
21 }
22 public virtual bool IsInternalEntity() const
23 {
24 return false;
25 }
26 }
27 public class InternalEntity : Entity
28 {
29 public InternalEntity(const ustring& value_) :
30 value(value_)
31 {
32 }
33 public const ustring& Value() const
34 {
35 return value;
36 }
37 public override bool IsInternalEntity() const
38 {
39 return true;
40 }
41 private ustring value;
42 }
43 public class XmlProcessor
44 {
45 public suppress XmlProcessor(const XmlProcessor&);
46 public suppress XmlProcessor(XmlProcessor&&);
47 public XmlProcessor(TrivialLexer& lexer_, XmlContentHandler* contentHandler_) :
48 lexer(lexer_), contentHandler(contentHandler_), attValue()
49 {
50 InternalEntity* quot = new InternalEntity(u"\"");
51 entityMap[u"quot"] = quot;
52 InternalEntity* amp = new InternalEntity(u"&");
53 entityMap[u"amp"] = amp;
54 InternalEntity* apos = new InternalEntity(u"\'");
55 entityMap[u"apos"] = apos;
56 InternalEntity* lt = new InternalEntity(u"<");
57 entityMap[u"lt"] = lt;
58 InternalEntity* gt = new InternalEntity(u">");
59 entityMap[u"gt"] = gt;
60 entities.Add(UniquePtr<Entity>(quot));
61 entities.Add(UniquePtr<Entity>(amp));
62 entities.Add(UniquePtr<Entity>(apos));
63 entities.Add(UniquePtr<Entity>(lt));
64 entities.Add(UniquePtr<Entity>(gt));
65 }
66 public suppress XmlProcessor& operator=(const XmlProcessor&);
67 public suppress XmlProcessor&& operator=(XmlProcessor&&);
68 public const ustring& AttValue() const
69 {
70 return attValue;
71 }
72 public ustring& AttValue()
73 {
74 return attValue;
75 }
76 public TrivialLexer& Lexer() const
77 {
78 return lexer;
79 }
80 public void StartDocument()
81 {
82 contentHandler->StartDocument();
83 }
84 public void EndDocument()
85 {
86 contentHandler->EndDocument();
87 }
88 public void Text(const ustring& text)
89 {
90 contentHandler->HandleText(text);
91 }
92 public void Comment(const ustring& text)
93 {
94 contentHandler->HandleComment(text);
95 }
96 public void PI(const ustring& target, const ustring& data)
97 {
98 contentHandler->HandlePI(target, data);
99 }
100 public void CDataSection(const ustring& cdata)
101 {
102 contentHandler->HandleCDataSection(cdata);
103 }
104 public void Version(const ustring& version)
105 {
106 contentHandler->Version(version);
107 }
108 public void Standalone(bool standalone)
109 {
110 contentHandler->Standalone(standalone);
111 }
112 public void Encoding(const ustring& encoding)
113 {
114 contentHandler->Encoding(encoding);
115 }
116 public void BeginStartTag(const ustring& tagName)
117 {
118 tagStack.Push(currentTagName);
119 currentTagName = tagName;
120 namespaceUriStack.Push(currentNamespaceUri);
121 namespacePrefixStack.Push(currentNamespacePrefix);
122 attributes.Clear();
123 }
124 public void EndStartTag(const System.Lex.Span& span, const string& systemId)
125 {
126 ustring localName;
127 ustring prefix;
128 ParseQualifiedName(currentTagName, localName, prefix, span, systemId);
129 if (prefix == u"xmlns")
130 {
131 throw XmlProcessingException(GetErrorLocationStr(systemId, span) + ": \'xmlns\' prefix cannot be declared for an element");
132 }
133 contentHandler->StartElement(GetNamespaceUri(prefix, span, systemId), localName, currentTagName, attributes);
134 }
135 public void EndTag(const ustring& tagName, const System.Lex.Span& span, const string& systemId)
136 {
137 if (tagStack.IsEmpty())
138 {
139 throw XmlProcessingException(GetErrorLocationStr(systemId, span) + ": end tag \'" + ToUtf8(tagName) + "\' has no corresponding start tag");
140 }
141 if (tagName != currentTagName)
142 {
143 throw XmlProcessingException(GetErrorLocationStr(systemId, span) + ": end tag \'" + ToUtf8(tagName) + "\' does not match start tag \'" + ToUtf8(currentTagName) + "\'");
144 }
145 ustring localName;
146 ustring prefix;
147 ParseQualifiedName(currentTagName, localName, prefix, span, systemId);
148 if (prefix == u"xmlns")
149 {
150 throw XmlProcessingException(GetErrorLocationStr(systemId, span) + ": \'xmlns\' prefix cannot be declared for an element");
151 }
152 contentHandler->EndElement(GetNamespaceUri(prefix, span, systemId), localName, currentTagName);
153 if (namespaceUriStack.IsEmpty())
154 {
155 throw Exception("namespace URI stack is empty");
156 }
157 currentNamespaceUri = namespaceUriStack.Top();
158 namespaceUriStack.Pop();
159 namespacePrefixMap.Remove(currentNamespacePrefix);
160 if (namespacePrefixStack.IsEmpty())
161 {
162 throw Exception("namespace prefix stack is empty");
163 }
164 currentNamespacePrefix = namespacePrefixStack.Top();
165 namespacePrefixStack.Pop();
166 namespacePrefixMap[currentNamespacePrefix] = currentNamespaceUri;
167 currentTagName = tagStack.Top();
168 tagStack.Pop();
169 }
170 public void AddAttribute(const ustring& attName, const ustring& attValue, const System.Lex.Span& span, const string& systemId)
171 {
172 ustring localName;
173 ustring prefix;
174 ParseQualifiedName(attName, localName, prefix, span, systemId);
175 if (prefix == u"xmlns")
176 {
177 currentNamespacePrefix = localName;
178 currentNamespaceUri = attValue;
179 namespacePrefixMap[currentNamespacePrefix] = currentNamespaceUri;
180 }
181 else if (localName == u"xmlns")
182 {
183 currentNamespacePrefix.Clear();
184 currentNamespaceUri = attValue;
185 }
186 else
187 {
188 attributes.Add(Attribute(GetNamespaceUri(prefix, span, systemId), localName, attName, attValue));
189 }
190 }
191 public void EntityRef(const ustring& entityName, const System.Lex.Span& span, const string& systemId)
192 {
193 HashMap<ustring, Entity*>.ConstIterator it = entityMap.CFind(entityName);
194 if (it != entityMap.CEnd())
195 {
196 Entity* entity = it->second;
197 if (entity->IsInternalEntity())
198 {
199 InternalEntity* internalEntity = cast<InternalEntity*>(entity);
200 const ustring& entityValue = internalEntity->Value();
201 if (!attValueStack.IsEmpty())
202 {
203 attValue.Append(entityValue);
204 }
205 else
206 {
207 Text(entityValue);
208 }
209 }
210 else
211 {
212 contentHandler->SkippedEntity(entityName);
213 }
214 }
215 else
216 {
217 contentHandler->SkippedEntity(entityName);
218 }
219 }
220 public void BeginAttributeValue()
221 {
222 attValueStack.Push(attValue);
223 attValue.Clear();
224 }
225 public void EndAttributeValue()
226 {
227 if (attValueStack.IsEmpty())
228 {
229 throw Exception("attribute value stack is empty");
230 }
231 attValue = attValueStack.Top();
232 attValueStack.Pop();
233 }
234 public ustring GetNamespaceUri(const ustring& namespacePrefix, const System.Lex.Span& span, const string& systemId)
235 {
236 if (namespacePrefix.IsEmpty())
237 {
238 return currentNamespaceUri;
239 }
240 HashMap<ustring, ustring>.ConstIterator it = namespacePrefixMap.CFind(namespacePrefix);
241 if (it != namespacePrefixMap.CEnd())
242 {
243 return it->second;
244 }
245 else
246 {
247 throw XmlProcessingException(GetErrorLocationStr(systemId, span) + ": namespace prefix \'" + ToUtf8(namespacePrefix) + "\' not bound to any namespace URI");
248 }
249 }
250 public void ParseQualifiedName(const ustring& qualifiedName, ustring& localName, ustring& prefix, const System.Lex.Span& span, const string& systemId)
251 {
252 List<ustring> parts = qualifiedName.Split(':');
253 if (parts.Count() > 2)
254 {
255 throw XmlProcessingException(GetErrorLocationStr(systemId, span) + ": qualified name \'" + ToUtf8(qualifiedName) + "\' has more than one \':\' character");
256 }
257 else if (parts.Count() == 2)
258 {
259 prefix = parts[0];
260 localName = parts[1];
261 }
262 else
263 {
264 prefix.Clear();
265 localName = qualifiedName;
266 }
267 }
268 public int GetErrorColumn(int index) const
269 {
270 int errorColumn = 0;
271 while (index > 0 && lexer.Start()[index] != '\n' && lexer.Start()[index] != '\r')
272 {
273 ++errorColumn;
274 --index;
275 }
276 if (errorColumn == 0)
277 {
278 errorColumn = 1;
279 }
280 return errorColumn;
281 }
282 public string GetErrorLocationStr(const string& systemId, const System.Lex.Span& span) const
283 {
284 System.Lex.Token token = lexer.GetToken(span.start);
285 int errorColumn = GetErrorColumn(cast<int>(token.match.begin - lexer.Start()));
286 return "error in \'" + systemId + "\' at line " + ToString(span.line) + " column " + ToString(errorColumn);
287 }
288 private TrivialLexer& lexer;
289 private XmlContentHandler* contentHandler;
290 private Stack<ustring> tagStack;
291 private ustring currentTagName;
292 private Stack<ustring> attValueStack;
293 private ustring attValue;
294 private Stack<ustring> namespaceUriStack;
295 private ustring currentNamespaceUri;
296 private Stack<ustring> namespacePrefixStack;
297 private ustring currentNamespacePrefix;
298 private HashMap<ustring, ustring> namespacePrefixMap;
299 private Attributes attributes;
300 private HashMap<ustring, Entity*> entityMap;
301 private List<UniquePtr<Entity>> entities;
302 }
303 }