Package org.apache.xalan.lib

Source Code of org.apache.xalan.lib.Redirect

/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* $Id: Redirect.java,v 1.23 2004/08/13 21:08:27 minchau Exp $
*/
package org.apache.xalan.lib;

import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Hashtable;

import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import javax.xml.transform.stream.StreamResult;

import org.apache.xalan.extensions.XSLProcessorContext;
import org.apache.xalan.res.XSLTErrorResources;
import org.apache.xalan.templates.ElemExtensionCall;
import org.apache.xalan.templates.OutputProperties;
import org.apache.xalan.transformer.TransformerImpl;
import org.apache.xpath.XPath;
import org.apache.xpath.objects.XObject;
import org.apache.xml.serializer.SerializationHandler;
import org.xml.sax.ContentHandler;

/**
* Implements three extension elements to allow an XSLT transformation to
* redirect its output to multiple output files.
*
* It is accessed by specifying a namespace URI as follows:
* <pre>
*    xmlns:redirect="http://xml.apache.org/xalan/redirect"
* </pre>
*
* <p>You can either just use redirect:write, in which case the file will be
* opened and immediately closed after the write, or you can bracket the
* write calls by redirect:open and redirect:close, in which case the
* file will be kept open for multiple writes until the close call is
* encountered.  Calls can be nested. 
*
* <p>Calls can take a 'file' attribute
* and/or a 'select' attribute in order to get the filename.  If a select
* attribute is encountered, it will evaluate that expression for a string
* that indicates the filename.  If the string evaluates to empty, it will
* attempt to use the 'file' attribute as a default.  Filenames can be relative
* or absolute.  If they are relative, the base directory will be the same as
* the base directory for the output document.  This is obtained by calling
* getOutputTarget() on the TransformerImpl.  You can set this base directory
* by calling TransformerImpl.setOutputTarget() or it is automatically set
* when using the two argument form of transform() or transformNode().
*
* <p>Calls to redirect:write and redirect:open also take an optional
* attribute append="true|yes", which will attempt to simply append
* to an existing file instead of always opening a new file.  The
* default behavior of always overwriting the file still happens
* if you do not specify append.
* <p><b>Note:</b> this may give unexpected results when using xml
* or html output methods, since this is <b>not</b> coordinated
* with the serializers - hence, you may get extra xml decls in
* the middle of your file after appending to it.
*
* <p>Example:</p>
* <PRE>
* &lt;?xml version="1.0"?>
* &lt;xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
*                 version="1.0"
*                 xmlns:redirect="http://xml.apache.org/xalan/redirect"
*                 extension-element-prefixes="redirect">
*
*   &lt;xsl:template match="/">
*     &lt;out>
*       default output.
*     &lt;/out>
*     &lt;redirect:open file="doc3.out"/>
*     &lt;redirect:write file="doc3.out">
*       &lt;out>
*         &lt;redirect:write file="doc1.out">
*           &lt;out>
*             doc1 output.
*             &lt;redirect:write file="doc3.out">
*               Some text to doc3
*             &lt;/redirect:write>
*           &lt;/out>
*         &lt;/redirect:write>
*         &lt;redirect:write file="doc2.out">
*           &lt;out>
*             doc2 output.
*             &lt;redirect:write file="doc3.out">
*               Some more text to doc3
*               &lt;redirect:write select="doc/foo">
*                 text for doc4
*               &lt;/redirect:write>
*             &lt;/redirect:write>
*           &lt;/out>
*         &lt;/redirect:write>
*       &lt;/out>
*     &lt;/redirect:write>
*     &lt;redirect:close file="doc3.out"/>
*   &lt;/xsl:template>
*
* &lt;/xsl:stylesheet>
* </PRE>
*
* @author Scott Boag
* @version 1.0
* @see <a href="../../../../../../extensions.html#ex-redirect" target="_top">Example with Redirect extension</a>
*/
public class Redirect
{
  /**
   * List of formatter listeners indexed by filename.
   */
  protected Hashtable m_formatterListeners = new Hashtable ();

  /**
   * List of output streams indexed by filename.
   */
  protected Hashtable m_outputStreams = new Hashtable ();

  /**
   * Default append mode for bare open calls. 
   * False for backwards compatibility (I think).
   */
  public static final boolean DEFAULT_APPEND_OPEN = false;

  /**
   * Default append mode for bare write calls. 
   * False for backwards compatibility.
   */
  public static final boolean DEFAULT_APPEND_WRITE = false;

