Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / System.XML / System.Xml / EntityResolvingXmlReader.cs
1 //
2 // EntityResolvingXmlReader.cs - XmlReader that handles entity resolution
3 //
4 // Author:
5 //   Atsushi Enomoto  (ginga@kit.hi-ho.ne.jp)
6 //
7 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 using System.Collections.Generic;
30 using System;
31 using System.Globalization;
32 using System.IO;
33 using System.Security.Permissions;
34 using System.Text;
35 using System.Xml.Schema;
36 using System.Xml;
37
38 namespace Mono.Xml
39 {
40         [PermissionSet (SecurityAction.InheritanceDemand, Unrestricted = true)]
41         internal class EntityResolvingXmlReader : XmlReader, IXmlNamespaceResolver,
42                 IXmlLineInfo, IHasXmlParserContext
43         {
44                 EntityResolvingXmlReader entity;
45                 XmlReader source;
46                 XmlParserContext context;
47                 XmlResolver resolver;
48                 EntityHandling entity_handling;
49                 bool entity_inside_attr;
50                 bool inside_attr;
51                 bool do_resolve;
52
53                 public EntityResolvingXmlReader (XmlReader source)
54                 {
55                         this.source = source;
56                         IHasXmlParserContext container = source as IHasXmlParserContext;
57                         if (container != null)
58                                 this.context = container.ParserContext;
59                         else
60                                 this.context = new XmlParserContext (source.NameTable, new XmlNamespaceManager (source.NameTable), null, XmlSpace.None);
61                 }
62
63                 EntityResolvingXmlReader (XmlReader entityContainer,
64                         bool inside_attr)
65                 {
66                         source = entityContainer;
67                         this.entity_inside_attr = inside_attr;
68                 }
69
70                 #region Properties
71
72                 private XmlReader Current {
73                         get { return entity != null && entity.ReadState != ReadState.Initial ? (XmlReader) entity : source; }
74                 }
75
76                 public override int AttributeCount {
77                         get { return Current.AttributeCount; }
78                 }
79
80                 public override string BaseURI {
81                         get { return Current.BaseURI; }
82                 }
83
84                 public override bool CanResolveEntity {
85                         get { return true; }
86                 }
87
88                 public override int Depth {
89                         get {
90                                 // On EndEntity, depth is the same as that 
91                                 // of EntityReference.
92                                 if (entity != null && entity.ReadState == ReadState.Interactive)
93                                         return source.Depth + entity.Depth + 1;
94                                 else
95                                         return source.Depth;
96                         }
97                 }
98
99                 public override bool EOF {
100                         get { return source.EOF; }
101                 }
102
103                 public override bool HasValue {
104                         get { return Current.HasValue; }
105                 }
106
107                 public override bool IsDefault {
108                         get { return Current.IsDefault; }
109                 }
110
111                 public override bool IsEmptyElement {
112                         get { return Current.IsEmptyElement; }
113                 }
114
115                 public override string LocalName {
116                         get { return Current.LocalName; }
117                 }
118
119                 public override string Name {
120                         get { return Current.Name; }
121                 }
122
123                 public override string NamespaceURI {
124                         get { return Current.NamespaceURI; }
125                 }
126
127                 public override XmlNameTable NameTable {
128                         get { return Current.NameTable; }
129                 }
130
131                 public override XmlNodeType NodeType {
132                         get {
133                                 if (entity != null) {
134                                         if (entity.ReadState == ReadState.Initial)
135                                                 return source.NodeType;
136                                         return entity.EOF ? XmlNodeType.EndEntity : entity.NodeType;
137                                 }
138                                 return source.NodeType;
139                         }
140                 }
141
142                 internal XmlParserContext ParserContext {
143                         get { return context; }
144                 }
145
146                 XmlParserContext IHasXmlParserContext.ParserContext {
147                         get { return context; }
148                 }
149
150                 public override string Prefix {
151                         get { return Current.Prefix; }
152                 }
153
154                 public override char QuoteChar {
155                         get { return Current.QuoteChar; }
156                 }
157
158                 public override ReadState ReadState {
159                         get { return entity != null ? ReadState.Interactive : source.ReadState; }
160                 }
161
162                 public override string Value {
163                         get { return Current.Value; }
164                 }
165
166                 public override string XmlLang {
167                         get { return Current.XmlLang; }
168                 }
169
170                 public override XmlSpace XmlSpace {
171                         get { return Current.XmlSpace; }
172                 }
173
174                 // non-overrides
175
176                 private void CopyProperties (EntityResolvingXmlReader other)
177                 {
178                         context = other.context;
179                         resolver = other.resolver;
180                         entity_handling = other.entity_handling;
181                 }
182
183                 // public members
184
185                 public EntityHandling EntityHandling {
186                         get { return entity_handling; }
187                         set {
188                                 if (entity != null)
189                                         entity.EntityHandling = value;
190                                 entity_handling = value;
191                         }
192                 }
193
194                 public int LineNumber {
195                         get {
196                                 IXmlLineInfo li = Current as IXmlLineInfo;
197                                 return li == null ? 0 : li.LineNumber;
198                         }
199                 }
200
201                 public int LinePosition {
202                         get {
203                                 IXmlLineInfo li = Current as IXmlLineInfo;
204                                 return li == null ? 0 : li.LinePosition;
205                         }
206                 }
207
208                 public XmlResolver XmlResolver {
209                         set {
210                                 if (entity != null)
211                                         entity.XmlResolver = value;
212                                 resolver = value;
213                         }
214                 }
215
216                 #endregion
217
218                 #region Methods
219
220                 // overrides
221
222                 public override void Close ()
223                 {
224                         if (entity != null)
225                                 entity.Close ();
226                         source.Close ();
227                 }
228
229                 public override string GetAttribute (int i)
230                 {
231                         return Current.GetAttribute (i);
232                 }
233
234                 // MS.NET 1.0 msdn says that this method returns String.Empty
235                 // for absent attribute, but in fact it returns null.
236                 // This description is corrected in MS.NET 1.1 msdn.
237                 public override string GetAttribute (string name)
238                 {
239                         return Current.GetAttribute (name);
240                 }
241
242                 public override string GetAttribute (string localName, string namespaceURI)
243                 {
244                         return Current.GetAttribute (localName, namespaceURI);
245                 }
246
247                 public IDictionary<string, string> GetNamespacesInScope (XmlNamespaceScope scope)
248                 {
249                         return ((IXmlNamespaceResolver) Current).GetNamespacesInScope (scope);
250                 }
251
252                 IDictionary<string, string> IXmlNamespaceResolver.GetNamespacesInScope (XmlNamespaceScope scope)
253                 {
254                         return GetNamespacesInScope (scope);
255                 }
256
257                 string IXmlNamespaceResolver.LookupPrefix (string ns)
258                 {
259                         return ((IXmlNamespaceResolver) Current).LookupPrefix (ns);
260                 }
261
262                 public override string LookupNamespace (string prefix)
263                 {
264                         return Current.LookupNamespace (prefix);
265                 }
266
267                 public override void MoveToAttribute (int i)
268                 {
269                         if (entity != null && entity_inside_attr) {
270                                 entity.Close ();
271                                 entity = null;
272                         }
273                         Current.MoveToAttribute (i);
274                         inside_attr = true;
275                 }
276
277                 public override bool MoveToAttribute (string name)
278                 {
279                         if (entity != null && !entity_inside_attr)
280                                 return entity.MoveToAttribute (name);
281                         if (!source.MoveToAttribute (name))
282                                 return false;
283                         if (entity != null && entity_inside_attr) {
284                                 entity.Close ();
285                                 entity = null;
286                         }
287                         inside_attr = true;
288                         return true;
289                 }
290
291                 public override bool MoveToAttribute (string localName, string namespaceName)
292                 {
293                         if (entity != null && !entity_inside_attr)
294                                 return entity.MoveToAttribute (localName, namespaceName);
295                         if (!source.MoveToAttribute (localName, namespaceName))
296                                 return false;
297                         if (entity != null && entity_inside_attr) {
298                                 entity.Close ();
299                                 entity = null;
300                         }
301                         inside_attr = true;
302                         return true;
303                 }
304
305                 public override bool MoveToElement ()
306                 {
307                         if (entity != null && entity_inside_attr) {
308                                 entity.Close ();
309                                 entity = null;
310                         }
311                         if (!Current.MoveToElement ())
312                                 return false;
313                         inside_attr = false;
314                         return true;
315                 }
316
317                 public override bool MoveToFirstAttribute ()
318                 {
319                         if (entity != null && !entity_inside_attr)
320                                 return entity.MoveToFirstAttribute ();
321                         if (!source.MoveToFirstAttribute ())
322                                 return false;
323                         if (entity != null && entity_inside_attr) {
324                                 entity.Close ();
325                                 entity = null;
326                         }
327                         inside_attr = true;
328                         return true;
329                 }
330
331                 public override bool MoveToNextAttribute ()
332                 {
333                         if (entity != null && !entity_inside_attr)
334                                 return entity.MoveToNextAttribute ();
335                         if (!source.MoveToNextAttribute ())
336                                 return false;
337                         if (entity != null && entity_inside_attr) {
338                                 entity.Close ();
339                                 entity = null;
340                         }
341                         inside_attr = true;
342                         return true;
343                 }
344
345                 public override bool Read ()
346                 {
347                         if (do_resolve) {
348                                 DoResolveEntity ();
349                                 do_resolve = false;
350                         }
351
352                         inside_attr = false;
353
354                         if (entity != null && (entity_inside_attr || entity.EOF)) {
355                                 entity.Close ();
356                                 entity = null;
357                         }
358                         if (entity != null) {
359                                 if (entity.Read ())
360                                         return true;
361                                 if (EntityHandling == EntityHandling.ExpandEntities) {
362                                         // EndEntity must be skipped
363                                         entity.Close ();
364                                         entity = null;
365                                         return Read ();
366                                 }
367                                 else
368                                         return true; // either success or EndEntity
369                         }
370                         else {
371                                 if (!source.Read ())
372                                         return false;
373                                 if (EntityHandling == EntityHandling.ExpandEntities
374                                         && source.NodeType == XmlNodeType.EntityReference) {
375                                         ResolveEntity ();
376                                         return Read ();
377                                 }
378                                 return true;
379                         }
380                 }
381
382                 public override bool ReadAttributeValue ()
383                 {
384                         if (entity != null && entity_inside_attr) {
385                                 if (entity.EOF) {
386                                         entity.Close ();
387                                         entity = null;
388                                 }
389                                 else {
390                                         entity.Read ();
391                                         return true; // either success or EndEntity
392                                 }
393                         }
394                         return Current.ReadAttributeValue ();
395                 }
396
397                 public override string ReadString ()
398                 {
399                         return base.ReadString ();
400                 }
401
402                 public override void ResolveEntity ()
403                 {
404                         DoResolveEntity ();
405                 }
406
407                 void DoResolveEntity ()
408                 {
409                         if (entity != null)
410                                 entity.ResolveEntity ();
411                         else {
412                                 if (source.NodeType != XmlNodeType.EntityReference)
413                                         throw new InvalidOperationException ("The current node is not an Entity Reference");
414                                 if (ParserContext.Dtd == null)
415                                         throw new XmlException (this as IXmlLineInfo, this.BaseURI, String.Format ("Cannot resolve entity without DTD: '{0}'", source.Name));
416                                 XmlReader entReader = ParserContext.Dtd.GenerateEntityContentReader (
417                                         source.Name, ParserContext);
418                                 if (entReader == null)
419                                         throw new XmlException (this as IXmlLineInfo, this.BaseURI, String.Format ("Reference to undeclared entity '{0}'.", source.Name));
420
421                                 entity = new EntityResolvingXmlReader (
422                                         entReader, inside_attr);
423                                 entity.CopyProperties (this);
424                         }
425                 }
426
427                 public override void Skip ()
428                 {
429                         base.Skip ();
430                 }
431
432                 public bool HasLineInfo ()
433                 {
434                         IXmlLineInfo li = Current as IXmlLineInfo;
435                         return li == null ? false : li.HasLineInfo ();
436                 }
437
438                 #endregion
439         }
440 }