New test.
[mono.git] / mcs / class / Commons.Xml.Relaxng / Commons.Xml.Nvdl / NvdlFilteredXmlReader.cs
1 using System;
2 using System.Collections;
3 using System.IO;
4 using System.Xml;
5
6 namespace Commons.Xml.Nvdl
7 {
8         internal class NvdlFilteredXmlReader : XmlReader, IXmlLineInfo
9         {
10                 // In this XmlReader, when AttachPlaceHolder() is called,
11                 // it treats the next node as to point virtual "placeholder"
12                 // element where IsEmptyElement is false. When it is
13                 // Detached, then *current* node becomes the usual reader
14                 // node.
15
16                 bool initial = true;
17                 int placeHolderDepth = 0;
18                 XmlNodeType nextPlaceHolder;
19                 XmlNodeType placeHolder = XmlNodeType.None;
20                 bool placeHolderLocalNameAttr;
21                 NvdlValidateInterp validate;
22                 XmlReader reader;
23                 IXmlLineInfo reader_as_line_info;
24
25                 AttributeInfo [] attributes = new AttributeInfo [10];
26                 int attributeCount = 0;
27
28                 // PlanAtt validation cache.
29                 Hashtable attributeValidators = new Hashtable ();
30
31                 class AttributeInfo
32                 {
33                         public string LocalName;
34                         public string NamespaceURI;
35                 }
36
37                 public NvdlFilteredXmlReader (XmlReader reader,
38                         NvdlValidateInterp validate)
39                 {
40                         this.reader = reader;
41                         reader_as_line_info = reader as IXmlLineInfo;
42                         this.validate = validate;
43                 }
44
45                 public bool HasLineInfo ()
46                 {
47                         return reader_as_line_info != null ? reader_as_line_info.HasLineInfo () : false;
48                 }
49
50                 public int LineNumber {
51                         get { return reader_as_line_info != null ? reader_as_line_info.LineNumber : 0; }
52                 }
53
54                 public int LinePosition {
55                         get { return reader_as_line_info != null ? reader_as_line_info.LinePosition : 0; }
56                 }
57
58                 public void AttachPlaceholder ()
59                 {
60                         placeHolderDepth++;
61                         nextPlaceHolder = XmlNodeType.Element;
62                 }
63
64                 public void DetachPlaceholder ()
65                 {
66                         placeHolderDepth--;
67                         placeHolder = XmlNodeType.None;
68                 }
69
70                 private void AddAttribute ()
71                 {
72                         if (attributes.Length == attributeCount) {
73                                 AttributeInfo [] newArr =
74                                         new AttributeInfo [attributeCount * 2];
75                                 Array.Copy (attributes, newArr, attributeCount);
76                         }
77                         AttributeInfo ai = attributes [attributeCount];
78                         if (ai == null) {
79                                 ai = new AttributeInfo ();
80                                 attributes [attributeCount] = ai;
81                         }
82                         ai.LocalName = reader.LocalName;
83                         ai.NamespaceURI = reader.NamespaceURI;
84                         attributeCount++;
85                 }
86
87                 private SimpleRule FindAttributeRule (string ns, SimpleMode mode)
88                 {
89                         SimpleRule any = null;
90                         foreach (SimpleRule rule in mode.AttributeRules) {
91                                 if (!rule.MatchNS (ns))
92                                         continue;
93                                 if (!rule.IsAny)
94                                         return rule;
95                                 any = rule;
96                         }
97                         if (any != null)
98                                 return any;
99                         throw new NvdlValidationException ("NVDL internal error: should not happen. No matching rule was found.", reader as IXmlLineInfo);
100                 }
101
102                 // Public overrides
103
104                 public override bool Read ()
105                 {
106                         // This class itself never proceeds, just checks if
107                         // the reader is placed on EOF.
108                         if (reader.EOF)
109                                 return false;
110
111                         MoveToElement ();
112                         attributeCount = 0;
113
114                         if (nextPlaceHolder != XmlNodeType.None) {
115                                 placeHolder = nextPlaceHolder;
116                                 nextPlaceHolder = XmlNodeType.None;
117                                 return true;
118                         }
119
120                         if (placeHolder != XmlNodeType.None)
121                                 // Inside placeHolder, ignore all contents.
122                                 // The source XmlReader should proceed
123                                 // regardless of this filtered reader.
124                                 return true;
125
126                         if (!reader.MoveToFirstAttribute ())
127                                 return true;
128
129                         // Attribute rule application
130                         attributeValidators.Clear ();
131                         do {
132                                 if (reader.NamespaceURI == "http://www.w3.org/2000/xmlns/")
133                                         continue;
134                                 // FIXME: could be more efficient
135                                 SimpleRule rule = FindAttributeRule (
136                                         reader.NamespaceURI,
137                                         validate.CreatedMode);
138                                 foreach (SimpleAction a in rule.Actions) {
139                                         SimpleResultAction ra =
140                                                 a as SimpleResultAction;
141                                         if (ra != null &&
142                                                 ra.ResultType == NvdlResultType.Attach)
143                                                 AddAttribute ();
144                                         if (ra != null)
145                                                 continue;
146                                         attributeValidators [reader.NamespaceURI] = a;
147                                 }
148                         } while (reader.MoveToNextAttribute ());
149                         reader.MoveToElement ();
150
151                         if (attributeValidators.Count > 0) {
152                                 foreach (string ns in attributeValidators.Keys) {
153                                         ((SimpleValidate) attributeValidators [
154                                                 ns]).ValidateAttributes (reader, ns);
155                                 }
156                         }
157
158                         return true;
159                 }
160
161                 public override int AttributeCount {
162                         get {
163                                 switch (placeHolder) {
164                                 case XmlNodeType.Element:
165                                 case XmlNodeType.Attribute: // ns or localName attribute on placeHolder element
166                                 case XmlNodeType.Text: // attribute value of ns or localName attribute on placeHolder element
167                                         return 2;
168                                 case XmlNodeType.EndElement:
169                                         return 0;
170                                 default:
171                                         return attributeCount;
172                                 }
173                         }
174                 }
175
176                 public override string BaseURI {
177                         get { return reader.BaseURI; }
178                 }
179
180                 public override int Depth {
181                         get {
182                                 if (placeHolderDepth == 0)
183                                         return reader.Depth;
184                                 int basis = reader.Depth + 1;
185                                 switch (placeHolder) {
186                                 case XmlNodeType.Text:
187                                         return basis + 2;
188                                 case XmlNodeType.Attribute:
189                                         return basis + 1;
190                                 default:
191                                         return basis;
192                                 }
193                         }
194                 }
195
196                 public override bool EOF {
197                         get { return reader.EOF && placeHolder != XmlNodeType.None; }
198                 }
199
200                 public override bool HasValue {
201                         get {
202                                 switch (placeHolder) {
203                                 case XmlNodeType.Attribute:
204                                 case XmlNodeType.Text:
205                                         return true;
206                                 case XmlNodeType.Element:
207                                 case XmlNodeType.EndElement:
208                                         return false;
209                                 default:
210                                         return reader.HasValue;
211                                 }
212                         }
213                 }
214
215                 public override bool IsDefault {
216                         get {
217                                 return placeHolder == XmlNodeType.None &&
218                                         reader.IsDefault;
219                         }
220                 }
221
222                 public override bool IsEmptyElement {
223                         get {
224                                 return placeHolder != XmlNodeType.None ||
225                                         reader.IsEmptyElement; 
226                         }
227                 }
228
229                 public override string LocalName {
230                         get {
231                                 switch (placeHolder) {
232                                 case XmlNodeType.Element:
233                                 case XmlNodeType.EndElement:
234                                         return "placeholder";
235                                 case XmlNodeType.Attribute:
236                                         return placeHolderLocalNameAttr ?
237                                                 "localName" : "ns";
238                                 case XmlNodeType.Text:
239                                         return String.Empty;
240                                 default:
241                                         return reader.LocalName;
242                                 }
243                         }
244                 }
245
246                 public override string Name {
247                         get {
248                                 switch (placeHolder) {
249                                 case XmlNodeType.Element:
250                                 case XmlNodeType.EndElement:
251                                         return "placeholder";
252                                 case XmlNodeType.Attribute:
253                                         return placeHolderLocalNameAttr ?
254                                                 "localName" : "ns";
255                                 case XmlNodeType.Text:
256                                         return String.Empty;
257                                 default:
258                                         return reader.Name;
259                                 }
260                         }
261                 }
262
263                 public override string NamespaceURI {
264                         get {
265                                 switch (placeHolder) {
266                                 case XmlNodeType.Element:
267                                 case XmlNodeType.EndElement:
268                                         return Nvdl.InstanceNamespace;
269                                 case XmlNodeType.Attribute:
270                                         return String.Empty;
271                                 case XmlNodeType.Text:
272                                         return String.Empty;
273                                 default:
274                                         return reader.NamespaceURI;
275                                 }
276                         }
277                 }
278
279                 public override XmlNameTable NameTable {
280                         get { return reader.NameTable; }
281                 }
282
283                 public override XmlNodeType NodeType {
284                         get {
285                                 return placeHolder != XmlNodeType.None ?
286                                         placeHolder : reader.NodeType; 
287                         }
288                 }
289
290                 public override string Prefix {
291                         get {
292                                 return placeHolder != XmlNodeType.None ?
293                                         String.Empty : reader.Name; 
294                         }
295                 }
296
297                 public override char QuoteChar {
298                         get { return reader.QuoteChar; }
299                 }
300
301                 public override ReadState ReadState {
302                         get { 
303                                 return initial ? ReadState.Initial :
304                                         placeHolder != XmlNodeType.None &&
305                                         reader.ReadState != ReadState.Error ? 
306                                         ReadState.Interactive :
307                                         reader.ReadState; 
308                         }
309                 }
310
311                 public override string Value {
312                         get {
313                                 switch (placeHolder) {
314                                 case XmlNodeType.Element:
315                                 case XmlNodeType.EndElement:
316                                         return String.Empty;
317                                 case XmlNodeType.Attribute:
318                                 case XmlNodeType.Text:
319                                         return placeHolderLocalNameAttr ?
320                                                 reader.LocalName :
321                                                 reader.NamespaceURI;
322                                 default:
323                                         return reader.Value;
324                                 }
325                         }
326                 }
327
328                 // FIXME: xml:lang might have been filtered
329                 public override string XmlLang {
330                         get { return reader.XmlLang; }
331                 }
332
333                 // FIXME: xml:space might have been filtered
334                 public override XmlSpace XmlSpace {
335                         get { return reader.XmlSpace; }
336                 }
337
338                 public override string this [int i] {
339                         get { return GetAttribute (i); }
340                 }
341
342                 public override string this [string name] {
343                         get { return GetAttribute (name); }
344                 }
345
346                 public override string this [string localName, string ns] {
347                         get { return GetAttribute (localName, ns); }
348                 }
349
350                 public override string GetAttribute (int i)
351                 {
352                         if (placeHolder == XmlNodeType.Element)
353                                 return placeHolderLocalNameAttr ?
354                                         reader.LocalName : reader.NamespaceURI;
355                         else
356                                 return reader.GetAttribute (
357                                         attributes [i].LocalName,
358                                         attributes [i].NamespaceURI);
359                 }
360
361                 public override string GetAttribute (string name)
362                 {
363                         if (placeHolder == XmlNodeType.Element) {
364                                 switch (name) {
365                                 case "localName":
366                                         return reader.LocalName;
367                                 case "ns":
368                                         return reader.NamespaceURI;
369                                 default:
370                                         return null;
371                                 }
372                         }
373                         return reader.GetAttribute (name);
374                 }
375
376                 public override string GetAttribute (string localName, string ns)
377                 {
378                         if (placeHolder == XmlNodeType.Element) {
379                                 if (ns != String.Empty)
380                                         return null;
381                                 return GetAttribute (localName);
382                         }
383                         return reader.GetAttribute (localName, ns);
384                 }
385
386                 public override void ResolveEntity ()
387                 {
388                         if (placeHolder != XmlNodeType.None)
389                                 throw new XmlException ("Current node is not an EntityReference.");
390                         reader.ResolveEntity ();
391                 }
392
393                 public override void Close ()
394                 {
395                         // do nothing.
396                 }
397
398                 public override string LookupNamespace (string prefix)
399                 {
400                         // For placeHolder element, we treat them as to have
401                         // empty prefix. So we have to handle empty prefix as if
402                         // it was mapped to the namespace.
403                         if (placeHolder != XmlNodeType.None && prefix == String.Empty)
404                                 return Nvdl.InstanceNamespace;
405                         return reader.LookupNamespace (prefix);
406                 }
407
408                 public override bool MoveToElement ()
409                 {
410                         switch (placeHolder) {
411                         case XmlNodeType.Element:
412                         case XmlNodeType.EndElement:
413                                 return false;
414                         case XmlNodeType.Attribute:
415                         case XmlNodeType.Text:
416                                 placeHolder = XmlNodeType.Element;
417                                 placeHolderLocalNameAttr = false;
418                                 return true;
419                         default:
420                                 return reader.MoveToElement ();
421                         }
422                 }
423
424                 public override bool MoveToFirstAttribute ()
425                 {
426                         switch (placeHolder) {
427                         case XmlNodeType.Element:
428                                 placeHolder = XmlNodeType.Attribute;
429                                 placeHolderLocalNameAttr = true;
430                                 return true;
431                         case XmlNodeType.EndElement:
432                                 return false;
433                         case XmlNodeType.Attribute:
434                         case XmlNodeType.Text:
435                                 placeHolder = XmlNodeType.Attribute;
436                                 placeHolderLocalNameAttr = true;
437                                 return true;
438                         default:
439                                 if (attributeCount == 0)
440                                         return false;
441                                 return reader.MoveToAttribute (
442                                         attributes [0].LocalName,
443                                         attributes [0].NamespaceURI);
444                         }
445                 }
446
447                 public override bool MoveToNextAttribute ()
448                 {
449                         switch (placeHolder) {
450                         case XmlNodeType.Element:
451                         case XmlNodeType.EndElement:
452                                 return false;
453                         case XmlNodeType.Attribute:
454                         case XmlNodeType.Text:
455                                 if (!placeHolderLocalNameAttr)
456                                         return false;
457                                 placeHolderLocalNameAttr = false;
458                                 placeHolder = XmlNodeType.Attribute;
459                                 return true;
460                         default:
461                                 if (reader.NodeType == XmlNodeType.Element)
462                                         return false;
463                                 for (int i = 0; i < attributeCount - 1; i++) {
464                                         if (attributes [i].LocalName != reader.LocalName ||
465                                                 attributes [i].NamespaceURI != reader.NamespaceURI)
466                                                 continue;
467                                         reader.MoveToAttribute (
468                                                 attributes [i + 1].LocalName,
469                                                 attributes [i + 1].NamespaceURI);
470                                         return true;
471                                 }
472                                 return false;
473                         }
474                 }
475
476                 public override bool ReadAttributeValue ()
477                 {
478                         switch (placeHolder) {
479                         case XmlNodeType.Element:
480                         case XmlNodeType.EndElement:
481                         case XmlNodeType.Text:
482                                 return false;
483                         case XmlNodeType.Attribute:
484                                 placeHolder = XmlNodeType.Text;
485                                 return true;
486                         default:
487                                 return reader.ReadAttributeValue ();
488                         }
489                 }
490
491                 public override void MoveToAttribute (int i)
492                 {
493                         switch (placeHolder) {
494                         case XmlNodeType.EndElement:
495                                 throw new IndexOutOfRangeException ();
496                         case XmlNodeType.Element:
497                         case XmlNodeType.Attribute:
498                         case XmlNodeType.Text:
499                                 switch (i) {
500                                 case 0:
501                                         placeHolderLocalNameAttr = true;
502                                         return;
503                                 case 1:
504                                         placeHolderLocalNameAttr = false;
505                                         return;
506                                 }
507                                 throw new IndexOutOfRangeException ();
508                         default:
509                                 if (i < 0 || attributeCount <= i)
510                                         throw new IndexOutOfRangeException ();
511                                 reader.MoveToAttribute (
512                                         attributes [i].LocalName,
513                                         attributes [i].NamespaceURI);
514                                 break;
515                         }
516                 }
517
518                 public override bool MoveToAttribute (string qname)
519                 {
520                         int index = qname.IndexOf (':');
521                         if (index < 0)
522                                 return MoveToAttribute (qname, String.Empty);
523                         return MoveToAttribute (qname.Substring (index + 1),
524                                 LookupNamespace (qname.Substring (0, index)));
525                 }
526
527                 public override bool MoveToAttribute (string localName, string ns)
528                 {
529                         switch (placeHolder) {
530                         case XmlNodeType.EndElement:
531                                 return false;
532                         case XmlNodeType.Element:
533                         case XmlNodeType.Attribute:
534                         case XmlNodeType.Text:
535                                 if (ns != String.Empty)
536                                         return false;
537                                 switch (localName) {
538                                 case "localName":
539                                         MoveToAttribute (0);
540                                         return true;
541                                 case "ns":
542                                         MoveToAttribute (1);
543                                         return true;
544                                 default:
545                                         return false;
546                                 }
547                         default:
548                                 for (int i = 0; i < attributeCount; i++) {
549                                         if (attributes [i].LocalName != localName ||
550                                                 attributes [i].NamespaceURI != ns)
551                                                 continue;
552                                         return reader.MoveToAttribute (localName, ns);
553                                 }
554                                 return false;
555                         }
556                 }
557         }
558 }
559