  /**
   * Open the given file and put it in the XML, HTML, or Text formatter listener's table.
   */
  public void open(XSLProcessorContext context, ElemExtensionCall elem)
    throws java.net.MalformedURLException,
           java.io.FileNotFoundException,
           java.io.IOException,
           javax.xml.transform.TransformerException
  {
    String fileName = getFilename(context, elem);
    Object flistener = m_formatterListeners.get(fileName);
    if(null == flistener)
    {
      String mkdirsExpr
        = elem.getAttribute ("mkdirs", context.getContextNode(),
                                                  context.getTransformer());
      boolean mkdirs = (mkdirsExpr != null)
                       ? (mkdirsExpr.equals("true") || mkdirsExpr.equals("yes")) : true;

      // Whether to append to existing files or not, <jpvdm@iafrica.com>
      String appendExpr = elem.getAttribute("append", context.getContextNode(), context.getTransformer());
    boolean append = (appendExpr != null)
                       ? (appendExpr.equals("true") || appendExpr.equals("yes")) : DEFAULT_APPEND_OPEN;

      Object ignored = makeFormatterListener(context, elem, fileName, true, mkdirs, append);
    }
  }
 
  /**
   * Write the evalutation of the element children to the given file. Then close the file
   * unless it was opened with the open extension element and is in the formatter listener's table.
   */
  public void write(XSLProcessorContext context, ElemExtensionCall elem)
    throws java.net.MalformedURLException,
           java.io.FileNotFoundException,
           java.io.IOException,
           javax.xml.transform.TransformerException
  {
    String fileName = getFilename(context, elem);
    Object flObject = m_formatterListeners.get(fileName);
    ContentHandler formatter;
    boolean inTable = false;
    if(null == flObject)
    {
      String mkdirsExpr
        = ((ElemExtensionCall)elem).getAttribute ("mkdirs",
                                                  context.getContextNode(),
                                                  context.getTransformer());
      boolean mkdirs = (mkdirsExpr != null)
                       ? (mkdirsExpr.equals("true") || mkdirsExpr.equals("yes")) : true;

      // Whether to append to existing files or not, <jpvdm@iafrica.com>
      String appendExpr = elem.getAttribute("append", context.getContextNode(), context.getTransformer());
    boolean append = (appendExpr != null)
                       ? (appendExpr.equals("true") || appendExpr.equals("yes")) : DEFAULT_APPEND_WRITE;

      formatter = makeFormatterListener(context, elem, fileName, true, mkdirs, append);
    }
    else
    {
      inTable = true;
      formatter = (ContentHandler)flObject;
    }
   
    TransformerImpl transf = context.getTransformer();
   
    startRedirection(transf, formatter)// for tracing only
   
    transf.executeChildTemplates(elem,
                                 context.getContextNode(),
                                 context.getMode(), formatter);
                                
    endRedirection(transf); // for tracing only
   
    if(!inTable)
    {
      OutputStream ostream = (OutputStream)m_outputStreams.get(fileName);
      if(null != ostream)
      {
        try
        {
          formatter.endDocument();
        }
        catch(org.xml.sax.SAXException se)
        {
          throw new TransformerException(se);
        }
        ostream.close();
        m_outputStreams.remove(fileName);
        m_formatterListeners.remove(fileName);
      }
    }
  }


  /**
   * Close the given file and remove it from the formatter listener's table.
   */
  public void close(XSLProcessorContext context, ElemExtensionCall elem)
    throws java.net.MalformedURLException,
    java.io.FileNotFoundException,
    java.io.IOException,
    javax.xml.transform.TransformerException
  {
    String fileName = getFilename(context, elem);
    Object formatterObj = m_formatterListeners.get(fileName);
    if(null != formatterObj)
    {
      ContentHandler fl = (ContentHandler)formatterObj;
      try
      {
        fl.endDocument();
      }
      catch(org.xml.sax.SAXException se)
      {
        throw new TransformerException(se);
      }
      OutputStream ostream = (OutputStream)m_outputStreams.get(fileName);
      if(null != ostream)
      {
        ostream.close();
        m_outputStreams.remove(fileName);
      }
      m_formatterListeners.remove(fileName);
    }
  }

  /**
   * Get the filename from the 'select' or the 'file' attribute.
   */
  private String getFilename(XSLProcessorContext context, ElemExtensionCall elem)
    throws java.net.MalformedURLException,
    java.io.FileNotFoundException,
    java.io.IOException,
    javax.xml.transform.TransformerException
  {
    String fileName;
    String fileNameExpr
      = ((ElemExtensionCall)elem).getAttribute ("select",
                                                context.getContextNode(),
                                                context.getTransformer());
    if(null != fileNameExpr)
    {
      org.apache.xpath.XPathContext xctxt
        = context.getTransformer().getXPathContext();
      XPath myxpath = new XPath(fileNameExpr, elem, xctxt.getNamespaceContext(), XPath.SELECT);
      XObject xobj = myxpath.execute(xctxt, context.getContextNode(), elem);
      fileName = xobj.str();
      if((null == fileName) || (fileName.length() == 0))
      {
        fileName = elem.getAttribute ("file",
                                      context.getContextNode(),
                                      context.getTransformer());
      }
    }
    else
    {
      fileName = elem.getAttribute ("file", context.getContextNode(),
                                                               context.getTransformer());
    }
    if(null == fileName)
    {
      context.getTransformer().getMsgMgr().error(elem, elem,
                                     context.getContextNode(),
                                     XSLTErrorResources.ER_REDIRECT_COULDNT_GET_FILENAME);
                              //"Redirect extension: Could not get filename - file or select attribute must return vald string.");
    }
    return fileName;
  }
 
