Package er.extensions.components

Source Code of er.extensions.components.ERXSLTWrapper

package er.extensions.components;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.XMLReaderFactory;

import com.webobjects.appserver.WOApplication;
import com.webobjects.appserver.WOContext;
import com.webobjects.appserver.WOResourceManager;
import com.webobjects.appserver.WOResponse;
import com.webobjects.foundation.NSArray;
import com.webobjects.foundation.NSData;
import com.webobjects.foundation.NSForwardException;

import er.extensions.appserver.ERXResponse;
import er.extensions.foundation.ERXFileUtilities;
import er.extensions.foundation.ERXSimpleTemplateParser;

/**
* Wrapper that translates its content via XSLT. The content must be valid XML for this to work.
* This is pretty usefull in conjunction with DynaReporter when you want to use one of the
* zillion PDF libs. You can generate the content via DynaReporter and then transform the content
* to a form that the PDF lib understands. Most likely this will be much easier than trying to re-generate
* the report with XML. <br />
* Other uses include a simple transformation of the generated front end code to privide for "skinning".
* As there is only so much you can do with CSS, you might need to structurally change the generated HTML prior
* to handing it to the client.<br />
* Note that XSLT engines vary <emp>greatly</emp> in speed. The default case of using Xalan which is included by WO
* is probably not the best choice for a site with a little bit of traffic.
* Therefore there is an option where you can set the transformer factory name to use, you also need to include the
* corresponding jar into the classpath.
*
* @binding enabled flag that decides if the transformation is applied. If not set, then only the content will be shown.
* @binding stylesheet name of the XLST stylesheet (mandatory)
* @binding transformerFactory name of the class for the XSLT transformer factory (optional, defaults to Xalan)
* @binding framework name of the XLST stylesheet's framework (optional)
* @binding data will be set to the transformed data (optional)
* @binding stream will be set to the transformed data (optional)
* @binding nocache flag that if set creates a new transformer instead of using the one in the cache. Useful when deleloping the stylesheet.
* @author ak on 07.04.05
*/
public class ERXSLTWrapper extends ERXNonSynchronizingComponent {
  /**
   * Do I need to update serialVersionUID?
   * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the
   * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a>
   */
  private static final long serialVersionUID = 1L;

  /** logging support */
  private static final Logger log = Logger.getLogger(ERXSLTWrapper.class);

  private long start, current;
  /**
   * Public constructor
   * @param context the context
   */
  public ERXSLTWrapper(WOContext context) {
    super(context);
  }

  private boolean isEnabled() {
    return booleanValueForBinding("enabled", true);
  }

  private static Map cache = new HashMap();

  private Transformer transformer() {
    Transformer transformer;
    try {
      synchronized (cache) {
        String stylesheet = (String)valueForBinding("stylesheet");
        String framework = (String)valueForBinding("framework");
        NSArray languages = session().languages();
        String key = stylesheet + "-" + framework;
        transformer = (Transformer) cache.get(key);
        if(transformer == null || booleanValueForBinding("nocache")) {
          byte bytes[] = application().resourceManager().bytesForResourceNamed(stylesheet, framework, languages);
          DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
          documentBuilderFactory.setValidating(false);
          documentBuilderFactory.setNamespaceAware(true);
          DocumentBuilder documentBuilder;
          documentBuilder = documentBuilderFactory.newDocumentBuilder();
          ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
          Document document = documentBuilder.parse(bis);
          Source xslt = new DOMSource(document);          
          xslt.setSystemId(key);
          String transformerFactoryName = (String)valueForBinding("transformerFactory");
          String oldTransformerFactoryName = System.getProperty("javax.xml.transform.TransformerFactory");
          if(transformerFactoryName != null) {
            System.setProperty("javax.xml.transform.TransformerFactory", transformerFactoryName);
          } else {
            System.setProperty("javax.xml.transform.TransformerFactory", "org.apache.xalan.processor.TransformerFactoryImpl");
          }
          TransformerFactory transformerFactory = TransformerFactory.newInstance();
          if(oldTransformerFactoryName != null) {
            System.setProperty("javax.xml.transform.TransformerFactory", oldTransformerFactoryName);
          }
          transformer = transformerFactory.newTransformer(xslt);
          // transformer.setOutputProperty("indent", "no");
          // transformer.setOutputProperty("method", "xml");

          cache.put(key, transformer);
        }
      }
      return transformer;
    } catch(Exception ex) {
      throw NSForwardException._runtimeExceptionForThrowable(ex);
    }
  }

  private static XMLReader xmlReader;

  static {
    try {
      xmlReader = XMLReaderFactory.createXMLReader();
      xmlReader.setFeature("http://xml.org/sax/features/validation", false);
      if(false) {
        // FIXME AK: we need  real handling for the normal case (HTML->FOP XML)
        EntityResolver resolver = new EntityResolver() {
          public InputSource resolveEntity(String arg0, String arg1) throws SAXException, IOException {
            log.info(arg0 + "::" + arg1);
            InputSource source = new InputSource((new URL("file:///Volumes/Home/Desktop/dtd/xhtml1-transitional.dtd")).openStream());
            source.setSystemId(arg1);
            return source;
          }
        };
        xmlReader.setEntityResolver(resolver);
      }
    } catch (SAXException e) {
      e.printStackTrace();
    }
  }

