// (C) 2003 Oleg Tkachenko, Atsushi Enomoto
//
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
using System;
using System.Collections;
+using System.Globalization;
using System.Xml;
using System.IO;
+using System.Text;
namespace Mono.Xml.Xsl
{
/// Implements attributes dublicate checking, nemaspace stuff and
/// choosing of right Emitter implementation.
/// </summary>
- public class GenericOutputter : Outputter {
+ internal class GenericOutputter : Outputter {
private Hashtable _outputs;
//Current xsl:output
private XslOutput _currentOutput;
//Underlying emitter
private Emitter _emitter;
+ // destination TextWriter,
+ // which is pended until the actual output is determined.
+ private TextWriter pendingTextWriter;
+ // also, whitespaces before the first element are cached.
+ StringBuilder pendingFirstSpaces;
//Outputting state
private WriteState _state;
// Collection of pending attributes. TODO: Can we make adding an attribute
private Hashtable _currentNamespaceDecls;
//Name table
private NameTable _nt;
+ // Specified encoding (for TextWriter output)
+ Encoding _encoding;
//Determines whether xsl:copy can output attribute-sets or not.
- bool canProcessAttributes;
- bool insideCData;
+ bool _canProcessAttributes;
+ bool _insideCData;
+ bool _isVariable;
- private GenericOutputter (Hashtable outputs)
+ private GenericOutputter (Hashtable outputs, Encoding encoding)
{
+ _encoding = encoding;
_outputs = outputs;
_currentOutput = (XslOutput)outputs [String.Empty];
_state = WriteState.Start;
_currentNamespaceDecls = new Hashtable ();
}
- public GenericOutputter (XmlWriter writer, Hashtable outputs)
- : this (outputs)
+ public GenericOutputter (XmlWriter writer, Hashtable outputs, Encoding encoding)
+ : this (writer, outputs, encoding, false)
+ {
+ }
+
+ internal GenericOutputter (XmlWriter writer, Hashtable outputs, Encoding encoding, bool isVariable)
+ : this (outputs, encoding)
{
_emitter = new XmlWriterEmitter (writer);
_state = writer.WriteState;
+ _isVariable = isVariable;
+ }
+
+ public GenericOutputter (TextWriter writer, Hashtable outputs, Encoding encoding)
+ : this (outputs, encoding)
+ {
+ this.pendingTextWriter = writer;
+ }
+
+
+ internal GenericOutputter (TextWriter writer, Hashtable outputs)
+ : this (writer, outputs, null)
+ {
+ }
+
+ internal GenericOutputter (XmlWriter writer, Hashtable outputs)
+ : this (writer, outputs, null)
+ {
+ }
+
+ private Emitter Emitter {
+ get {
+ if (_emitter == null)
+ DetermineOutputMethod (null, null);
+ return _emitter;
+ }
}
- public GenericOutputter (TextWriter writer, Hashtable outputs)
- : this (outputs)
- {
- XslOutput xslOutput = (XslOutput)outputs [String.Empty];
+ private void DetermineOutputMethod (string localName, string ns)
+ {
+ XslOutput xslOutput = (XslOutput)_outputs [String.Empty];
switch (xslOutput.Method) {
-
+ case OutputMethod.Unknown:
+ if (localName != null && String.Compare (localName, "html", true, CultureInfo.InvariantCulture) == 0 && ns == String.Empty)
+ goto case OutputMethod.HTML;
+ goto case OutputMethod.XML;
case OutputMethod.HTML:
- _emitter = new HtmlEmitter (writer, xslOutput);
+ _emitter = new HtmlEmitter (pendingTextWriter, xslOutput);
break;
- case OutputMethod.Unknown: //TODO: handle xml vs html
case OutputMethod.XML:
- XmlTextWriter w = new XmlTextWriter (writer);
+ XmlTextWriter w = new XmlTextWriter (pendingTextWriter);
if (xslOutput.Indent == "yes")
w.Formatting = Formatting.Indented;
_emitter = new XmlWriterEmitter (w);
break;
case OutputMethod.Text:
- _emitter = new TextEmitter (writer);
+ _emitter = new TextEmitter (pendingTextWriter);
break;
case OutputMethod.Custom:
- throw new NotImplementedException ("Custom output method is not implemented yet.");
- }
+ throw new NotSupportedException ("Custom output method is not supported in this version.");
+ }
+ pendingTextWriter = null;
}
/// <summary>
Attribute attr = pendingAttributes [i];
string prefix = attr.Prefix;
if (prefix == String.Empty)
- prefix = _nsManager.LookupPrefix (attr.Namespace);
- _emitter.WriteAttributeString (prefix, attr.LocalName, attr.Namespace, attr.Value);
+ prefix = _nsManager.LookupPrefix (attr.Namespace, false);
+ Emitter.WriteAttributeString (prefix, attr.LocalName, attr.Namespace, attr.Value);
}
- foreach (string prefix in _currentNsPrefixes) {
+ for (int i = 0; i < _currentNsPrefixes.Count; i++) {
+ string prefix = (string) _currentNsPrefixes [i];
string uri = _currentNamespaceDecls [prefix] as string;
if (prefix != String.Empty)
- _emitter.WriteAttributeString ("xmlns", prefix, XmlNamespaceManager.XmlnsXmlns, uri);
+ Emitter.WriteAttributeString ("xmlns", prefix, XmlNamespaceManager.XmlnsXmlns, uri);
else
- _emitter.WriteAttributeString (String.Empty, "xmlns", XmlNamespaceManager.XmlnsXmlns, uri);
+ Emitter.WriteAttributeString (String.Empty, "xmlns", XmlNamespaceManager.XmlnsXmlns, uri);
}
_currentNsPrefixes.Clear ();
_currentNamespaceDecls.Clear ();
//Attributes flushed, state is Content now
_state = WriteState.Content;
}
- canProcessAttributes = false;
+ _canProcessAttributes = false;
}
#region Outputter's methods implementation
public override void WriteStartDocument ()
- {
+ {
+ if (_isVariable)
+ return;
+
if (!_currentOutput.OmitXmlDeclaration)
- _emitter.WriteStartDocument (_currentOutput.Standalone);
+ Emitter.WriteStartDocument (_encoding != null ? _encoding : _currentOutput.Encoding, _currentOutput.Standalone);
_state = WriteState.Prolog;
}
public override void WriteEndDocument ()
{
- _emitter.WriteEndDocument ();
+ Emitter.WriteEndDocument ();
}
int _nsCount;
public override void WriteStartElement (string prefix, string localName, string nsURI)
{
+ if (_emitter == null) {
+ this.DetermineOutputMethod (localName, nsURI);
+ if (pendingFirstSpaces != null) {
+ WriteWhitespace (pendingFirstSpaces.ToString ());
+ pendingFirstSpaces = null;
+ }
+ }
+
if (_state == WriteState.Start)
WriteStartDocument ();
//Seems to be the first element - take care of Doctype
// Note that HTML does not require SYSTEM identifier.
if (_currentOutput.DoctypePublic != null || _currentOutput.DoctypeSystem != null)
- _emitter.WriteDocType (prefix + (prefix==null? ":" : "") + localName,
+ Emitter.WriteDocType (prefix + (prefix==null? ":" : "") + localName,
_currentOutput.DoctypePublic, _currentOutput.DoctypeSystem);
}
CheckState ();
- _emitter.WriteStartElement (prefix, localName, nsURI);
+ Emitter.WriteStartElement (prefix, localName, nsURI);
_state = WriteState.Element;
pendingAttributesPos = 0;
- canProcessAttributes = true;
+ _canProcessAttributes = true;
}
public override void WriteEndElement ()
{
CheckState ();
if (fullEndElement)
- _emitter.WriteFullEndElement ();
+ Emitter.WriteFullEndElement ();
else
- _emitter.WriteEndElement ();
+ Emitter.WriteEndElement ();
_state = WriteState.Content;
//Pop namespace scope
_nsManager.PopScope ();
public override void WriteNamespaceDecl (string prefix, string nsUri)
{
- if (_nsManager.LookupNamespace (prefix) == nsUri)
+ if (_nsManager.LookupNamespace (prefix, false) == nsUri)
return;
if (prefix == String.Empty) {
//Default namespace
if (_nsManager.DefaultNamespace != nsUri)
_nsManager.AddNamespace (prefix, nsUri);
- } else if (_nsManager.LookupPrefix (nsUri) == null)
+ } else if (_nsManager.LookupPrefix (nsUri, false) == null)
//That's new namespace - add it to the collection
_nsManager.AddNamespace (prefix, nsUri);
public override void WriteComment (string text)
{
CheckState ();
- _emitter.WriteComment (text);
+ Emitter.WriteComment (text);
}
public override void WriteProcessingInstruction (string name, string text)
{
CheckState ();
- _emitter.WriteProcessingInstruction (name, text);
+ Emitter.WriteProcessingInstruction (name, text);
}
public override void WriteString (string text)
{
CheckState ();
- if (insideCData)
- _emitter.WriteCDataSection (text);
+ if (_insideCData)
+ Emitter.WriteCDataSection (text);
else
- _emitter.WriteString (text);
+ Emitter.WriteString (text);
}
public override void WriteRaw (string data)
{
CheckState ();
- _emitter.WriteRaw (data);
+ Emitter.WriteRaw (data);
}
public override void WriteWhitespace (string text)
{
- CheckState ();
- _emitter.WriteString (text);
+ if (_emitter == null) {
+ if (pendingFirstSpaces == null)
+ pendingFirstSpaces = new StringBuilder ();
+ pendingFirstSpaces.Append (text);
+ if (_state == WriteState.Start)
+ _state = WriteState.Prolog;
+ } else {
+ CheckState ();
+ Emitter.WriteWhitespace (text);
+ }
}
public override void Done ()
{
- _emitter.Done ();
+ Emitter.Done ();
_state = WriteState.Closed;
}
public override bool CanProcessAttributes {
- get { return canProcessAttributes; }
+ get { return _canProcessAttributes; }
}
public override WriteState WriteState { get { return _state; } }
public override bool InsideCDataSection {
- get { return insideCData; }
- set { insideCData = value; }
+ get { return _insideCData; }
+ set { _insideCData = value; }
}
#endregion
}