  // yuck.
  // Note: this is not the best way to do this, and may not even
  //    be fully correct! Patches (with test cases) welcomed. -sc
  private String urlToFileName(String base)
  {
    if(null != base)
    {
      if(base.startsWith("file:////"))
      {
        base = base.substring(7);
      }
      else if(base.startsWith("file:///"))
      {
        base = base.substring(6);
      }
      else if(base.startsWith("file://"))
      {
        base = base.substring(5); // absolute?
      }
      else if(base.startsWith("file:/"))
      {
        base = base.substring(5);
      }
      else if(base.startsWith("file:"))
      {
        base = base.substring(4);
      }
    }
    return base;
  }

  /**
   * Create a new ContentHandler, based on attributes of the current ContentHandler.
   */
  private ContentHandler makeFormatterListener(XSLProcessorContext context,
                                               ElemExtensionCall elem,
                                               String fileName,
                                               boolean shouldPutInTable,
                                               boolean mkdirs,
                                               boolean append)
    throws java.net.MalformedURLException,
    java.io.FileNotFoundException,
    java.io.IOException,
    javax.xml.transform.TransformerException
  {
    File file = new File(fileName);
    TransformerImpl transformer = context.getTransformer();
    String base;          // Base URI to use for relative paths

    if(!file.isAbsolute())
    {
      // This code is attributed to Jon Grov <jon@linpro.no>.  A relative file name
      // is relative to the Result used to kick off the transform.  If no such
      // Result was supplied, the filename is relative to the source document.
      // When transforming with a SAXResult or DOMResult, call
      // TransformerImpl.setOutputTarget() to set the desired Result base.
  //      String base = urlToFileName(elem.getStylesheet().getSystemId());

      Result outputTarget = transformer.getOutputTarget();
      if ( (null != outputTarget) && ((base = outputTarget.getSystemId()) != null) ) {
        base = urlToFileName(base);
      }
      else
      {
        base = urlToFileName(transformer.getBaseURLOfSource());
      }

      if(null != base)
      {
        File baseFile = new File(base);
        file = new File(baseFile.getParent(), fileName);
      }
      // System.out.println("file is: "+file.toString());
    }

    if(mkdirs)
    {
      String dirStr = file.getParent();
      if((null != dirStr) && (dirStr.length() > 0))
      {
        File dir = new File(dirStr);
        dir.mkdirs();
      }
    }

    // This should be worked on so that the output format can be
    // defined by a first child of the redirect element.
    OutputProperties format = transformer.getOutputFormat();

    // FileOutputStream ostream = new FileOutputStream(file);
    // Patch from above line to below by <jpvdm@iafrica.com>
    //  Note that in JDK 1.2.2 at least, FileOutputStream(File)
    //  is implemented as a call to
    //  FileOutputStream(File.getPath, append), thus this should be
    //  the equivalent instead of getAbsolutePath()
    FileOutputStream ostream = new FileOutputStream(file.getPath(), append);
   
    try
    {
      SerializationHandler flistener =
        createSerializationHandler(transformer, ostream, file, format);
       
      try
      {
        flistener.startDocument();
      }
      catch(org.xml.sax.SAXException se)
      {
        throw new TransformerException(se);
      }
      if(shouldPutInTable)
      {
        m_outputStreams.put(fileName, ostream);
        m_formatterListeners.put(fileName, flistener);
      }
      return flistener;
    }
    catch(TransformerException te)
    {
      throw new javax.xml.transform.TransformerException(te);
    }
   
  }

  /**
   * A class that extends this class can over-ride this public method and recieve
   * a callback that redirection is about to start
   * @param transf The transformer.
   * @param formatter The handler that receives the redirected output
   */
  public void startRedirection(TransformerImpl transf, ContentHandler formatter)
  {
      // A class that extends this class could provide a method body       
  }
   
  /**
   * A class that extends this class can over-ride this public method and receive
   * a callback that redirection to the ContentHandler specified in the startRedirection()
   * call has ended
   * @param transf The transformer.
   */
  public void endRedirection(TransformerImpl transf)
  {
      // A class that extends this class could provide a method body       
  }
   
  /**
   * A class that extends this one could over-ride this public method and receive
   * a callback for the creation of the serializer used in the redirection.
   * @param transformer The transformer
   * @param ostream The output stream that the serializer wraps
   * @param file The file associated with the ostream
   * @param format The format parameter used to create the serializer
   * @return the serializer that the redirection will go to.
   *
   * @throws java.io.IOException
   * @throws TransformerException
   */
  public SerializationHandler createSerializationHandler(
        TransformerImpl transformer,
        FileOutputStream ostream,
        File file,
        OutputProperties format)
        throws java.io.IOException, TransformerException
  {

      SerializationHandler serializer =
          transformer.createSerializationHandler(
              new StreamResult(ostream),
              format);
      return serializer;
  }
}
TOP

Related Classes of org.apache.xalan.lib.Redirect

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.