  /**
   * Overridden to get use apply the XLST transformation on the content.
   * @throws TransformerException
   */
  @Override
  public void appendToResponse(WOResponse response, WOContext context) {
    start = System.currentTimeMillis(); current = start;
    if (isEnabled()) {
      ERXResponse newResponse = new ERXResponse();
      newResponse.setContentEncoding(response.contentEncoding());

      super.appendToResponse(newResponse, context);

      if (log.isDebugEnabled()) {
        String contentString = newResponse.contentString();
        log.debug("Converting content string:\n" + contentString);
      }

      try {
        NSData data = transform(transformer(), newResponse.content());
        if(hasBinding("data") && canSetValueForBinding("data")) {
          setValueForBinding(data, "data");
        }
        if(hasBinding("stream") && canSetValueForBinding("stream")) {
          setValueForBinding(data.stream(), "stream");
        }
        response.appendContentData(data);
      } catch (TransformerException e) {
        throw NSForwardException._runtimeExceptionForThrowable(e);
      }
    } else {
      super.appendToResponse(response, context);
    }
    log.debug("Total: " + (System.currentTimeMillis() - start));  start = System.currentTimeMillis();
  }

  private static TemplatePool pool = new TemplatePool();

  public static Transformer getTransformer(String framework, String filename) {
    return pool.getTransformer(framework, filename);
  }

  public static String transform(Transformer transformer, String xml) throws TransformerException {
    StringReader stringreader = new StringReader(xml);
    InputSource inputsource = new InputSource(stringreader);
    SAXSource s = new SAXSource(inputsource);
   
    StringWriter writer = new StringWriter();
    StreamResult r = new StreamResult(writer);

    transformer.transform(s, r);
    String result = writer.toString();
    return result;
  }

  public static NSData transform(Transformer transformer, NSData data) throws TransformerException {
    ByteArrayInputStream bis = new ByteArrayInputStream(data.bytes());
    SAXSource saxSource = new SAXSource();
    saxSource.setXMLReader(xmlReader);
    saxSource.setInputSource(new InputSource(bis));
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    Result r = new StreamResult(os);
    transformer.transform(saxSource, r);
    NSData result = new NSData(os.toByteArray());
    return result;
  }

  public static class TemplatePool {

    //private static final WeakHashMap templates = new WeakHashMap();
    private final Map   templates = new HashMap();
    private static final Logger log       = Logger.getLogger(TemplatePool.class);
    private ERXSimpleTemplateParser templateParser = new ERXSimpleTemplateParser("?", false);

    protected TemplatePool() {}

    public Map getTemplates() {
      return templates;
    }

    public synchronized Transformer getTransformer(String framework, String filename) {
      if (filename == null || filename.length() == 0) {
        throw new IllegalArgumentException("filename cannot be null or empty");
      }
      String key = framework + "-"  +filename;

      Templates t = (Templates) pool.getTemplates().get(key);
      String s = null;

      if (t == null) {
        try {
          WOApplication app = WOApplication.application();
          WOResourceManager rm = app.resourceManager();

          TransformerFactory fac = TransformerFactory.newInstance();

          log.debug("creating template for file " + filename + " in framework " + framework);
          InputStream is = rm.inputStreamForResourceNamed(filename, framework, null);
          if (is == null) {
            log.debug("trying with framework = null");
            is = rm.inputStreamForResourceNamed(filename, null, null);
            if (is == null) {
              throw new IllegalArgumentException("inputStream is null");
            }
          }
          if (is.available() == 0) {
            throw new IllegalArgumentException("InputStream has 0 bytes available, cannot read xsl file!");
          }
          s = ERXFileUtilities.stringFromInputStream(is);
          s = templateParser.parseTemplateWithObject(s, "@@", app);
          t = fac.newTemplates(new StreamSource(new ByteArrayInputStream(s.getBytes())));

          if (app.isCachingEnabled()) {
            templates.put(key, t);
          }
        } catch (IOException e1) {
          throw NSForwardException._runtimeExceptionForThrowable(e1);
        } catch (TransformerConfigurationException tce) {
          log.error("could not create template " + tce.getLocationAsString(), tce);
          log.error("  cause", tce.getCause());
          if (tce.getCause() != null && tce.getCause() instanceof org.xml.sax.SAXParseException) {
            org.xml.sax.SAXParseException e = (org.xml.sax.SAXParseException) tce.getCause();
            log.error("SAXParseException: line " + e.getLineNumber() + ", column " + e.getColumnNumber());
          }
          log.error("this is the incorrect xsl:>>>" + s + "<<<");
          return null;
        }
      }

      try {
        return t.newTransformer();
      } catch (TransformerConfigurationException tce) {
        log.error("could not create template " + tce.getLocationAsString(), tce);
        log.error("  cause", tce.getCause());
        return null;
      }
    }
  }
}
TOP

Related Classes of er.extensions.components.ERXSLTWrapper

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.