From: Oleg T. <he...@us...> - 2005-11-17 21:53:00
|
Update of /cvsroot/mvp-xml/EXSLT/v2/src/Exslt/Xsl In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv22424/v2/src/Exslt/Xsl Added Files: IXmlTransform.cs MvpXslTransform.cs XslReader.cs Log Message: --- NEW FILE: IXmlTransform.cs --- #region using using System; using System.IO; using System.Xml; using System.Xml.Xsl; using System.Xml.XPath; #endregion namespace Mvp.Xml.Common.Xsl { /// <summary> /// New experimental generic XML transform interface. Defines an API for /// transforming <see cref="XmlInput"/> into <see cref="XmlOutput"/>. /// </summary> public interface IXmlTransform { /// <summary> /// Transforms given <see cref="XmlInput"/> into <see cref="XmlOutput"/>. /// </summary> /// <param name="defaulDocument">Default input XML document</param> /// <param name="args">Parameters</param> /// <param name="output">Represents the transformation's output</param> void Transform(XmlInput defaulDocument, XsltArgumentList args, XmlOutput output); /// <summary> /// Defines default output settings. /// </summary> XmlWriterSettings OutputSettings { get; } } /// <summary> /// XmlInput class represents generic XML input to a trasnformation. The actual /// XML to be transformed can be provided as string URI, <see cref="Stream"/>, /// <see cref="TextReader"/>, <see cref="XmlReader"/> or <see cref="IXPathNavigable"/>. /// Optional <see cref="XmlResolver"/> is used to resolve external references when /// loading input XML document and URIs in a "document()" function calls during /// transformation. /// </summary> public class XmlInput { internal object source ; internal XmlResolver resolver; /// <summary> /// Creates new XmlInput object for an XML document provided as an /// <see cref="XmlReader"/>. Also registers an <see cref="XmlResolver"/> to be used /// for resolving external references in the XML document and document() function. /// </summary> /// <param name="reader">Input XML document</param> /// <param name="resolver"><see cref="XmlResolver"/> to resolve external references</param> public XmlInput(XmlReader reader, XmlResolver resolver) { this.source = reader; this.resolver = resolver; } /// <summary> /// Creates new XmlInput object for an XML document provided as a /// <see cref="TextReader"/>. Also registers an <see cref="XmlResolver"/> to be used /// for resolving external references in the XML document and document() function. /// </summary> /// <param name="reader">Input XML document</param> /// <param name="resolver"><see cref="XmlResolver"/> to resolve external references</param> public XmlInput(TextReader reader, XmlResolver resolver) { this.source = reader; this.resolver = resolver; } /// <summary> /// Creates new XmlInput object for an XML document provided as a /// <see cref="Stream"/>. Also registers an <see cref="XmlResolver"/> to be used /// for resolving external references in the XML document and document() function. /// </summary> /// <param name="stream">Input XML document</param> /// <param name="resolver"><see cref="XmlResolver"/> to resolve external references</param> public XmlInput(Stream stream, XmlResolver resolver) { this.source = stream; this.resolver = resolver; } /// <summary> /// Creates new XmlInput object for an XML document provided as an URI. /// Also registers an <see cref="XmlResolver"/> to be used /// for resolving external references in the XML document and document() function. /// </summary> /// <param name="uri">Input XML document</param> /// <param name="resolver"><see cref="XmlResolver"/> to resolve external references</param> public XmlInput(String uri , XmlResolver resolver) { this.source = uri ; this.resolver = resolver; } /// <summary> /// Creates new XmlInput object for an XML document provided as an /// <see cref="IXPathNavigable"/>. Also registers an <see cref="XmlResolver"/> to be used /// for resolving document() function. /// </summary> /// <param name="nav">Input XML document</param> /// <param name="resolver"><see cref="XmlResolver"/> to resolve external references</param> public XmlInput(IXPathNavigable nav , XmlResolver resolver) { this.source = nav ; this.resolver = resolver; } /// <summary> /// Creates new XmlInput object for an XML document provided as an /// <see cref="XmlReader"/>. /// </summary> /// <param name="reader">Input XML document</param> public XmlInput(XmlReader reader) : this(reader, new DefaultXmlResolver()) {} /// <summary> /// Creates new XmlInput object for an XML document provided as a /// <see cref="TextReader"/>. /// </summary> /// <param name="reader">Input XML document</param> public XmlInput(TextReader reader) : this(reader, new DefaultXmlResolver()) {} /// <summary> /// Creates new XmlInput object for an XML document provided as a /// <see cref="Stream"/>. /// </summary> /// <param name="stream">Input XML document</param> public XmlInput(Stream stream) : this(stream, new DefaultXmlResolver()) {} /// <summary> /// Creates new XmlInput object for an XML document provided as an URI. /// </summary> /// <param name="uri">Input XML document</param> public XmlInput(String uri ) : this(uri , new DefaultXmlResolver()) {} /// <summary> /// Creates new XmlInput object for an XML document provided as an /// <see cref="IXPathNavigable"/>. /// </summary> /// <param name="nav">Input XML document</param> public XmlInput(IXPathNavigable nav ) : this(nav , new DefaultXmlResolver()) {} // We can add set of implicit constructors. // I am not shre that this will be for good, so I commented them for now. //public static implicit operator XmlInput(XmlReader reader) { return new XmlInput(reader); } //public static implicit operator XmlInput(TextReader reader) { return new XmlInput(reader); } //public static implicit operator XmlInput(Stream stream) { return new XmlInput(stream); } //public static implicit operator XmlInput(String uri ) { return new XmlInput(uri ); } //public static implicit operator XmlInput(XPathNavigator nav ) { return new XmlInput(nav ); } // the trick doesn't work with interfaces } /// <summary> /// XmlOutput class represents generic XML transformation output. An output XML /// can be written to an URI, <see cref="Stream"/>, <see cref="TextWriter"/> or /// <see cref="XmlWriter"/>. /// </summary> public class XmlOutput { internal object destination; /// <summary> /// Creates new XmlOutput object over an <see cref="XmlWriter"/>. /// </summary> /// <param name="writer">An <see cref="XmlWriter"/> to write output to</param> public XmlOutput(XmlWriter writer) { this.destination = writer; } /// <summary> /// Creates new XmlOutput object over a <see cref="TextWriter"/>. /// </summary> /// <param name="writer">A <see cref="TextWriter"/> to write output to</param> public XmlOutput(TextWriter writer) { this.destination = writer; } /// <summary> /// Creates new XmlOutput object over a <see cref="Stream"/>. /// </summary> /// <param name="stream">A <see cref="Stream"/> to write output to</param> public XmlOutput(Stream stream ) { this.destination = stream ; } /// <summary> /// Creates new XmlOutput object over an URI. /// </summary> /// <param name="uri">An URI to write output to</param> public XmlOutput(String uri ) { this.destination = uri ; } // We will add overrides with XmlOutputResolver here later to support multiple output documents (<xsl:result-document>) } /// <summary> /// XmlUrlResolver wrapper allowing us to recognize the case when no /// XmlResolver was passed. /// </summary> internal class DefaultXmlResolver : XmlUrlResolver { public DefaultXmlResolver() : base() {} } } --- NEW FILE: MvpXslTransform.cs --- #region using using System; using System.IO; using System.Xml; using System.Xml.Xsl; using System.Xml.XPath; using System.Reflection; using System.CodeDom.Compiler; using Mvp.Xml.Exslt; #endregion namespace Mvp.Xml.Common.Xsl { /// <summary> /// <para>MvpXslTransform class extends capabilities of the <see cref="XslCompiledTransform"/> /// class by adding support for transforming into <see cref="XmlReader"/>, /// vast collection of EXSLT extention functions, multiple outputs and /// transforming of <see cref="IXPathNavigable"/> along with <see cref="XmlResolver"/>. /// Also MvpXslTransform class provides new clean experimental XSL transformation API /// by introducing concepts of <see cref="IXmlTransform"/> interface, <see cref="XmlInput"/> /// and <see cref="XmlOutput"/>.</para> /// </summary> /// <remarks><para>MvpXslTransform class is thread-safe for Transorm() methods. I.e. /// once MvpXslTransform object is loaded, you can safely call its Transform() methods /// in multiple threads simultaneously.</para> /// <para>MvpXslTransform supports EXSLT extension functions from the following namespaces: /// * http://exslt.org/common /// * http://exslt.org/dates-and-times /// * http://exslt.org/math /// * http://exslt.org/random /// * http://exslt.org/regular-expressions /// * http://exslt.org/sets /// * http://exslt.org/strings /// * http://gotdotnet.com/exslt/dates-and-times /// * http://gotdotnet.com/exslt/math /// * http://gotdotnet.com/exslt/regular-expressions /// * http://gotdotnet.com/exslt/sets /// * http://gotdotnet.com/exslt/strings /// * http://gotdotnet.com/exslt/dynamic</para> /// <para>Multioutput (<exsl:document> element) is turned off by default and can /// be turned on using MultiOutput property. Note, that multioutput is not supported /// when transfomation is done to <see cref="XmlWriter"/> or <see cref="XmlReader"/>.</para> /// <para>MvpXslTransform uses XSLT extension objects and reflection and so using /// it requires FullTrust security level.</para> /// </remarks> public class MvpXslTransform : IXmlTransform { private XslCompiledTransform compiledTransform; private object sync = new object(); private ExsltFunctionNamespace supportedFunctions = ExsltFunctionNamespace.All; private bool multiOutput; #region ctors /// <summary> /// Initializes a new instance of the MvpXslTransform class. /// </summary> public MvpXslTransform() { this.compiledTransform = new XslCompiledTransform(); } /// <summary> /// Initializes a new instance of the MvpXslTransform /// class with the specified debug setting. /// </summary> public MvpXslTransform(bool debug) { this.compiledTransform = new XslCompiledTransform(debug); } #endregion #region Load() method overloads /// <summary> Loads the XSLT stylesheet contained in the IXPathNavigable</summary> public void Load(IXPathNavigable ixn) { this.compiledTransform.Load(ixn); } /// <summary> Loads the XSLT stylesheet specified by a URI</summary> public void Load(string stylesheetUri) { this.compiledTransform.Load(stylesheetUri); } /// <summary> Loads the XSLT stylesheet contained in the XmlReader</summary> public void Load(XmlReader reader) { this.compiledTransform.Load(reader); } /// <summary> /// Compiles the XSLT style sheet contained in the IXPathNavigable. /// The XmlResolver resolves any XSLT import or include elements and the /// XSLT settings determine the permissions for the style sheet. /// </summary> public void Load(IXPathNavigable stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) { this.compiledTransform.Load(stylesheet, settings, stylesheetResolver); } /// <summary> /// Loads and compiles the XSLT style sheet specified by the URI. /// The XmlResolver resolves any XSLT import or include elements and the /// XSLT settings determine the permissions for the style sheet. /// </summary> public void Load(string stylesheetUri, XsltSettings settings, XmlResolver stylesheetResolver) { this.compiledTransform.Load(stylesheetUri, settings, stylesheetResolver); } /// <summary> /// Compiles the XSLT style sheet contained in the XmlReader. /// The XmlResolver resolves any XSLT import or include elements and the /// XSLT settings determine the permissions for the style sheet. /// </summary> public void Load(XmlReader stylesheet, XsltSettings settings, XmlResolver stylesheetResolver) { this.compiledTransform.Load(stylesheet, settings, stylesheetResolver); } #endregion #region public properties /// <summary> /// Bitwise enumeration used to specify which EXSLT functions should be accessible to /// the MvpXslTransform object. The default value is ExsltFunctionNamespace.All /// </summary> public ExsltFunctionNamespace SupportedFunctions { set { if (Enum.IsDefined(typeof(ExsltFunctionNamespace), value)) supportedFunctions = value; } get { return supportedFunctions; } } /// <summary> /// Boolean flag used to specify whether multiple output (via exsl:document) is /// supported. /// Note: This property is ignored (hence multiple output is not supported) when /// transformation is done to XmlReader or XmlWriter (use overloaded method, /// which transforms to MultiXmlTextWriter instead). /// Note: Because of some restrictions and slight overhead this feature is /// disabled by default. If you need multiple output support, set this property to /// true before the Transform() call. /// </summary> public bool MultiOutput { get { return multiOutput; } set { multiOutput = value; } } /// <summary> /// Gets the TempFileCollection that contains the temporary files generated /// on disk after a successful call to the Load method. /// </summary> public TempFileCollection TemporaryFiles { get { return this.compiledTransform.TemporaryFiles; } } #endregion #region IXmlTransform impl /// <summary> /// Transforms given <see cref="XmlInput"/> into <see cref="XmlOutput"/>. /// </summary> /// <param name="defaulDocument">Default input XML document</param> /// <param name="args">Parameters</param> /// <param name="output">Represents the transformation's output</param> public void Transform(XmlInput defaultDocument, XsltArgumentList args, XmlOutput output) { if (defaultDocument == null) throw new ArgumentNullException("defaltDocument"); XmlWriter xmlWriter = output.destination as XmlWriter; bool closeWriter = false; if (xmlWriter == null) { closeWriter = true; while (true) { TextWriter txtWriter = output.destination as TextWriter; if (txtWriter != null) { if (multiOutput) { xmlWriter = new MultiXmlTextWriter(txtWriter); } else { xmlWriter = XmlWriter.Create(txtWriter, this.compiledTransform.OutputSettings); } break; } Stream strm = output.destination as Stream; if (strm != null) { if (multiOutput) { xmlWriter = new MultiXmlTextWriter(strm, this.compiledTransform.OutputSettings.Encoding); } else { xmlWriter = XmlWriter.Create(strm, this.compiledTransform.OutputSettings); } break; } String str = output.destination as String; if (str != null) { if (multiOutput) { xmlWriter = new MultiXmlTextWriter(str, this.compiledTransform.OutputSettings.Encoding); } else { // BugBug: We should read doc before creating output file in case they are the same xmlWriter = XmlWriter.Create(str, this.compiledTransform.OutputSettings); } break; } throw new Exception("Unexpected XmlOutput"); } } try { TransformToWriter(defaultDocument, args, xmlWriter); } finally { if (closeWriter) { xmlWriter.Close(); } } } /// <summary> /// Gets an <see cref="XmlWriterSettings"/> object that contains the output /// information derived from the xsl:output element of the style sheet. /// </summary> public XmlWriterSettings OutputSettings { get { if (this.compiledTransform != null) { return this.compiledTransform.OutputSettings; } else { return new XmlWriterSettings(); } } } #endregion #region additional Transform() methods /// <summary> /// Transforms given <see cref="XmlInput"/> into <see cref="XmlReader"/>. /// </summary> /// <param name="defaulDocument">Default input XML document</param> /// <param name="args">Parameters</param> public XmlReader Transform(XmlInput defaultDocument, XsltArgumentList args) { XslReader r = new XslReader(this.compiledTransform); r.StartTransform(defaultDocument, AddExsltExtensionObjects(args)); return r; } #endregion #region private stuff private static XmlReaderSettings DefaultReaderSettings; static MvpXslTransform() { DefaultReaderSettings = new XmlReaderSettings(); DefaultReaderSettings.ProhibitDtd = false; } private XmlReaderSettings GetReaderSettings(XmlInput defaultDocument) { if (defaultDocument.resolver is DefaultXmlResolver) { return DefaultReaderSettings; } else { XmlReaderSettings settings = DefaultReaderSettings.Clone(); settings.XmlResolver = defaultDocument.resolver; return settings; } } private void TransformToWriter(XmlInput defaultDocument, XsltArgumentList xsltArgs, XmlWriter xmlWriter) { XsltArgumentList args = AddExsltExtensionObjects(xsltArgs); XmlReader xmlReader = defaultDocument.source as XmlReader; if (xmlReader != null) { this.compiledTransform.Transform(xmlReader, args, xmlWriter, defaultDocument.resolver); return; } IXPathNavigable nav = defaultDocument.source as IXPathNavigable; if (nav != null) { if (defaultDocument.resolver is DefaultXmlResolver) { this.compiledTransform.Transform(nav, args, xmlWriter); } else { TransformIXPathNavigable(nav, args, xmlWriter, defaultDocument.resolver); } return; } string str = defaultDocument.source as string; if (str != null) { using (XmlReader reader = XmlReader.Create(str, GetReaderSettings(defaultDocument))) { this.compiledTransform.Transform(reader, args, xmlWriter, defaultDocument.resolver); } return; } Stream strm = defaultDocument.source as Stream; if (strm != null) { using (XmlReader reader = XmlReader.Create(strm, GetReaderSettings(defaultDocument))) { this.compiledTransform.Transform(reader, args, xmlWriter, defaultDocument.resolver); } return; } TextReader txtReader = defaultDocument.source as TextReader; if (txtReader != null) { using (XmlReader reader = XmlReader.Create(txtReader, GetReaderSettings(defaultDocument))) { this.compiledTransform.Transform(reader, args, xmlWriter, defaultDocument.resolver); } return; } throw new Exception("Unexpected XmlInput"); } private void TransformIXPathNavigable(IXPathNavigable nav, XsltArgumentList args, XmlWriter xmlWriter, XmlResolver resolver) { object command = this.compiledTransform.GetType().GetField( "command", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(this.compiledTransform); MethodInfo executeMethod = command.GetType().GetMethod("Execute", BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { typeof(IXPathNavigable), typeof(XmlResolver), typeof(XsltArgumentList), typeof(XmlWriter) }, null); executeMethod.Invoke(this.compiledTransform, new object[] { nav, resolver, AddExsltExtensionObjects(args), xmlWriter }); } /// <summary> /// Adds the objects that implement the EXSLT extensions to the provided argument /// list. The extension objects added depend on the value of the SupportedFunctions /// property. /// </summary> /// <param name="list">The argument list</param> /// <returns>An XsltArgumentList containing the contents of the list passed in /// and objects that implement the EXSLT. </returns> /// <remarks>If null is passed in then a new XsltArgumentList is constructed. </remarks> private XsltArgumentList AddExsltExtensionObjects(XsltArgumentList list) { if (list == null) { list = new XsltArgumentList(); } lock (sync) { //remove all our extension objects in case the XSLT argument list is being reused list.RemoveExtensionObject(ExsltNamespaces.Math); list.RemoveExtensionObject(ExsltNamespaces.Random); list.RemoveExtensionObject(ExsltNamespaces.DatesAndTimes); list.RemoveExtensionObject(ExsltNamespaces.RegularExpressions); list.RemoveExtensionObject(ExsltNamespaces.Strings); list.RemoveExtensionObject(ExsltNamespaces.Sets); list.RemoveExtensionObject(ExsltNamespaces.GDNDatesAndTimes); list.RemoveExtensionObject(ExsltNamespaces.GDNMath); list.RemoveExtensionObject(ExsltNamespaces.GDNRegularExpressions); list.RemoveExtensionObject(ExsltNamespaces.GDNSets); list.RemoveExtensionObject(ExsltNamespaces.GDNStrings); list.RemoveExtensionObject(ExsltNamespaces.GDNDynamic); //add extension objects as specified by SupportedFunctions if ((this.SupportedFunctions & ExsltFunctionNamespace.Math) > 0) { list.AddExtensionObject(ExsltNamespaces.Math, new ExsltMath()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.Random) > 0) { list.AddExtensionObject(ExsltNamespaces.Random, new ExsltRandom()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.DatesAndTimes) > 0) { list.AddExtensionObject(ExsltNamespaces.DatesAndTimes, new ExsltDatesAndTimes()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.RegularExpressions) > 0) { list.AddExtensionObject(ExsltNamespaces.RegularExpressions, new ExsltRegularExpressions()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.Strings) > 0) { list.AddExtensionObject(ExsltNamespaces.Strings, new ExsltStrings()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.Sets) > 0) { list.AddExtensionObject(ExsltNamespaces.Sets, new ExsltSets()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.GDNDatesAndTimes) > 0) { list.AddExtensionObject(ExsltNamespaces.GDNDatesAndTimes, new GDNDatesAndTimes()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.GDNMath) > 0) { list.AddExtensionObject(ExsltNamespaces.GDNMath, new GDNMath()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.GDNRegularExpressions) > 0) { list.AddExtensionObject(ExsltNamespaces.GDNRegularExpressions, new GDNRegularExpressions()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.GDNSets) > 0) { list.AddExtensionObject(ExsltNamespaces.GDNSets, new GDNSets()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.GDNStrings) > 0) { list.AddExtensionObject(ExsltNamespaces.GDNStrings, new GDNStrings()); } if ((this.SupportedFunctions & ExsltFunctionNamespace.GDNDynamic) > 0) { list.AddExtensionObject(ExsltNamespaces.GDNDynamic, new GDNDynamic()); } } return list; } #endregion } } --- NEW FILE: XslReader.cs --- #region using using System; using System.Collections.Generic; using System.Text; using System.IO; using System.Xml; using System.Xml.Xsl; using System.Xml.XPath; using System.Diagnostics; using System.Threading; #endregion namespace Mvp.Xml.Common.Xsl { /// <summary> /// <para>XslReader provides an efficient way to read results of an XSL /// transformation via an <see cref="XmlReader"/> API. Due to /// architectural and performance reasons the <see cref="XslCompiledTransform"/> /// class doesn't support transforming to an <see cref="XmlReader"/> as obsolete [...1065 lines suppressed...] } } public override void Close() { Write(XmlNodeType.None, null, null); lock (this) { Monitor.Pulse(this); } } public override void GetToken(int attNum, out QName name, out string value) { Debug.Assert(0 <= attNum && attNum < readEndPos - readStartPos - 1); XmlNodeType nodeType; XmlToken.Get(ref buffer[(readStartPos + attNum) & mask], out nodeType, out name, out value); Debug.Assert(nodeType == (attNum == 0 ? XmlNodeType.Element : XmlNodeType.Attribute), "We use GetToken() only to access parts of start element tag."); } } #endregion ------------------------------- Supporting classes ------------------------------ } } |