536cd135cc
Former-commit-id: 5624ac747d633e885131e8349322922b6a59baaa
623 lines
20 KiB
C#
623 lines
20 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="ReaderOutput.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
// <owner current="true" primary="true">Microsoft</owner>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Xml.Xsl.XsltOld {
|
|
using Res = System.Xml.Utils.Res;
|
|
using System;
|
|
using System.Globalization;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Text;
|
|
using System.Xml;
|
|
using System.Xml.XPath;
|
|
using System.Collections;
|
|
|
|
internal class ReaderOutput : XmlReader, RecordOutput {
|
|
private Processor processor;
|
|
private XmlNameTable nameTable;
|
|
|
|
// Main node + Fields Collection
|
|
private RecordBuilder builder;
|
|
private BuilderInfo mainNode;
|
|
private ArrayList attributeList;
|
|
private int attributeCount;
|
|
private BuilderInfo attributeValue;
|
|
|
|
// OutputScopeManager
|
|
private OutputScopeManager manager;
|
|
|
|
// Current position in the list
|
|
private int currentIndex;
|
|
private BuilderInfo currentInfo;
|
|
|
|
// Reader state
|
|
private ReadState state = ReadState.Initial;
|
|
private bool haveRecord;
|
|
|
|
// Static default record
|
|
static BuilderInfo s_DefaultInfo = new BuilderInfo();
|
|
|
|
XmlEncoder encoder = new XmlEncoder();
|
|
XmlCharType xmlCharType = XmlCharType.Instance;
|
|
|
|
internal ReaderOutput(Processor processor) {
|
|
Debug.Assert(processor != null);
|
|
Debug.Assert(processor.NameTable != null);
|
|
|
|
this.processor = processor;
|
|
this.nameTable = processor.NameTable;
|
|
|
|
Reset();
|
|
}
|
|
|
|
// XmlReader abstract methods implementation
|
|
public override XmlNodeType NodeType {
|
|
get {
|
|
CheckCurrentInfo();
|
|
return this.currentInfo.NodeType;
|
|
}
|
|
}
|
|
|
|
public override string Name {
|
|
get {
|
|
CheckCurrentInfo();
|
|
string prefix = Prefix;
|
|
string localName = LocalName;
|
|
|
|
if (prefix != null && prefix.Length > 0) {
|
|
if (localName.Length > 0) {
|
|
return nameTable.Add(prefix + ":" + localName);
|
|
}
|
|
else {
|
|
return prefix;
|
|
}
|
|
}
|
|
else {
|
|
return localName;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string LocalName {
|
|
get {
|
|
CheckCurrentInfo();
|
|
return this.currentInfo.LocalName;
|
|
}
|
|
}
|
|
|
|
public override string NamespaceURI {
|
|
get {
|
|
CheckCurrentInfo();
|
|
return this.currentInfo.NamespaceURI;
|
|
}
|
|
}
|
|
|
|
public override string Prefix {
|
|
get {
|
|
CheckCurrentInfo();
|
|
return this.currentInfo.Prefix;
|
|
}
|
|
}
|
|
|
|
public override bool HasValue {
|
|
get {
|
|
return XmlReader.HasValueInternal(NodeType);
|
|
}
|
|
}
|
|
|
|
public override string Value {
|
|
get {
|
|
CheckCurrentInfo();
|
|
return this.currentInfo.Value;
|
|
}
|
|
}
|
|
|
|
public override int Depth {
|
|
get {
|
|
CheckCurrentInfo();
|
|
return this.currentInfo.Depth;
|
|
}
|
|
}
|
|
|
|
public override string BaseURI {
|
|
get {
|
|
return string.Empty;
|
|
}
|
|
}
|
|
|
|
public override bool IsEmptyElement {
|
|
get {
|
|
CheckCurrentInfo();
|
|
return this.currentInfo.IsEmptyTag;
|
|
}
|
|
}
|
|
|
|
public override char QuoteChar {
|
|
get { return encoder.QuoteChar; }
|
|
}
|
|
|
|
public override bool IsDefault {
|
|
get { return false; }
|
|
}
|
|
|
|
public override XmlSpace XmlSpace {
|
|
get { return this.manager != null ? this.manager.XmlSpace : XmlSpace.None; }
|
|
}
|
|
|
|
public override string XmlLang {
|
|
get { return this.manager != null ? this.manager.XmlLang : string.Empty; }
|
|
}
|
|
|
|
// Attribute Accessors
|
|
|
|
public override int AttributeCount {
|
|
get { return this.attributeCount; }
|
|
}
|
|
|
|
public override string GetAttribute(string name) {
|
|
int ordinal;
|
|
if (FindAttribute(name, out ordinal)) {
|
|
Debug.Assert(ordinal >= 0);
|
|
return((BuilderInfo)this.attributeList[ordinal]).Value;
|
|
}
|
|
else {
|
|
Debug.Assert(ordinal == -1);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public override string GetAttribute(string localName, string namespaceURI) {
|
|
int ordinal;
|
|
if (FindAttribute(localName, namespaceURI, out ordinal)) {
|
|
Debug.Assert(ordinal >= 0);
|
|
return((BuilderInfo)this.attributeList[ordinal]).Value;
|
|
}
|
|
else {
|
|
Debug.Assert(ordinal == -1);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public override string GetAttribute(int i) {
|
|
BuilderInfo attribute = GetBuilderInfo(i);
|
|
return attribute.Value;
|
|
}
|
|
|
|
public override string this [int i] {
|
|
get { return GetAttribute(i); }
|
|
}
|
|
|
|
public override string this [string name] {
|
|
get { return GetAttribute(name); }
|
|
}
|
|
|
|
public override string this [string name, string namespaceURI] {
|
|
get { return GetAttribute(name, namespaceURI); }
|
|
}
|
|
|
|
public override bool MoveToAttribute(string name) {
|
|
int ordinal;
|
|
if (FindAttribute(name, out ordinal)) {
|
|
Debug.Assert(ordinal >= 0);
|
|
SetAttribute(ordinal);
|
|
return true;
|
|
}
|
|
else {
|
|
Debug.Assert(ordinal == -1);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override bool MoveToAttribute(string localName, string namespaceURI) {
|
|
int ordinal;
|
|
if (FindAttribute(localName, namespaceURI, out ordinal)) {
|
|
Debug.Assert(ordinal >= 0);
|
|
SetAttribute(ordinal);
|
|
return true;
|
|
}
|
|
else {
|
|
Debug.Assert(ordinal == -1);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public override void MoveToAttribute(int i) {
|
|
if (i < 0 || this.attributeCount <= i) {
|
|
throw new ArgumentOutOfRangeException("i");
|
|
}
|
|
SetAttribute(i);
|
|
}
|
|
|
|
public override bool MoveToFirstAttribute() {
|
|
if (this.attributeCount <= 0) {
|
|
Debug.Assert(this.attributeCount == 0);
|
|
return false;
|
|
}
|
|
else {
|
|
SetAttribute(0);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public override bool MoveToNextAttribute() {
|
|
if (this.currentIndex + 1 < this.attributeCount) {
|
|
SetAttribute(this.currentIndex + 1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public override bool MoveToElement() {
|
|
if (NodeType == XmlNodeType.Attribute || this.currentInfo == this.attributeValue) {
|
|
SetMainNode();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Moving through the Stream
|
|
|
|
public override bool Read() {
|
|
Debug.Assert(this.processor != null || this.state == ReadState.Closed);
|
|
|
|
if (this.state != ReadState.Interactive) {
|
|
if (this.state == ReadState.Initial) {
|
|
state = ReadState.Interactive;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
while (true) { // while -- to ignor empty whitespace nodes.
|
|
if (this.haveRecord) {
|
|
this.processor.ResetOutput();
|
|
this.haveRecord = false;
|
|
}
|
|
|
|
this.processor.Execute();
|
|
|
|
if (this.haveRecord) {
|
|
CheckCurrentInfo();
|
|
// check text nodes on whitespaces;
|
|
switch (this.NodeType) {
|
|
case XmlNodeType.Text :
|
|
if (xmlCharType.IsOnlyWhitespace(this.Value)) {
|
|
this.currentInfo.NodeType = XmlNodeType.Whitespace;
|
|
goto case XmlNodeType.Whitespace;
|
|
}
|
|
Debug.Assert(this.Value.Length != 0, "It whould be Whitespace in this case");
|
|
break;
|
|
case XmlNodeType.Whitespace :
|
|
if(this.Value.Length == 0) {
|
|
continue; // ignoring emty text nodes
|
|
}
|
|
if (this.XmlSpace == XmlSpace.Preserve) {
|
|
this.currentInfo.NodeType = XmlNodeType.SignificantWhitespace;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
Debug.Assert(this.processor.ExecutionDone);
|
|
this.state = ReadState.EndOfFile;
|
|
Reset();
|
|
}
|
|
|
|
return this.haveRecord;
|
|
}
|
|
}
|
|
|
|
public override bool EOF {
|
|
get { return this.state == ReadState.EndOfFile; }
|
|
}
|
|
|
|
public override void Close() {
|
|
this.processor = null;
|
|
this.state = ReadState.Closed;
|
|
Reset();
|
|
}
|
|
|
|
public override ReadState ReadState {
|
|
get { return this.state; }
|
|
}
|
|
|
|
// Whole Content Read Methods
|
|
public override string ReadString() {
|
|
string result = string.Empty;
|
|
|
|
if (NodeType == XmlNodeType.Element || NodeType == XmlNodeType.Attribute || this.currentInfo == this.attributeValue) {
|
|
if(this.mainNode.IsEmptyTag) {
|
|
return result;
|
|
}
|
|
if (! Read()) {
|
|
throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
|
|
}
|
|
}
|
|
|
|
StringBuilder sb = null;
|
|
bool first = true;
|
|
|
|
while(true) {
|
|
switch (NodeType) {
|
|
case XmlNodeType.Text:
|
|
case XmlNodeType.Whitespace:
|
|
case XmlNodeType.SignificantWhitespace:
|
|
// case XmlNodeType.CharacterEntity:
|
|
if (first) {
|
|
result = this.Value;
|
|
first = false;
|
|
} else {
|
|
if (sb == null) {
|
|
sb = new StringBuilder(result);
|
|
}
|
|
sb.Append(this.Value);
|
|
}
|
|
if (! Read())
|
|
throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
|
|
break;
|
|
default:
|
|
return (sb == null) ? result : sb.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
public override string ReadInnerXml() {
|
|
if (ReadState == ReadState.Interactive) {
|
|
if (NodeType == XmlNodeType.Element && ! IsEmptyElement) {
|
|
StringOutput output = new StringOutput(this.processor);
|
|
output.OmitXmlDecl();
|
|
int depth = Depth;
|
|
|
|
Read(); // skeep begin Element
|
|
while (depth < Depth) { // process content
|
|
Debug.Assert(this.builder != null);
|
|
output.RecordDone(this.builder);
|
|
Read();
|
|
}
|
|
Debug.Assert(NodeType == XmlNodeType.EndElement);
|
|
Read(); // skeep end element
|
|
|
|
output.TheEnd();
|
|
return output.Result;
|
|
}
|
|
else if(NodeType == XmlNodeType.Attribute) {
|
|
return encoder.AtributeInnerXml(Value);
|
|
}
|
|
else {
|
|
Read();
|
|
}
|
|
}
|
|
return string.Empty;
|
|
}
|
|
|
|
public override string ReadOuterXml() {
|
|
if (ReadState == ReadState.Interactive) {
|
|
if (NodeType == XmlNodeType.Element) {
|
|
StringOutput output = new StringOutput(this.processor);
|
|
output.OmitXmlDecl();
|
|
bool emptyElement = IsEmptyElement;
|
|
int depth = Depth;
|
|
// process current record
|
|
output.RecordDone(this.builder);
|
|
Read();
|
|
// process internal elements & text nodes
|
|
while(depth < Depth) {
|
|
Debug.Assert(this.builder != null);
|
|
output.RecordDone(this.builder);
|
|
Read();
|
|
}
|
|
// process end element
|
|
if (! emptyElement) {
|
|
output.RecordDone(this.builder);
|
|
Read();
|
|
}
|
|
|
|
output.TheEnd();
|
|
return output.Result;
|
|
}
|
|
else if(NodeType == XmlNodeType.Attribute) {
|
|
return encoder.AtributeOuterXml(Name, Value);
|
|
}
|
|
else {
|
|
Read();
|
|
}
|
|
}
|
|
return string.Empty;
|
|
}
|
|
|
|
//
|
|
// Nametable and Namespace Helpers
|
|
//
|
|
|
|
public override XmlNameTable NameTable {
|
|
get {
|
|
Debug.Assert(this.nameTable != null);
|
|
return this.nameTable;
|
|
}
|
|
}
|
|
|
|
public override string LookupNamespace(string prefix) {
|
|
prefix = this.nameTable.Get(prefix);
|
|
|
|
if (this.manager != null && prefix != null) {
|
|
return this.manager.ResolveNamespace(prefix);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public override void ResolveEntity() {
|
|
Debug.Assert(NodeType != XmlNodeType.EntityReference);
|
|
|
|
if (NodeType != XmlNodeType.EntityReference) {
|
|
throw new InvalidOperationException(Res.GetString(Res.Xml_InvalidOperation));
|
|
}
|
|
}
|
|
|
|
public override bool ReadAttributeValue() {
|
|
if (ReadState != ReadState.Interactive || NodeType != XmlNodeType.Attribute) {
|
|
return false;
|
|
}
|
|
|
|
if (this.attributeValue == null) {
|
|
this.attributeValue = new BuilderInfo();
|
|
this.attributeValue.NodeType = XmlNodeType.Text;
|
|
}
|
|
if (this.currentInfo == this.attributeValue) {
|
|
return false;
|
|
}
|
|
|
|
this.attributeValue.Value = this.currentInfo.Value;
|
|
this.attributeValue.Depth = this.currentInfo.Depth + 1;
|
|
this.currentInfo = this.attributeValue;
|
|
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// RecordOutput interface method implementation
|
|
//
|
|
|
|
public Processor.OutputResult RecordDone(RecordBuilder record) {
|
|
this.builder = record;
|
|
this.mainNode = record.MainNode;
|
|
this.attributeList = record.AttributeList;
|
|
this.attributeCount = record.AttributeCount;
|
|
this.manager = record.Manager;
|
|
|
|
this.haveRecord = true;
|
|
SetMainNode();
|
|
|
|
return Processor.OutputResult.Interrupt;
|
|
}
|
|
|
|
public void TheEnd() {
|
|
// nothing here, was taken care of by RecordBuilder
|
|
}
|
|
|
|
//
|
|
// Implementation internals
|
|
//
|
|
|
|
private void SetMainNode() {
|
|
this.currentIndex = -1;
|
|
this.currentInfo = this.mainNode;
|
|
}
|
|
|
|
private void SetAttribute(int attrib) {
|
|
Debug.Assert(0 <= attrib && attrib < this.attributeCount);
|
|
Debug.Assert(0 <= attrib && attrib < this.attributeList.Count);
|
|
Debug.Assert(this.attributeList[attrib] is BuilderInfo);
|
|
|
|
this.currentIndex = attrib;
|
|
this.currentInfo = (BuilderInfo) this.attributeList[attrib];
|
|
}
|
|
|
|
private BuilderInfo GetBuilderInfo(int attrib) {
|
|
if (attrib < 0 || this.attributeCount <= attrib) {
|
|
throw new ArgumentOutOfRangeException("attrib");
|
|
}
|
|
|
|
Debug.Assert(this.attributeList[attrib] is BuilderInfo);
|
|
|
|
return(BuilderInfo) this.attributeList[attrib];
|
|
}
|
|
|
|
private bool FindAttribute(String localName, String namespaceURI, out int attrIndex) {
|
|
if (namespaceURI == null) {
|
|
namespaceURI = string.Empty;
|
|
}
|
|
if (localName == null) {
|
|
localName = string.Empty;
|
|
}
|
|
|
|
for (int index = 0; index < this.attributeCount; index ++) {
|
|
Debug.Assert(this.attributeList[index] is BuilderInfo);
|
|
|
|
BuilderInfo attribute = (BuilderInfo) this.attributeList[index];
|
|
if (attribute.NamespaceURI == namespaceURI && attribute.LocalName == localName) {
|
|
attrIndex = index;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
attrIndex = -1;
|
|
return false;
|
|
}
|
|
|
|
private bool FindAttribute(String name, out int attrIndex) {
|
|
if (name == null) {
|
|
name = string.Empty;
|
|
}
|
|
|
|
for (int index = 0; index < this.attributeCount; index ++) {
|
|
Debug.Assert(this.attributeList[index] is BuilderInfo);
|
|
|
|
BuilderInfo attribute = (BuilderInfo) this.attributeList[index];
|
|
if (attribute.Name == name) {
|
|
attrIndex = index;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
attrIndex = -1;
|
|
return false;
|
|
}
|
|
|
|
private void Reset() {
|
|
this.currentIndex = -1;
|
|
this.currentInfo = s_DefaultInfo;
|
|
this.mainNode = s_DefaultInfo;
|
|
this.manager = null;
|
|
}
|
|
|
|
[System.Diagnostics.Conditional("DEBUG")]
|
|
private void CheckCurrentInfo() {
|
|
Debug.Assert(this.currentInfo != null);
|
|
Debug.Assert(this.attributeCount == 0 || this.attributeList != null);
|
|
Debug.Assert((this.currentIndex == -1) == (this.currentInfo == this.mainNode));
|
|
Debug.Assert((this.currentIndex == -1) || (this.currentInfo == this.attributeValue || this.attributeList[this.currentIndex] is BuilderInfo && this.attributeList[this.currentIndex] == this.currentInfo));
|
|
}
|
|
|
|
private class XmlEncoder {
|
|
private StringBuilder buffer = null;
|
|
private XmlTextEncoder encoder = null;
|
|
|
|
private void Init() {
|
|
buffer = new StringBuilder();
|
|
encoder = new XmlTextEncoder(new StringWriter(buffer, CultureInfo.InvariantCulture));
|
|
}
|
|
|
|
public string AtributeInnerXml(string value) {
|
|
if(encoder == null) Init();
|
|
buffer .Length = 0; // clean buffer
|
|
encoder.StartAttribute(/*save:*/false);
|
|
encoder.Write(value);
|
|
encoder.EndAttribute();
|
|
return buffer.ToString();
|
|
}
|
|
|
|
public string AtributeOuterXml(string name, string value) {
|
|
if(encoder == null) Init();
|
|
buffer .Length = 0; // clean buffer
|
|
buffer .Append(name);
|
|
buffer .Append('=');
|
|
buffer .Append(QuoteChar);
|
|
encoder.StartAttribute(/*save:*/false);
|
|
encoder.Write(value);
|
|
encoder.EndAttribute();
|
|
buffer .Append(QuoteChar);
|
|
return buffer.ToString();
|
|
}
|
|
|
|
public char QuoteChar {
|
|
get { return '"'; }
|
|
}
|
|
}
|
|
}
|
|
}
|