"Steve" <st***@nospam.com> wrote in message news:C7**********************************@microsof t.com...
Is there any way of specifying the startMode when using the xslTransform class?
The XslTransform class doesn't afford a direct means of setting a
so-called "start mode" that I'm aware of. Start mode is just an
MSXML-specific means of having multiple 'root' template rules each
with a different mode attribute and choosing one of them to apply.
Know that we could use global params, but would rather not have to update
all the stylesheets and any code that uses msxml and the same stylesheets.
Parameters would make your stylesheets more portable in the long
run, because "start mode" is an implementation-specific feature that
you may not be able to take with you when migrating to another XSLT
processor.
However, I think you can accomplish the same effect by using an
<xsl:import> in a "wrapper" stylesheet. The <xsl:apply-templates>
in the wrapper could select the 'root' template rule in the imported
stylesheet having your desired start mode.
Of course, this <xsl:apply-templates> would appear in the 'root' template
rule of the wrapper stylesheet, thus superceding any 'root' template that
doesn't have a mode attribute in the imported stylesheet. Therefore, you
only want to create this wrapper when you're actually going to specify a
start mode (if no start mode is specified, or start mode is null or the empty
string, then the imported stylesheet is simply used directly; which is exactly
as it would be for the start mode property in MSXML).
Here's a concrete example of an XslTransform-aggregating class that I've
dubbed StartModeTransform exposing a StartMode property and managing
when to use, or not to use, the "wrap and import" stylesheet design.
- - - StartModeTransform.cs
using System;
using System.IO;
using System.Security.Policy;
using System.Text;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Xsl;
/// <summary>
/// Encapsulates an <b>XslTransform</b> and adds <i>start mode</i> capability
/// by "wrapping and importing" the loaded stylesheet as required.
/// </summary>
public class StartModeTransform
{
private XslTransform transformer;
private string startMode;
private bool isAdjusted;
private string baseUrl;
public StartModeTransform( ) : this( new XslTransform( ))
{
;
}
public StartModeTransform( XslTransform xslt)
{
this.transformer = xslt;
}
public string StartMode
{
get
{
return this.startMode;
}
set
{
if ( value != this.startMode )
{
this.startMode = value;
this.isAdjusted = false;
if ( ( ( value == null ) || ( 0 == value.Length ) ) && isAdjusted )
// Un-adjust when StartMode is reset to none.
Load( this.baseUrl);
else
// Re-adjust when StartMode changes.
AdjustForStartMode( );
}
}
}
public virtual void Load( string url)
{
this.baseUrl = url;
this.transformer.Load( url);
}
// Implementation of other Load( ) methods left as an
// exercise for the reader.
public virtual void Transform( IXPathNavigable input, XsltArgumentList args,
TextWriter output, XmlResolver resolver)
{
this.transformer.Transform( input, args, output, resolver);
}
// Implementation of other Transform( ) methods left as
// an exercise for the reader.
protected virtual void AdjustForStartMode( )
{
if ( this.isAdjusted || ( null == this.startMode ) || ( 0 == this.startMode.Length ) )
return;
// Import current XSLT stylesheet inside of wrapper that
// honors StartMode.
XmlDocument adjustedXsl = new XmlDocument();
adjustedXsl.Load( "startMode.xsl");
// Replace two placeholders, one for the xsl:import href
// that points to the wrapped stylesheet.
XmlAttribute hrefAttr = ((XmlElement)(adjustedXsl.DocumentElement.FirstChi ld)).Attributes[ "href"];
hrefAttr.Value = String.Format( "{0}", this.baseUrl); // use Format to allow for http:// URLs
// The other placeholder specifies the xsl:apply-templates mode
// that is the start mode to be called in the wrapped stylesheet.
XmlAttribute modeAttr = ((XmlElement)(adjustedXsl.DocumentElement.ChildNod es[ 1].FirstChild)).Attributes[ "mode"];
modeAttr.Value = this.startMode;
// Depending on where you keep the XSLT stylesheet being
// loaded, you may need to subclass XmlResolver and specify
// it here (instead of resolver).
XmlUrlResolver resolver = new XmlUrlResolver( );
Evidence evidence = XmlSecureResolver.CreateEvidenceForUrl( this.baseUrl);
this.transformer.Load( adjustedXsl.CreateNavigator(), resolver, evidence);
this.isAdjusted = true;
return;
}
}
- - -
This class depends on a placeholder XSLT stylesheet into which it adjusts the
<xsl:import>'s href attribute, and the <xsl:apply-templates>'s mode attribute,
to affect the "wrapping and importing" of your stylesheet when a StartMode is
assigned.
- - - startMode.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:import href="X" />
<xsl:template match="/">
<xsl:apply-templates select="/" mode="X" />
</xsl:template>
</xsl:stylesheet>
- - -
The attribute values of this wrapper stylesheet import the originally loaded style-
sheet, and assign the StartMode used to apply the 'root' template of the imported
stylesheet.
Another advantage to using <xsl:import> is that it must appear as the first
child under the <xsl:stylesheet>, and therefore it honors any existing global
parameters in the stylesheets you're loading, and any namespace declarations
in the <xsl:stylesheet> that's imported are respected.
In conclusion, you can't expect every XSLT processor to directly support a
StartMode, but it is still something that can be simulated and overcome
through the skillful application of XSLT.
Derek Harmon