Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.SqlXml / System / Xml / Xsl / XsltOld / ReaderOutput.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ReaderOutput.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml.Xsl.XsltOld {
9     using Res = System.Xml.Utils.Res;
10     using System;
11     using System.Globalization;
12     using System.Diagnostics;
13     using System.IO;
14     using System.Text;
15     using System.Xml;
16     using System.Xml.XPath;
17     using System.Collections;
18
19     internal class ReaderOutput : XmlReader, RecordOutput {
20         private Processor       processor;
21         private XmlNameTable    nameTable;
22
23         // Main node + Fields Collection
24         private RecordBuilder   builder;
25         private BuilderInfo     mainNode;
26         private ArrayList       attributeList;
27         private int             attributeCount;
28         private BuilderInfo     attributeValue;
29
30         // OutputScopeManager
31         private OutputScopeManager  manager;
32
33         // Current position in the list
34         private int             currentIndex;
35         private BuilderInfo     currentInfo;
36
37         // Reader state
38         private ReadState       state = ReadState.Initial;
39         private bool            haveRecord;
40
41         // Static default record
42         static BuilderInfo      s_DefaultInfo = new BuilderInfo();
43
44         XmlEncoder  encoder = new XmlEncoder();
45         XmlCharType xmlCharType = XmlCharType.Instance;
46
47         internal ReaderOutput(Processor processor) {
48             Debug.Assert(processor != null);
49             Debug.Assert(processor.NameTable != null);
50
51             this.processor = processor;
52             this.nameTable = processor.NameTable;
53
54             Reset();
55         }
56
57         // XmlReader abstract methods implementation
58         public override XmlNodeType NodeType {
59             get {
60                 CheckCurrentInfo();
61                 return this.currentInfo.NodeType;
62             }
63         }
64
65         public override string Name {
66             get {
67                 CheckCurrentInfo();
68                 string prefix    = Prefix;
69                 string localName = LocalName;
70
71                 if (prefix != null && prefix.Length > 0) {
72                     if (localName.Length > 0) {
73                         return nameTable.Add(prefix + ":" + localName);
74                     }
75                     else {
76                         return prefix;
77                     }
78                 }
79                 else {
80                     return localName;
81                 }
82             }
83         }
84
85         public override string LocalName {
86             get {
87                 CheckCurrentInfo();
88                 return this.currentInfo.LocalName;
89             }
90         }
91
92         public override string NamespaceURI {
93             get {
94                 CheckCurrentInfo();
95                 return this.currentInfo.NamespaceURI;
96             }
97         }
98
99         public override string Prefix {
100             get {
101                 CheckCurrentInfo();
102                 return this.currentInfo.Prefix;
103             }
104         }
105
106         public override bool HasValue {
107             get {
108                 return XmlReader.HasValueInternal(NodeType);
109             }
110         }
111
112         public override string Value {
113             get {
114                 CheckCurrentInfo();
115                 return this.currentInfo.Value;
116             }
117         }
118
119         public override int Depth {
120             get {
121                 CheckCurrentInfo();
122                 return this.currentInfo.Depth;
123             }
124         }
125
126         public override string BaseURI {
127             get {
128                 return string.Empty;
129             }
130         }
131
132         public override bool IsEmptyElement {
133             get {
134                 CheckCurrentInfo();
135                 return this.currentInfo.IsEmptyTag;
136             }
137         }
138
139         public override char QuoteChar {
140             get { return encoder.QuoteChar; }
141         }
142
143         public override bool IsDefault {
144             get { return false; }
145         }
146
147         public override XmlSpace XmlSpace {
148             get { return this.manager != null ? this.manager.XmlSpace : XmlSpace.None; }
149         }
150
151         public override string XmlLang {
152             get { return this.manager != null ? this.manager.XmlLang : string.Empty; }
153         }
154
155         // Attribute Accessors
156
157         public override int AttributeCount {
158             get { return this.attributeCount; }
159         }
160
161         public override string GetAttribute(string name) {
162             int ordinal;
163             if (FindAttribute(name, out ordinal)) {
164                 Debug.Assert(ordinal >= 0);
165                 return((BuilderInfo)this.attributeList[ordinal]).Value;
166             }
167             else {
168                 Debug.Assert(ordinal == -1);
169                 return null;
170             }            
171         }
172
173         public override string GetAttribute(string localName, string namespaceURI) {
174             int ordinal;
175             if (FindAttribute(localName, namespaceURI, out ordinal)) {
176                 Debug.Assert(ordinal >= 0);
177                 return((BuilderInfo)this.attributeList[ordinal]).Value;
178             }
179             else {
180                 Debug.Assert(ordinal == -1);
181                 return null;
182             }
183         }
184
185         public override string GetAttribute(int i) {
186             BuilderInfo attribute = GetBuilderInfo(i);
187             return attribute.Value;
188         }
189
190         public override string this [int i] {
191             get { return GetAttribute(i); }
192         }
193
194         public override string this [string name] {
195             get { return GetAttribute(name); }
196         }
197
198         public override string this [string name, string namespaceURI] {
199             get { return GetAttribute(name, namespaceURI); }
200         }
201
202         public override bool MoveToAttribute(string name) {
203             int ordinal;
204             if (FindAttribute(name, out ordinal)) {
205                 Debug.Assert(ordinal >= 0);
206                 SetAttribute(ordinal);
207                 return true;
208             }
209             else {
210                 Debug.Assert(ordinal == -1);
211                 return false;
212             }
213         }
214
215         public override bool MoveToAttribute(string localName, string namespaceURI) {
216             int ordinal;
217             if (FindAttribute(localName, namespaceURI, out ordinal)) {
218                 Debug.Assert(ordinal >= 0);
219                 SetAttribute(ordinal);
220                 return true;
221             }
222             else {
223                 Debug.Assert(ordinal == -1);
224                 return false;
225             }
226         }
227
228         public override void MoveToAttribute(int i) {
229             if (i < 0 || this.attributeCount <= i) {
230                 throw new ArgumentOutOfRangeException("i");
231             }
232             SetAttribute(i);
233         }
234
235         public override bool MoveToFirstAttribute() {
236             if (this.attributeCount <= 0) {
237                 Debug.Assert(this.attributeCount == 0);
238                 return false;
239             }
240             else {
241                 SetAttribute(0);
242                 return true;
243             }
244         }
245
246         public override bool MoveToNextAttribute() {
247             if (this.currentIndex + 1 < this.attributeCount) {
248                 SetAttribute(this.currentIndex + 1);
249                 return true;
250             }
251             return false;
252         }
253
254         public override bool MoveToElement() {
255             if (NodeType == XmlNodeType.Attribute || this.currentInfo == this.attributeValue) {
256                 SetMainNode();
257                 return true;
258             }
259             return false;
260         }
261
262         // Moving through the Stream
263
264         public override bool Read() {
265             Debug.Assert(this.processor != null || this.state == ReadState.Closed);
266
267             if (this.state != ReadState.Interactive) {
268                 if (this.state == ReadState.Initial) {
269                     state = ReadState.Interactive;
270                 }
271                 else {
272                     return false;
273                 }
274             }
275
276             while (true) { // while -- to ignor empty whitespace nodes.
277                 if (this.haveRecord) {
278                     this.processor.ResetOutput();
279                     this.haveRecord = false;
280                 }
281
282                 this.processor.Execute();
283
284                 if (this.haveRecord) {
285                     CheckCurrentInfo();
286                     // check text nodes on whitespaces;
287                     switch (this.NodeType) {
288                     case XmlNodeType.Text :
289                         if (xmlCharType.IsOnlyWhitespace(this.Value)) {
290                             this.currentInfo.NodeType = XmlNodeType.Whitespace;
291                             goto case XmlNodeType.Whitespace;
292                         }
293                         Debug.Assert(this.Value.Length != 0, "It whould be Whitespace in this case");
294                         break;
295                     case XmlNodeType.Whitespace :
296                         if(this.Value.Length == 0) {
297                             continue;                          // ignoring emty text nodes
298                         }
299                         if (this.XmlSpace == XmlSpace.Preserve) {
300                             this.currentInfo.NodeType = XmlNodeType.SignificantWhitespace;
301                         }
302                         break;
303                     }                
304                 }
305                 else {
306                     Debug.Assert(this.processor.ExecutionDone);
307                     this.state = ReadState.EndOfFile;
308                     Reset();
309                 }
310
311                 return this.haveRecord;
312             }
313         }
314
315         public override bool EOF {
316             get { return this.state == ReadState.EndOfFile; }
317         }
318
319         public override void Close() {
320             this.processor = null;
321             this.state     = ReadState.Closed;
322             Reset();
323         }
324
325         public override ReadState ReadState {
326             get { return this.state; }
327         }
328
329         // Whole Content Read Methods
330         public override string ReadString() {
331             string result = string.Empty;
332
333             if (NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute || this.currentInfo == this.attributeValue) {
334                 if(this.mainNode.IsEmptyTag) {
335                     return result;
336                 }
337                 if (! Read()) {
338                     throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
339                 }
340             }
341
342             StringBuilder   sb    = null;
343             bool            first = true;
344
345             while(true) {
346                 switch (NodeType) {
347                 case XmlNodeType.Text:
348                 case XmlNodeType.Whitespace:
349                 case XmlNodeType.SignificantWhitespace:
350 //              case XmlNodeType.CharacterEntity:
351                     if (first) {
352                         result = this.Value;
353                         first = false;
354                     } else {
355                         if (sb == null) {
356                             sb = new StringBuilder(result);
357                         }
358                         sb.Append(this.Value);
359                     }
360                     if (! Read())
361                         throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
362                         break;
363                 default:
364                     return (sb == null) ? result : sb.ToString();
365                 }
366             }
367         }
368
369         public override string ReadInnerXml() {
370             if (ReadState == ReadState.Interactive) {
371                 if (NodeType == XmlNodeType.Element && ! IsEmptyElement) {
372                     StringOutput output = new StringOutput(this.processor);
373                     output.OmitXmlDecl();
374                     int depth = Depth;
375
376                     Read();                 // skeep  begin Element
377                     while (depth < Depth) { // process content
378                         Debug.Assert(this.builder != null);
379                         output.RecordDone(this.builder);
380                         Read();
381                     }
382                     Debug.Assert(NodeType == XmlNodeType.EndElement);
383                     Read();                 // skeep end element
384
385                     output.TheEnd();
386                     return output.Result;
387                 }
388                 else if(NodeType == XmlNodeType.Attribute) {
389                     return encoder.AtributeInnerXml(Value);
390                 }
391                 else {
392                     Read();
393                 }
394             }
395             return string.Empty;
396         }
397
398         public override string ReadOuterXml() {
399             if (ReadState == ReadState.Interactive) {
400                 if (NodeType == XmlNodeType.Element) {
401                     StringOutput output = new StringOutput(this.processor);
402                     output.OmitXmlDecl();
403                     bool emptyElement = IsEmptyElement;
404                     int  depth        = Depth;
405                     // process current record
406                     output.RecordDone(this.builder); 
407                     Read();                          
408                     // process internal elements & text nodes
409                     while(depth < Depth) {                      
410                         Debug.Assert(this.builder != null);
411                         output.RecordDone(this.builder);
412                         Read();
413                     }
414                     // process end element
415                     if (! emptyElement) {
416                         output.RecordDone(this.builder);            
417                         Read();
418                     }
419
420                     output.TheEnd();
421                     return output.Result; 
422                 }
423                 else if(NodeType == XmlNodeType.Attribute) {
424                     return encoder.AtributeOuterXml(Name, Value);
425                 }
426                 else {
427                     Read();
428                 }
429             }
430             return string.Empty;
431         }
432
433         //
434         // Nametable and Namespace Helpers
435         //
436
437         public override XmlNameTable NameTable {
438             get {
439                 Debug.Assert(this.nameTable != null);
440                 return this.nameTable;
441             }
442         }
443
444         public override string LookupNamespace(string prefix) {
445             prefix = this.nameTable.Get(prefix);
446
447             if (this.manager != null && prefix != null) {
448                 return this.manager.ResolveNamespace(prefix);
449             }
450             return null;
451         }
452
453         public override void ResolveEntity() {
454             Debug.Assert(NodeType != XmlNodeType.EntityReference);
455
456             if (NodeType != XmlNodeType.EntityReference) {
457                 throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
458             }
459         }
460
461         public override bool ReadAttributeValue() {
462             if (ReadState != ReadState.Interactive || NodeType != XmlNodeType.Attribute) {
463                 return false;
464             }
465
466             if (this.attributeValue == null) {
467                 this.attributeValue = new BuilderInfo();
468                 this.attributeValue.NodeType = XmlNodeType.Text;
469             }
470             if (this.currentInfo == this.attributeValue) {
471                 return false;
472             }
473
474             this.attributeValue.Value = this.currentInfo.Value;
475             this.attributeValue.Depth = this.currentInfo.Depth + 1;
476             this.currentInfo          = this.attributeValue;
477
478             return true;
479         }
480
481         //
482         // RecordOutput interface method implementation
483         //
484
485         public Processor.OutputResult RecordDone(RecordBuilder record) {
486             this.builder        = record;
487             this.mainNode       = record.MainNode;
488             this.attributeList  = record.AttributeList;
489             this.attributeCount = record.AttributeCount;
490             this.manager        = record.Manager;
491
492             this.haveRecord     = true;
493             SetMainNode();
494
495             return Processor.OutputResult.Interrupt;
496         }
497
498         public void TheEnd() {
499             // nothing here, was taken care of by RecordBuilder
500         }
501
502         //
503         // Implementation internals
504         //
505
506         private void SetMainNode() {
507             this.currentIndex   = -1;
508             this.currentInfo    = this.mainNode;
509         }
510
511         private void SetAttribute(int attrib) {
512             Debug.Assert(0 <= attrib && attrib < this.attributeCount);
513             Debug.Assert(0 <= attrib && attrib < this.attributeList.Count);
514             Debug.Assert(this.attributeList[attrib] is BuilderInfo);
515
516             this.currentIndex = attrib;
517             this.currentInfo  = (BuilderInfo) this.attributeList[attrib];
518         }
519
520         private BuilderInfo GetBuilderInfo(int attrib) {
521             if (attrib < 0 || this.attributeCount <= attrib) {
522                 throw new ArgumentOutOfRangeException("attrib");
523             }
524
525             Debug.Assert(this.attributeList[attrib] is BuilderInfo);
526
527             return(BuilderInfo) this.attributeList[attrib];
528         }
529
530         private bool FindAttribute(String localName, String namespaceURI, out int attrIndex) {
531             if (namespaceURI == null) {
532                 namespaceURI = string.Empty;
533             }
534             if (localName == null) {
535                 localName    = string.Empty;
536             }
537
538             for (int index = 0; index < this.attributeCount; index ++) {
539                 Debug.Assert(this.attributeList[index] is BuilderInfo);
540
541                 BuilderInfo attribute = (BuilderInfo) this.attributeList[index];
542                 if (attribute.NamespaceURI == namespaceURI && attribute.LocalName == localName) {
543                     attrIndex = index;
544                     return true;
545                 }
546             }
547
548             attrIndex = -1;
549             return false;
550         }
551
552         private bool FindAttribute(String name, out int attrIndex) {
553             if (name == null) {
554                 name  = string.Empty;
555             }
556
557             for (int index = 0; index < this.attributeCount; index ++) {
558                 Debug.Assert(this.attributeList[index] is BuilderInfo);
559
560                 BuilderInfo attribute = (BuilderInfo) this.attributeList[index];
561                 if (attribute.Name == name) {
562                     attrIndex = index;
563                     return true;
564                 }
565             }
566
567             attrIndex = -1;
568             return false;
569         }
570
571         private void Reset() {
572             this.currentIndex = -1;
573             this.currentInfo  = s_DefaultInfo;
574             this.mainNode     = s_DefaultInfo;
575             this.manager      = null;
576         }
577
578         [System.Diagnostics.Conditional("DEBUG")]
579         private void CheckCurrentInfo() {
580             Debug.Assert(this.currentInfo   != null);
581             Debug.Assert(this.attributeCount == 0 || this.attributeList != null);
582             Debug.Assert((this.currentIndex == -1) == (this.currentInfo == this.mainNode));
583             Debug.Assert((this.currentIndex == -1) || (this.currentInfo == this.attributeValue || this.attributeList[this.currentIndex] is BuilderInfo && this.attributeList[this.currentIndex] == this.currentInfo));
584         }
585
586         private class XmlEncoder {
587             private StringBuilder  buffer  = null;
588             private XmlTextEncoder encoder = null;
589
590             private void Init() {
591                 buffer  = new StringBuilder();
592                 encoder = new XmlTextEncoder(new StringWriter(buffer, CultureInfo.InvariantCulture));
593             }
594
595             public string AtributeInnerXml(string value) {
596                 if(encoder == null) Init();
597                 buffer .Length = 0;       // clean buffer
598                 encoder.StartAttribute(/*save:*/false);
599                 encoder.Write(value);
600                 encoder.EndAttribute();
601                 return buffer.ToString();
602             }
603
604             public string AtributeOuterXml(string name, string value) {
605                 if(encoder == null) Init();
606                 buffer .Length = 0;       // clean buffer
607                 buffer .Append(name);
608                 buffer .Append('=');
609                 buffer .Append(QuoteChar);
610                 encoder.StartAttribute(/*save:*/false);
611                 encoder.Write(value);
612                 encoder.EndAttribute();
613                 buffer .Append(QuoteChar);
614                 return buffer.ToString();
615             }
616
617             public char QuoteChar {
618                 get { return '"'; }
619             }
620         }
621     }
622 }