You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@xalan.apache.org by Jon Smirl <jo...@mediaone.net> on 2000/07/31 21:03:41 UTC

Xalan-C: Custom output stages

I'm working on building custom output stages under Xalan-C. Based on some
experiments, here's what I think I need.

1) Make the output stream (not DOM) creation code in
StylesheetRoot::process() into a separate method. I need this because I
initially start the processor off outputting to a DOM tree. I then scan the
out going PIs looking for a PI saying - toMail, toChart, toXSLAgain, etc. If
I find one of these I continue to let it build a tree. But if I don't find
one of these the output needs to switch to an output stream.

When I see the document element without seeing one of my special PI's I want
to call this new build stream method. I need a build stream method on the
processor object because it needs to know the XSL output method specified in
the stylesheet. When I build the stream the processor should switch to using
the stream instead of the DOM tree. I also need access to the Formatter
object since I need to send any PIs and the document element I captured in
the DOM tree onto the output stream.

XT used a different scheme and allowed a Formatter object specification in
xsl:output. That works for me too but it has more impact on Xalan.

2)  XercesTextOutputStream needs a virtual method on it called
setContentType. When a new XML, HTML, text Formatter is created it needs to
call the setContentType method on the output stream with the appropriate
mime type string. This will give me a chance to send the right mime type to
the browser. My browser output stream is derived from
XercesTextOutputStream.

These are minor changes to make and shouldn't impact any existing apps.

Jon Smirl
jonsmirl@mediaone.net


Re: Xalan-C: Custom output stages

Posted by Jon Smirl <jo...@mediaone.net>.
I now have Xalan-C working in Apache with custom output stages. In this
particular case I am chaining two stylesheets, the first sheet in the chain
is always run locally at the server. It contains this code...

  <xsl:choose>
   <xsl:when test="$print='pdf'">
    <xsl:processing-instruction name="stylesheet">
     href="qbPrint/standardPurchOrd.xsl" type="text/xsl"
    </xsl:processing-instruction>
   </xsl:when>
   <xsl:when test="$xslt=true()">
    <xsl:processing-instruction name="xml-stylesheet">
     href="/qbScreen/standardTransaction.xsl" type="text/xsl"
    </xsl:processing-instruction>
   </xsl:when>
   <xsl:otherwise>
    <xsl:processing-instruction name="stylesheet">
     href="qbScreen/standardTransaction.xsl" type="text/xsl"
    </xsl:processing-instruction>
   </xsl:otherwise>
  </xsl:choose>

Using a PI to pick the custom output stage is better that using <xsl:output
method="custom class">. I can compute the output method in my implementation
and I can't in the xsl:output case.

The browser contains code which sets a variable xslt=true() if it is IE5
with XSLT installed. It this variable is set I emit the xml-stylesheet PI.
The xml-stylesheet PI is not one of my internal PIs so I just stream the XML
output to the browser and perform the transform there. This is why I needed
access to the output method variable, in order to set the mime type into the
output stream. This is also where I use the divert stream function I just
added.

In the default case the stylesheet PI is a special PI for my system which
will cause the stylesheet transform to be run locally at the server. If
xslt() is not true I transform the XML to HTML at the server before
streaming the result to the browser. The sheet uses the user-agent header
field as an input variable to generate specific HTML for various target
browsers.

The final case is a stylesheet that uses a server local transform to convert
the XML into XSL:fo.  In my Java app I fed this output into FOP. But now I
don't have a C version of FOP.

My C++ version of this system is visibly faster than an identical
implementation using Java running on the same hardware. My biggest current
bottleneck is local XSLT transform speed.

Jon Smirl
jonsmirl@mediaone.net


Re: Xalan-C: Custom output stages

Posted by Jon Smirl <jo...@mediaone.net>.
Here are the diffs needed to make Xalan-C able to chain output stage and to
support mime types. I handle the mime type issue by making the output method
variable visible and handling it in my own code.

The divert stream function lets you start off building a DOM tree, look at
the PI's, and then divert to a stream if that's what the PI's indicate.

This is a minor change to Xalan which does not impact existing apps.

