Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

478 lines
12 KiB
C#

//
// XslTemplate.cs
//
// Authors:
// Ben Maurer (bmaurer@users.sourceforge.net)
// Atsushi Enomoto (ginga@kit.hi-ho.ne.jp)
//
// (C) 2003 Ben Maurer
// (C) 2003 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.Xml.XPath;
using System.Xml.Xsl;
using Mono.Xml.Xsl.Operations;
using Mono.Xml.XPath;
using QName = System.Xml.XmlQualifiedName;
namespace Mono.Xml.Xsl {
internal class XslModedTemplateTable {
class TemplateWithPriority : IComparable {
public readonly double Priority;
public readonly XslTemplate Template;
public readonly Pattern Pattern;
public readonly int TemplateID;
public TemplateWithPriority (XslTemplate t, Pattern p)
{
Template = t;
Pattern = p;
Priority = p.DefaultPriority;
TemplateID = t.Id;
}
public TemplateWithPriority (XslTemplate t, double p)
{
Template = t;
Pattern = t.Match;
Priority = p;
TemplateID = t.Id;
}
public int CompareTo (object o)
{
TemplateWithPriority a = this,
b = (TemplateWithPriority)o;
//Debug.WriteLine (a.Pattern.ToString () + " ? " + b.Pattern.ToString ());
//Debug.WriteLine (a.Priority + " " + b.Priority);
int r0 = a.Priority.CompareTo (b.Priority);
//Debug.WriteLine (r0);
if (r0 != 0) return r0;
int r1 = a.TemplateID.CompareTo (b.TemplateID);
//Debug.WriteLine (r1);
return r1;
}
public bool Matches (XPathNavigator n, XslTransformProcessor p)
{
//Debug.WriteLine (Pattern.ToString ());
return p.Matches (Pattern, n);
}
}
// [QName name]=>XslTemplate
ArrayList unnamedTemplates = new ArrayList ();
XmlQualifiedName mode;
public XslModedTemplateTable (XmlQualifiedName mode)
{
if (mode == null)
throw new InvalidOperationException ();
this.mode = mode;
}
public XmlQualifiedName Mode {
get { return mode; }
}
public void Add (XslTemplate t)
{
if (!double.IsNaN (t.Priority))
unnamedTemplates.Add (new TemplateWithPriority (t, t.Priority));
else
Add (t, t.Match);
}
public void Add (XslTemplate t, Pattern p)
{
if (p is UnionPattern) {
Add (t, ((UnionPattern)p).p0);
Add (t, ((UnionPattern)p).p1);
return;
}
unnamedTemplates.Add (new TemplateWithPriority (t, p));
}
bool sorted = false;
public XslTemplate FindMatch (XPathNavigator node, XslTransformProcessor p)
{
//Debug.WriteLine ("...");
if (!sorted) {
unnamedTemplates.Sort ();
unnamedTemplates.Reverse ();
sorted = true;
}
for (int i = 0; i < unnamedTemplates.Count; i++) {
TemplateWithPriority t = (TemplateWithPriority) unnamedTemplates [i];
if (t.Matches (node, p))
return t.Template;
}
return null;
}
}
internal class XslTemplateTable {
// [QName mode]=>XslTemplateTable
Hashtable templateTables = new Hashtable ();
Hashtable namedTemplates = new Hashtable ();
XslStylesheet parent;
public XslTemplateTable (XslStylesheet parent)
{
this.parent = parent;
}
public Hashtable TemplateTables {
get { return templateTables; }
}
public XslModedTemplateTable this [XmlQualifiedName mode] {
get {
return templateTables [mode] as XslModedTemplateTable;
}
}
public void Add (XslTemplate template)
{
if (template.Name != XmlQualifiedName.Empty) {
if (namedTemplates [template.Name] != null)
throw new InvalidOperationException ("Named template " + template.Name + " is already registered.");
namedTemplates [template.Name] = template;
}
if (template.Match == null) return;
XslModedTemplateTable tbl = this [template.Mode];
if (tbl == null) {
tbl = new XslModedTemplateTable (template.Mode);
Add (tbl);
}
tbl.Add (template);
}
public void Add (XslModedTemplateTable table)
{
if (this [table.Mode] != null)
throw new InvalidOperationException ("Mode " + table.Mode + " is already registered.");
templateTables.Add (table.Mode, table);
}
public XslTemplate FindMatch (XPathNavigator node, XmlQualifiedName mode, XslTransformProcessor p)
{
XslTemplate ret;
if (this [mode] != null)
{
ret = this [mode].FindMatch (node, p);
if (ret != null) return ret;
}
for (int i = parent.Imports.Count - 1; i >= 0; i--)
{
XslStylesheet s = (XslStylesheet)parent.Imports [i];
ret = s.Templates.FindMatch (node, mode, p);
if (ret != null)
return ret;
}
return null;
}
public XslTemplate FindTemplate (XmlQualifiedName name)
{
XslTemplate ret = (XslTemplate)namedTemplates [name];
if (ret != null) return ret;
for (int i = parent.Imports.Count - 1; i >= 0; i--) {
XslStylesheet s = (XslStylesheet)parent.Imports [i];
ret = s.Templates.FindTemplate (name);
if (ret != null)
return ret;
}
return null;
}
}
internal class XslTemplate
{
XmlQualifiedName name;
Pattern match;
XmlQualifiedName mode;
double priority = double.NaN;
ArrayList parameters;
XslOperation content;
static int nextId = 0;
public readonly int Id = nextId ++;
XslStylesheet style;
int stackSize;
public XslTemplate (Compiler c)
{
if (c == null) return; // built in template
this.style = c.CurrentStylesheet;
c.PushScope ();
if (c.Input.Name == "template" &&
c.Input.NamespaceURI == Compiler.XsltNamespace &&
c.Input.MoveToAttribute ("mode", String.Empty)) {
c.Input.MoveToParent ();
if (!c.Input.MoveToAttribute ("match", String.Empty))
throw new XsltCompileException ("XSLT 'template' element must not have 'mode' attribute when it does not have 'match' attribute", null, c.Input);
c.Input.MoveToParent ();
}
if (c.Input.NamespaceURI != Compiler.XsltNamespace) {
this.name = QName.Empty;
this.match = c.CompilePattern ("/", c.Input);
this.mode = QName.Empty;
} else {
this.name = c.ParseQNameAttribute ("name");
this.match = c.CompilePattern (c.GetAttribute ("match"), c.Input);
this.mode = c.ParseQNameAttribute ("mode");
string pri = c.GetAttribute ("priority");
if (pri != null) {
try {
this.priority = double.Parse (pri, CultureInfo.InvariantCulture);
} catch (FormatException ex) {
throw new XsltException ("Invalid priority number format", ex, c.Input);
}
}
}
Parse (c);
stackSize = c.PopScope ().VariableHighTide;
}
public XmlQualifiedName Name {
get { return name; }
}
public Pattern Match {
get {
return match;
}
}
public XmlQualifiedName Mode {
get { return mode; }
}
public double Priority {
get { return priority; }
}
public XslStylesheet Parent {
get { return style; }
}
private void Parse (Compiler c) {
if (c.Input.NamespaceURI != Compiler.XsltNamespace) {
content = c.CompileTemplateContent ();
return;
}
if (c.Input.MoveToFirstChild ()) {
bool alldone = true;
XPathNavigator contentStart = c.Input.Clone ();
bool shouldMove = false;
do {
if (shouldMove) {
shouldMove = false;
contentStart.MoveTo (c.Input);
}
if (c.Input.NodeType == XPathNodeType.Text)
{ alldone = false; break; }
if (c.Input.NodeType != XPathNodeType.Element)
continue;
if (c.Input.NamespaceURI != Compiler.XsltNamespace)
{ alldone = false; break; }
if (c.Input.LocalName != "param")
{ alldone = false; break; }
if (this.parameters == null)
this.parameters = new ArrayList ();
parameters.Add (new XslLocalParam (c));
shouldMove = true;
} while (c.Input.MoveToNext ());
if (!alldone) {
c.Input.MoveTo (contentStart);
content = c.CompileTemplateContent ();
}
c.Input.MoveToParent ();
}
}
string LocationMessage {
get {
XslCompiledElementBase op = (XslCompiledElementBase) content;
return String.Format (" from\nxsl:template {0} at {1} ({2},{3})", Match, style.BaseURI, op.LineNumber, op.LinePosition);
}
}
void AppendTemplateFrame (XsltException ex)
{
ex.AddTemplateFrame (LocationMessage);
}
public virtual void Evaluate (XslTransformProcessor p, Hashtable withParams)
{
if (XslTransform.TemplateStackFrameError) {
try {
EvaluateCore (p, withParams);
} catch (XsltException ex) {
AppendTemplateFrame (ex);
throw ex;
} catch (Exception) {
// Note that this catch causes different
// type of error to be thrown (esp.
// this causes NUnit test regression).
XsltException e = new XsltException ("Error during XSLT processing: ", null, p.CurrentNode);
AppendTemplateFrame (e);
throw e;
}
}
else
EvaluateCore (p, withParams);
}
void EvaluateCore (XslTransformProcessor p, Hashtable withParams)
{
if (XslTransform.TemplateStackFrameOutput != null)
XslTransform.TemplateStackFrameOutput.WriteLine (LocationMessage);
p.PushStack (stackSize);
if (parameters != null) {
if (withParams == null) {
int len = parameters.Count;
for (int i = 0; i < len; i++) {
XslLocalParam param = (XslLocalParam)parameters [i];
param.Evaluate (p);
}
} else {
int len = parameters.Count;
for (int i = 0; i < len; i++) {
XslLocalParam param = (XslLocalParam)parameters [i];
object o = withParams [param.Name];
if (o != null)
param.Override (p, o);
else
param.Evaluate (p);
}
}
}
if (content != null)
content.Evaluate (p);
p.PopStack ();
}
public void Evaluate (XslTransformProcessor p)
{
Evaluate (p, null);
}
}
internal class XslDefaultNodeTemplate : XslTemplate {
QName mode;
static XslDefaultNodeTemplate instance = new XslDefaultNodeTemplate (QName.Empty);
public XslDefaultNodeTemplate (QName mode) : base (null)
{
this.mode = mode;
}
public static XslTemplate Instance {
get { return instance; }
}
public override void Evaluate (XslTransformProcessor p, Hashtable withParams)
{
p.ApplyTemplates (p.CurrentNode.SelectChildren (XPathNodeType.All), mode, null);
}
}
internal class XslEmptyTemplate : XslTemplate {
static XslEmptyTemplate instance = new XslEmptyTemplate ();
XslEmptyTemplate () : base (null) {}
public static XslTemplate Instance {
get { return instance; }
}
public override void Evaluate (XslTransformProcessor p, Hashtable withParams)
{
}
}
internal class XslDefaultTextTemplate: XslTemplate {
static XslDefaultTextTemplate instance = new XslDefaultTextTemplate ();
XslDefaultTextTemplate () : base (null) {}
public static XslTemplate Instance {
get { return instance; }
}
public override void Evaluate (XslTransformProcessor p, Hashtable withParams)
{
if (p.CurrentNode.NodeType == XPathNodeType.Whitespace) {
if (p.PreserveOutputWhitespace)
p.Out.WriteWhitespace (p.CurrentNode.Value);
}
else
p.Out.WriteString (p.CurrentNode.Value);
}
}
}