5 // Oleg Tkachenko (oleg@tkachenko.com)
7 // (C) 2003 Oleg Tkachenko
11 using System.Collections;
15 namespace Mono.Xml.Xsl
18 /// Generic implemenatation of the Outputter.
19 /// Works as a buffer between Transformation classes and an Emitter.
20 /// Implements attributes dublicate checking, nemaspace stuff and
21 /// choosing of right Emitter implementation.
23 public class GenericOutputter : Outputter {
24 private Hashtable _outputs;
26 private XslOutput _currentOutput;
28 private Emitter _emitter;
30 private WriteState _state;
31 // Collection of pending attributes. TODO: Can we make adding an attribute
32 // O(1)? I'm not sure it is that important (this would only really make a difference
33 // if elements had like 10 attributes, which is very rare).
34 Attribute [] pendingAttributes = new Attribute [10];
35 int pendingAttributesPos = 0;
36 //Namespace manager. Subject to optimization.
37 private XmlNamespaceManager _nsManager;
39 private NameTable _nt;
41 private GenericOutputter (Hashtable outputs)
44 _currentOutput = (XslOutput)outputs [String.Empty];
45 _state = WriteState.Start;
46 //TODO: Optimize using nametable
47 _nt = new NameTable ();
48 _nsManager = new XmlNamespaceManager (_nt);
51 public GenericOutputter (XmlWriter writer, Hashtable outputs)
54 _emitter = new XmlWriterEmitter (writer);
57 public GenericOutputter (TextWriter writer, Hashtable outputs)
60 XslOutput xslOutput = (XslOutput)outputs [String.Empty];
61 switch (xslOutput.Method) {
62 case OutputMethod.Unknown: //TODO: handle xml vs html
63 case OutputMethod.XML:
64 //TODO: XmlTextEmitter goes here
65 //_emitter = new XmlTextEmitter (writer);
66 _emitter = new XmlWriterEmitter (new XmlTextWriter (writer));
68 case OutputMethod.HTML:
69 throw new NotImplementedException ("HTML output method is not implemented yet.");
70 case OutputMethod.Text:
71 _emitter = new TextEmitter (writer);
73 case OutputMethod.Custom:
74 throw new NotImplementedException ("Custom output method is not implemented yet.");
79 /// Checks output state and flushes pending attributes and namespaces
80 /// when it's appropriate.
82 private void CheckState ()
84 if (_state == WriteState.Element) {
85 //Push scope to allow to unwind namespaces scope back in WriteEndElement
86 //Subject to optimization - avoid redundant push/pop by moving
87 //namespaces to WriteStartElement
88 _nsManager.PushScope ();
89 //Emit pending attributes
90 for (int i = 0; i < pendingAttributesPos; i++) {
91 Attribute attr = pendingAttributes [i];
92 _emitter.WriteAttributeString (attr.Prefix, attr.LocalName, attr.Namespace, attr.Value);
94 //Attributes flushed, state is Content now
95 _state = WriteState.Content;
99 #region Outputter's methods implementation
101 public override void WriteStartDocument ()
103 if (!_currentOutput.OmitXmlDeclaration)
104 _emitter.WriteStartDocument (_currentOutput.Standalone);
106 _state = WriteState.Prolog;
109 public override void WriteEndDocument ()
111 _emitter.WriteEndDocument ();
114 public override void WriteStartElement (string prefix, string localName, string nsURI)
116 if (_state == WriteState.Prolog) {
117 //Seems to be the first element - take care of Doctype
118 if (_currentOutput.DoctypeSystem != null)
119 _emitter.WriteDocType (prefix + (prefix==null? ":" : "") + localName,
120 _currentOutput.DoctypePublic, _currentOutput.DoctypeSystem);
123 _emitter.WriteStartElement (prefix, localName, nsURI);
124 _state = WriteState.Element;
125 pendingAttributesPos = 0;
128 public override void WriteEndElement ()
131 _emitter.WriteEndElement ();
132 _state = WriteState.Content;
133 //Pop namespace scope
134 _nsManager.PopScope ();
137 public override void WriteAttributeString (string prefix, string localName, string nsURI, string value)
139 //Put attribute to pending attributes collection, replacing namesake one
140 for (int i = 0; i < pendingAttributesPos; i++) {
141 Attribute attr = pendingAttributes [i];
143 if (attr.LocalName == localName && attr.Namespace == nsURI) {
145 //Keep prefix (e.g. when literal attribute is overriden by xsl:attribute)
146 if (attr.Prefix == String.Empty && prefix != String.Empty)
147 attr.Prefix = prefix;
153 if (pendingAttributesPos == pendingAttributes.Length) {
154 Attribute [] old = pendingAttributes;
155 pendingAttributes = new Attribute [pendingAttributesPos * 2 + 1];
156 if (pendingAttributesPos > 0)
157 Array.Copy (old, 0, pendingAttributes, 0, pendingAttributesPos);
159 pendingAttributes [pendingAttributesPos].Prefix = prefix;
160 pendingAttributes [pendingAttributesPos].Namespace = nsURI;
161 pendingAttributes [pendingAttributesPos].LocalName = localName;
162 pendingAttributes [pendingAttributesPos].Value = value;
163 pendingAttributesPos++;
166 public override void WriteNamespaceDecl (string prefix, string nsUri)
168 if (prefix == String.Empty) {
170 if (_nsManager.DefaultNamespace != nsUri) {
171 _nsManager.AddNamespace (prefix, nsUri);
172 _emitter.WriteAttributeString ("", "xmlns", "", nsUri);
174 } else if (_nsManager.LookupPrefix (nsUri) == null) {
175 //That's new namespace - add it to the collection and emit
176 _nsManager.AddNamespace (prefix, nsUri);
177 _emitter.WriteAttributeString ("xmlns", prefix, null, nsUri);
181 public override void WriteComment (string text)
184 _emitter.WriteComment (text);
187 public override void WriteProcessingInstruction (string name, string text)
190 _emitter.WriteProcessingInstruction (name, text);
193 public override void WriteString (string text)
196 _emitter.WriteString (text);
199 public override void WriteRaw (string data)
202 _emitter.WriteRaw (data);
205 public override void Done ()
208 _state = WriteState.Closed;