cvs diff StylesheetRoot.hpp (in directory d:\cvs\xml-xalan\c\src\XSLT\)
Index: StylesheetRoot.hpp
===================================================================
RCS file: /home/cvspublic/xml-xalan/c/src/XSLT/StylesheetRoot.hpp,v
retrieving revision 1.10
diff -r1.10 StylesheetRoot.hpp
118a119,127
>
>  /**
>   * Divert an inprocess transformation from DOM tree to stream
>   */
>  FormatterListener*
>  divertStream(
>    XSLTResultTarget&    outputTarget,
>    StylesheetExecutionContext&  executionContext
>  ) const;
368a378,383
>  }
>
>  FormatterListener::eFormat
>  getOutputMethod()
>  {
>   return m_outputMethod;

I didn't change the destructor below, the diffs just picked it up anyway.

cvs diff StylesheetRoot.cpp (in directory d:\cvs\xml-xalan\c\src\XSLT\)
Index: StylesheetRoot.cpp
===================================================================
RCS file: /home/cvspublic/xml-xalan/c/src/XSLT/StylesheetRoot.cpp,v
retrieving revision 1.19
diff -r1.19 StylesheetRoot.cpp
170,188d169
<
< StylesheetRoot::~StylesheetRoot()
< {
< #if !defined(XALAN_NO_NAMESPACES)
<  using std::for_each;
< #endif
<
<  // Clean up all entries in the vector.
<  for_each(m_importStack.begin(),
<     m_importStack.end(),
<     DeleteFunctor<XMLURL>());
<
<  delete m_defaultRule;
<  delete m_defaultTextRule;
<  delete m_defaultRootRule;
< }
<
<
<
213,214d193
<  Writer* pw = 0;
<
237,313c216
<   if(0 != outputTarget.getCharacterStream())
<   {
<    pw = outputTarget.getCharacterStream();
<   }
<   else
<   {
< /*
<     java:
<     XalanDOMString mimeEncoding;
<     XalanDOMString encoding;
<     mimeEncoding = getOutputEncoding();
<     encoding = getJavaOutputEncoding();
<     if(0 == encoding)
<     {
<      m_processor->m_diagnosticsPrintWriter.println("Encoding not
supported: "+mimeEncoding);
<      mimeEncoding = "UTF-8";
<      encoding = FormatterToXML.convertMime2JavaEncoding(mimeEncoding);
<     }
< */
<
<    if(0 != outputTarget.getByteStream())
<    {
<     pw =
executionContext.createPrintWriter(*outputTarget.getByteStream());
<    }
<    else if(!isEmpty(outputTarget.getFileName()))
<    {
<     pw = executionContext.createPrintWriter(
<        outputTarget.getFileName(),
<        XalanDOMString());
<    }
<    else
<    {
< #if !defined(XALAN_NO_NAMESPACES)
<     using std::cout;
< #endif
<
<     pw = executionContext.createPrintWriter(cout);
<    }
<   }
<
<   int   indentAmount = executionContext.getIndent();
<   const bool doIndent = (indentAmount > -1) ? true : m_indentResult;
<
<   switch(m_outputMethod)
<   {
<   case FormatterListener::OUTPUT_METHOD_HTML:
<    if (doIndent == true && indentAmount < 0)
<    {
<     indentAmount = FormatterToHTML::eDefaultIndentAmount;
<    }
<
<    flistener = executionContext.createFormatterToHTML(
<       *pw, m_encoding, m_mediatype, m_doctypeSystem, m_doctypePublic,
<       doIndent, indentAmount, m_version, m_standalone, !m_omitxmlDecl);
<    break;
<
<   case FormatterListener::OUTPUT_METHOD_TEXT:
<    flistener = executionContext.createFormatterToText(*pw);
<    break;
<
<   case FormatterListener::OUTPUT_METHOD_NONE:
<   case FormatterListener::OUTPUT_METHOD_XML:
<   default:
<    // Make sure we don't have a negative indent amount if we're
<    // indenting
<    if (doIndent == true && indentAmount < 0)
<    {
<     indentAmount = FormatterToXML::eDefaultIndentAmount;
<    }
<
<    flistener = executionContext.createFormatterToXML(
<       *pw, m_version, doIndent, indentAmount, m_encoding, m_mediatype,
<       m_doctypeSystem, m_doctypePublic, !m_omitxmlDecl, m_standalone);
<    break;
<   }
<
<   executionContext.setFormatterListener(flistener);
---
>   flistener = divertStream(outputTarget, executionContext);
400a304,411
>
> /**
>  * Divert an inprocess transformation from DOM tree to stream
>  */
> FormatterListener*
> StylesheetRoot::divertStream(
>    XSLTResultTarget&  outputTarget,
>    StylesheetExecutionContext&  executionContext) const
> {
>  FormatterListener* flistener = 0;
>  Writer* pw = 0;
>
>  if(0 != outputTarget.getCharacterStream())
>  {
>   pw = outputTarget.getCharacterStream();
>  }
>  else
>  {
> /*
>    java:
>    XalanDOMString mimeEncoding;
>    XalanDOMString encoding;
>    mimeEncoding = getOutputEncoding();
>    encoding = getJavaOutputEncoding();
>    if(0 == encoding)
>    {
>     m_processor->m_diagnosticsPrintWriter.println("Encoding not supported:
"+mimeEncoding);
>     mimeEncoding = "UTF-8";
>     encoding = FormatterToXML.convertMime2JavaEncoding(mimeEncoding);
>    }
> */
>
>   if(0 != outputTarget.getByteStream())
>   {
>    pw = executionContext.createPrintWriter(*outputTarget.getByteStream());
>   }
>   else if(!isEmpty(outputTarget.getFileName()))
>   {
>    pw = executionContext.createPrintWriter(
>       outputTarget.getFileName(),
>       XalanDOMString());
>   }
>   else
>   {
> #if !defined(XALAN_NO_NAMESPACES)
>    using std::cout;
> #endif
>
>    pw = executionContext.createPrintWriter(cout);
>   }
>  }
>
>  int   indentAmount = executionContext.getIndent();
>  const bool doIndent = (indentAmount > -1) ? true : m_indentResult;
>
>  switch(m_outputMethod)
>  {
>  case FormatterListener::OUTPUT_METHOD_HTML:
>   if (doIndent == true && indentAmount < 0)
>   {
>    indentAmount = FormatterToHTML::eDefaultIndentAmount;
>   }
>
>   flistener = executionContext.createFormatterToHTML(
>      *pw, m_encoding, m_mediatype, m_doctypeSystem, m_doctypePublic,
>      doIndent, indentAmount, m_version, m_standalone, !m_omitxmlDecl);
>   break;
>
>  case FormatterListener::OUTPUT_METHOD_TEXT:
>   flistener = executionContext.createFormatterToText(*pw);
>   break;
>
>  case FormatterListener::OUTPUT_METHOD_NONE:
>  case FormatterListener::OUTPUT_METHOD_XML:
>  default:
>   // Make sure we don't have a negative indent amount if we're
>   // indenting
>   if (doIndent == true && indentAmount < 0)
>   {
>    indentAmount = FormatterToXML::eDefaultIndentAmount;
>   }
>
>   flistener = executionContext.createFormatterToXML(
>      *pw, m_version, doIndent, indentAmount, m_encoding, m_mediatype,
>      m_doctypeSystem, m_doctypePublic, !m_omitxmlDecl, m_standalone);
>   break;
>  }
>
>  executionContext.setFormatterListener(flistener);
>  return flistener;
> }
>
>
> StylesheetRoot::~StylesheetRoot()
> {
> #if !defined(XALAN_NO_NAMESPACES)
>  using std::for_each;
> #endif
>
>  // Clean up all entries in the vector.
>  for_each(m_importStack.begin(),
>     m_importStack.end(),
>     DeleteFunctor<XMLURL>());
>
>  delete m_defaultRule;
>  delete m_defaultTextRule;
>  delete m_defaultRootRule;
> }

Jon Smirl
jonsmirl@mediaone.net