0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.xsl;
0031:
0032: import com.caucho.java.JavaCompiler;
0033: import com.caucho.loader.DynamicClassLoader;
0034: import com.caucho.loader.EnvironmentLocal;
0035: import com.caucho.loader.SimpleLoader;
0036: import com.caucho.log.Log;
0037: import com.caucho.server.util.CauchoSystem;
0038: import com.caucho.util.Base64;
0039: import com.caucho.util.CharBuffer;
0040: import com.caucho.util.L10N;
0041: import com.caucho.util.LruCache;
0042: import com.caucho.vfs.Crc64Stream;
0043: import com.caucho.vfs.Path;
0044: import com.caucho.vfs.MergePath;
0045: import com.caucho.vfs.ReadStream;
0046: import com.caucho.vfs.Vfs;
0047: import com.caucho.vfs.WriteStream;
0048: import com.caucho.xml.*;
0049: import com.caucho.xpath.Expr;
0050: import com.caucho.xpath.XPath;
0051:
0052: import org.w3c.dom.Attr;
0053: import org.w3c.dom.Document;
0054: import org.w3c.dom.DocumentType;
0055: import org.w3c.dom.Node;
0056: import org.xml.sax.ContentHandler;
0057: import org.xml.sax.InputSource;
0058: import org.xml.sax.XMLFilter;
0059: import org.xml.sax.XMLReader;
0060:
0061: import javax.xml.transform.ErrorListener;
0062: import javax.xml.transform.Source;
0063: import javax.xml.transform.Templates;
0064: import javax.xml.transform.TransformerConfigurationException;
0065: import javax.xml.transform.TransformerException;
0066: import javax.xml.transform.URIResolver;
0067: import javax.xml.transform.dom.DOMResult;
0068: import javax.xml.transform.dom.DOMSource;
0069: import javax.xml.transform.sax.SAXResult;
0070: import javax.xml.transform.sax.SAXSource;
0071: import javax.xml.transform.sax.SAXTransformerFactory;
0072: import javax.xml.transform.sax.TemplatesHandler;
0073: import javax.xml.transform.sax.TransformerHandler;
0074: import javax.xml.transform.stream.StreamResult;
0075: import javax.xml.transform.stream.StreamSource;
0076: import java.io.IOException;
0077: import java.io.InputStream;
0078: import java.io.OutputStream;
0079: import java.io.Reader;
0080: import java.lang.ref.SoftReference;
0081: import java.util.Iterator;
0082: import java.util.Random;
0083: import java.util.logging.Level;
0084: import java.util.logging.Logger;
0085:
0086: /**
0087: * Abstract factory for creating stylesheets.
0088: */
0089: abstract public class AbstractStylesheetFactory extends
0090: SAXTransformerFactory {
0091: static final Logger log = Log.open(AbstractStylesheetFactory.class);
0092: static final L10N L = new L10N(AbstractStylesheetFactory.class);
0093:
0094: private static EnvironmentLocal<LruCache<String, SoftReference<StylesheetImpl>>> _stylesheetCache = new EnvironmentLocal<LruCache<String, SoftReference<StylesheetImpl>>>();
0095:
0096: private URIResolver _uriResolver;
0097: private ErrorListener _errorListener;
0098:
0099: private String _systemId;
0100:
0101: private Path _workPath;
0102: private Path _stylePath;
0103: private ClassLoader _loader;
0104: private String _className;
0105: private boolean _isAutoCompile = true;
0106: private boolean _loadPrecompiledStylesheet = true;
0107:
0108: protected AbstractStylesheetFactory() {
0109: }
0110:
0111: /**
0112: * Returns an implementation-specific attribute.
0113: *
0114: * @param name the attribute name
0115: */
0116: public Object getAttribute(String name) {
0117: return null;
0118: }
0119:
0120: /**
0121: * Sets an implementation-specific attribute.
0122: *
0123: * @param name the attribute name
0124: * @param value the attribute value
0125: */
0126: public void setAttribute(String name, Object value) {
0127: }
0128:
0129: /**
0130: * Returns an implementation-specific feature.
0131: *
0132: * @param name the feature name
0133: */
0134: public boolean getFeature(String name) {
0135: if (name.equals(SAXTransformerFactory.FEATURE)
0136: || name.equals(SAXTransformerFactory.FEATURE_XMLFILTER)
0137: || name.equals(DOMResult.FEATURE)
0138: || name.equals(DOMSource.FEATURE)
0139: || name.equals(SAXResult.FEATURE)
0140: || name.equals(SAXSource.FEATURE)
0141: || name.equals(StreamResult.FEATURE)
0142: || name.equals(StreamSource.FEATURE))
0143: return true;
0144: else
0145: return false;
0146: }
0147:
0148: /**
0149: * Sets an implementation-specific feature
0150: *
0151: * @param name the feature name
0152: * @param value the feature value
0153: */
0154: public void setFeature(String name, boolean value) {
0155: }
0156:
0157: /**
0158: * Returns the URI to filename resolver.
0159: */
0160: public URIResolver getURIResolver() {
0161: return _uriResolver;
0162: }
0163:
0164: /**
0165: * Sets the URI to filename resolver.
0166: */
0167: public void setURIResolver(URIResolver uriResolver) {
0168: _uriResolver = uriResolver;
0169: }
0170:
0171: /**
0172: * Returns the error listener.
0173: */
0174: public ErrorListener getErrorListener() {
0175: return _errorListener;
0176: }
0177:
0178: /**
0179: * Sets the error listener.
0180: */
0181: public void setErrorListener(ErrorListener errorListener) {
0182: _errorListener = errorListener;
0183: }
0184:
0185: public String getSystemId() {
0186: return _systemId;
0187: }
0188:
0189: public void setSystemId(String systemId) {
0190: _systemId = systemId;
0191: }
0192:
0193: /**
0194: * Sets the search path for stylesheets. Generally applications will use
0195: * MergePath to create their search path.
0196: *
0197: * @param path path containing stylesheets.
0198: */
0199: public void setStylePath(Path path) {
0200: _stylePath = path;
0201: }
0202:
0203: /**
0204: * Returns the stylesheet search path.
0205: */
0206: public Path getStylePath() {
0207: if (_stylePath != null)
0208: return _stylePath;
0209: else
0210: return getSearchPath();
0211: }
0212:
0213: /**
0214: * Sets the search path for stylesheets. Generally applications will use
0215: * MergePath to create their search path.
0216: *
0217: * @param path path containing stylesheets.
0218: */
0219: public void setSearchPath(Path path) {
0220: _stylePath = path;
0221: }
0222:
0223: /**
0224: * Returns the stylesheet search path.
0225: */
0226: public Path getSearchPath() {
0227: if (_stylePath != null)
0228: return _stylePath;
0229: else
0230: return Vfs.getPwd();
0231: }
0232:
0233: /**
0234: * Sets the working directory.
0235: */
0236: public void setWorkPath(Path path) {
0237: _workPath = path;
0238: }
0239:
0240: /**
0241: * Gets the working directory.
0242: */
0243: public Path getWorkPath() {
0244: if (_workPath != null)
0245: return _workPath;
0246: else
0247: return CauchoSystem.getWorkPath();
0248: }
0249:
0250: public void setClassName(String className) {
0251: _className = className;
0252: }
0253:
0254: public String getClassName() {
0255: return _className;
0256: }
0257:
0258: /**
0259: * Sets the classloader for the stylesheet.
0260: *
0261: * @param loader the new loader.
0262: */
0263: public void setClassLoader(ClassLoader loader) {
0264: _loader = loader;
0265: }
0266:
0267: /**
0268: * Gets the classloader for the stylesheet.
0269: */
0270: public ClassLoader getClassLoader() {
0271: return _loader;
0272: }
0273:
0274: /**
0275: * Returns true if precompiled stylesheets should be loaded.
0276: */
0277: public boolean getLoadPrecompiledStylesheet() {
0278: return _loadPrecompiledStylesheet;
0279: }
0280:
0281: /**
0282: * Returns true if precompiled stylesheets should be loaded.
0283: */
0284: public void setLoadPrecompiledStylesheet(boolean preload) {
0285: _loadPrecompiledStylesheet = preload;
0286: }
0287:
0288: /**
0289: * Returns true if the stylesheet should be automatically compiled.
0290: */
0291: public boolean isAutoCompile() {
0292: return _isAutoCompile;
0293: }
0294:
0295: /**
0296: * Returns true if precompiled stylesheets should be loaded.
0297: */
0298: public void setAutoCompile(boolean autoCompile) {
0299: _isAutoCompile = autoCompile;
0300: }
0301:
0302: /**
0303: * Returns the stylesheet source object associated with the given
0304: * XML document.
0305: *
0306: * @param source the XML document which needs a stylesheet.
0307: * @param media the media attribute for the stylesheet
0308: * @param title the title attribute for the stylesheet
0309: * @param charset the character encoding for the stylesheet result.
0310: */
0311: public Source getAssociatedStylesheet(Source source, String media,
0312: String title, String charset)
0313: throws TransformerConfigurationException {
0314: try {
0315: XmlStylesheetReader reader = new XmlStylesheetReader();
0316:
0317: parseSAX(source, reader);
0318:
0319: String href = reader.getAssociatedStylesheet(media, title,
0320: charset);
0321:
0322: if (href == null)
0323: return null;
0324:
0325: String base = source.getSystemId();
0326:
0327: return getSource(href, base);
0328: } catch (Exception e) {
0329: throw new TransformerConfigurationException(e);
0330: }
0331: }
0332:
0333: /**
0334: * Opens a relative path.
0335: */
0336: private Source getSource(String href, String base) throws Exception {
0337: Path subpath;
0338:
0339: if (href == null)
0340: href = "";
0341: if (base == null)
0342: base = "/";
0343:
0344: if (_uriResolver != null) {
0345: if (href.startsWith("/") || base.equals("/"))
0346: subpath = getSearchPath().lookup(href);
0347: else {
0348: subpath = getSearchPath().lookup(base).getParent()
0349: .lookup(href);
0350: }
0351:
0352: Source source = _uriResolver.resolve(href, base);
0353:
0354: if (source != null) {
0355: if (source.getSystemId() == null)
0356: source.setSystemId(subpath.getURL());
0357:
0358: return source;
0359: }
0360: }
0361:
0362: if (href.startsWith("/") || base.equals("/"))
0363: subpath = getSearchPath().lookup(href);
0364: else {
0365: if (base.startsWith("file:"))
0366: base = base.substring(5);
0367:
0368: subpath = getSearchPath().lookup(base).getParent().lookup(
0369: href);
0370: }
0371:
0372: return new StreamSource(subpath.getURL());
0373: }
0374:
0375: private void parseSAX(Source source, ContentHandler handler)
0376: throws TransformerConfigurationException {
0377: try {
0378: if (source instanceof SAXSource) {
0379: SAXSource saxSource = (SAXSource) source;
0380:
0381: XMLReader reader = saxSource.getXMLReader();
0382: InputSource inputSource = saxSource.getInputSource();
0383:
0384: reader.setContentHandler(handler);
0385:
0386: reader.parse(inputSource);
0387: } else if (source instanceof StreamSource) {
0388:
0389: XmlParser parser = new Xml();
0390:
0391: parser.setContentHandler(handler);
0392:
0393: ReadStream rs = openPath(source);
0394: try {
0395: parser.parse(rs);
0396: } finally {
0397: rs.close();
0398: }
0399: } else if (source instanceof DOMSource) {
0400: DOMSource domSource = (DOMSource) source;
0401:
0402: Node node = domSource.getNode();
0403:
0404: XmlUtil.toSAX(node, handler);
0405: }
0406: } catch (Exception e) {
0407: throw new TransformerConfigurationException(e);
0408: }
0409: }
0410:
0411: /**
0412: * Create a transformer from an input stream.
0413: *
0414: * @param source the source stream
0415: *
0416: * @return the compiled stylesheet
0417: */
0418: public javax.xml.transform.Transformer newTransformer(Source source)
0419: throws TransformerConfigurationException {
0420: Templates templates = newTemplates(source);
0421:
0422: return templates.newTransformer();
0423: }
0424:
0425: /**
0426: * Create an identity transformer.
0427: *
0428: * @return the compiled stylesheet
0429: */
0430: public javax.xml.transform.Transformer newTransformer()
0431: throws TransformerConfigurationException {
0432: return new TransformerImpl(new IdentityStylesheet());
0433: }
0434:
0435: /**
0436: * Creates a new stylesheet from an XML document.
0437: */
0438: public StylesheetImpl newStylesheet(Document xsl) throws Exception {
0439: return (StylesheetImpl) generate(xsl, null);
0440: }
0441:
0442: /**
0443: * Create a new stylesheet from a reader.
0444: */
0445: public StylesheetImpl newStylesheet(Reader reader) throws Exception {
0446: ReadStream rs = Vfs.openRead(reader);
0447:
0448: return (StylesheetImpl) generate(parseXSL(rs), rs.getPath());
0449: }
0450:
0451: /**
0452: * Create a new stylesheet from an input stream.
0453: */
0454: public StylesheetImpl newStylesheet(InputStream is)
0455: throws Exception {
0456: ReadStream rs = Vfs.openRead(is);
0457:
0458: return (StylesheetImpl) generate(parseXSL(rs), rs.getPath());
0459: }
0460:
0461: /**
0462: * Loads a stylesheet from a named file
0463: *
0464: * @param systemId the URL of the file
0465: */
0466: public StylesheetImpl newStylesheet(String systemId)
0467: throws Exception {
0468: StylesheetImpl stylesheet = loadPrecompiledStylesheet(systemId,
0469: systemId);
0470:
0471: if (stylesheet != null)
0472: return stylesheet;
0473:
0474: synchronized (AbstractStylesheetFactory.class) {
0475: stylesheet = loadPrecompiledStylesheet(systemId, systemId);
0476:
0477: if (stylesheet != null)
0478: return stylesheet;
0479:
0480: ReadStream is;
0481:
0482: if (_stylePath != null)
0483: is = _stylePath.lookup(systemId).openRead();
0484: else
0485: is = Vfs.lookup(systemId).openRead();
0486:
0487: try {
0488: return newStylesheet(is);
0489: } finally {
0490: if (is != null)
0491: is.close();
0492: }
0493: }
0494: }
0495:
0496: public StylesheetImpl newStylesheet(Path path) throws Exception {
0497: StylesheetImpl stylesheet = loadPrecompiledStylesheet(path
0498: .getFullPath(), path.getUserPath());
0499:
0500: if (stylesheet != null)
0501: return stylesheet;
0502:
0503: synchronized (AbstractStylesheetFactory.class) {
0504: stylesheet = loadPrecompiledStylesheet(path.getFullPath(),
0505: path.getUserPath());
0506:
0507: if (stylesheet != null)
0508: return stylesheet;
0509:
0510: Path oldStylePath = _stylePath;
0511:
0512: if (_stylePath == null)
0513: _stylePath = path.getParent();
0514:
0515: InputStream is = null;
0516:
0517: try {
0518: is = path.openRead();
0519: return newStylesheet(is);
0520: } finally {
0521: _stylePath = oldStylePath;
0522: if (is != null)
0523: is.close();
0524: }
0525: }
0526: }
0527:
0528: /**
0529: * Create a compiled stylesheet from an input stream.
0530: *
0531: * @param source the source stream
0532: *
0533: * @return the compiled stylesheet
0534: */
0535: public Templates newTemplates(Source source)
0536: throws TransformerConfigurationException {
0537: String systemId = source.getSystemId();
0538:
0539: try {
0540: if (systemId != null) {
0541: StylesheetImpl stylesheet = loadPrecompiledStylesheet(
0542: systemId, systemId);
0543:
0544: if (stylesheet != null)
0545: return stylesheet;
0546: }
0547:
0548: if (source instanceof DOMSource) {
0549: Node node = ((DOMSource) source).getNode();
0550:
0551: return generateFromNode(node, systemId);
0552: } else if (source instanceof SAXSource) {
0553: SAXSource saxSource = (SAXSource) source;
0554: XMLReader reader = saxSource.getXMLReader();
0555: InputSource inputSource = saxSource.getInputSource();
0556:
0557: Document doc = new QDocument();
0558: DOMBuilder builder = new DOMBuilder();
0559: builder.init(doc);
0560: reader.setContentHandler(builder);
0561:
0562: reader.parse(inputSource);
0563:
0564: return generateFromNode(doc, systemId);
0565: }
0566:
0567: ReadStream rs = openPath(source);
0568: try {
0569: Path path = rs.getPath();
0570:
0571: Document doc = parseXSL(rs);
0572:
0573: if (systemId != null) {
0574: String mangledName = getMangledName(systemId);
0575: Path genPath = getWorkPath().lookup(mangledName);
0576:
0577: genPath.setUserPath(systemId);
0578:
0579: return generate(doc, genPath);
0580: } else
0581: return generateFromNode(doc, null);
0582: } finally {
0583: if (rs != null)
0584: rs.close();
0585: }
0586: } catch (TransformerConfigurationException e) {
0587: throw e;
0588: } catch (Exception e) {
0589: throw new XslParseException(e);
0590: }
0591: }
0592:
0593: private Templates generateFromNode(Node node, String systemId)
0594: throws IOException, TransformerConfigurationException {
0595: Path tempPath = writeTempFile(node);
0596:
0597: String tempId = tempPath.getTail();
0598:
0599: StylesheetImpl stylesheet = loadPrecompiledStylesheet(tempId,
0600: tempId, false);
0601:
0602: if (systemId != null)
0603: tempPath.setUserPath(systemId);
0604:
0605: if (stylesheet != null)
0606: return stylesheet;
0607:
0608: return generate(node, tempPath);
0609: }
0610:
0611: private Path writeTempFile(Node node) throws IOException {
0612: Path workDir = CauchoSystem.getWorkPath().lookup("_xsl");
0613: workDir.mkdirs();
0614:
0615: // Path temp = workDir.createTempFile("tmp", "xsl");
0616:
0617: WriteStream os = Vfs.lookup("null:").openWrite();
0618: Crc64Stream crcStream = new Crc64Stream(os.getSource());
0619: os.init(crcStream);
0620: try {
0621: XmlPrinter printer = new XmlPrinter(os);
0622:
0623: printer.printNode(node);
0624: } finally {
0625: os.close();
0626: }
0627:
0628: long crc = crcStream.getCRC();
0629: CharBuffer cb = new CharBuffer();
0630: Base64.encode(cb, crc);
0631:
0632: String crcValue = cb.toString().replace('/', '-');
0633:
0634: Path xslPath = workDir.lookup(crcValue + ".xsl");
0635:
0636: // temp.renameTo(xslPath);
0637:
0638: return xslPath;
0639: }
0640:
0641: /**
0642: * Create a new transformer handler.
0643: */
0644: public TransformerHandler newTransformerHandler()
0645: throws TransformerConfigurationException {
0646: return newTransformerHandler(new StylesheetImpl());
0647: }
0648:
0649: /**
0650: * Create a new transformer handler based on a source.
0651: */
0652: public TransformerHandler newTransformerHandler(Source source)
0653: throws TransformerConfigurationException {
0654: return newTransformerHandler(newTemplates(source));
0655: }
0656:
0657: /**
0658: * Create a new transformer handler based on a stylesheet.
0659: */
0660: public TransformerHandler newTransformerHandler(Templates templates)
0661: throws TransformerConfigurationException {
0662: return new TransformerHandlerImpl(templates.newTransformer());
0663: }
0664:
0665: /**
0666: * Returns a templates handler.
0667: *
0668: * @param source the source file
0669: */
0670: public TemplatesHandler newTemplatesHandler()
0671: throws TransformerConfigurationException {
0672: return new TemplatesHandlerImpl(this );
0673: }
0674:
0675: /**
0676: * Returns an XML filter from the transformer.
0677: *
0678: * @param source the source file
0679: */
0680: public XMLFilter newXMLFilter(Source source)
0681: throws TransformerConfigurationException {
0682: Templates templates = newTemplates(source);
0683:
0684: return newXMLFilter(templates);
0685: }
0686:
0687: /**
0688: * Returns an XML filter from the transformer.
0689: *
0690: * @param source the source file
0691: */
0692: public XMLFilter newXMLFilter(Templates templates)
0693: throws TransformerConfigurationException {
0694: return new SAXFilterImpl((TransformerImpl) templates
0695: .newTransformer());
0696: }
0697:
0698: /**
0699: * Parses a stylesheet from the source.
0700: */
0701: protected Node parseStylesheet(Source source)
0702: throws TransformerConfigurationException {
0703: if (source instanceof DOMSource)
0704: return ((DOMSource) source).getNode();
0705: else if (source instanceof StreamSource) {
0706: InputStream is = ((StreamSource) source).getInputStream();
0707: ReadStream rs = null;
0708:
0709: try {
0710: rs = Vfs.openRead(is);
0711: return parseXSL(rs);
0712: } catch (Exception e) {
0713: throw new TransformerConfigurationException(e);
0714: } finally {
0715: if (rs != null)
0716: rs.close();
0717: }
0718: } else
0719: return null;
0720: }
0721:
0722: /**
0723: * Convenience class to create a compiled stylesheet.
0724: *
0725: * @param node DOM source for the stylesheet.
0726: *
0727: * @return a compiled stylesheet
0728: */
0729: public javax.xml.transform.Templates newTemplates(Node node)
0730: throws TransformerConfigurationException {
0731: Document doc = node.getOwnerDocument();
0732: if (node instanceof Document)
0733: doc = (Document) node;
0734:
0735: DocumentType dtd = doc.getDoctype();
0736:
0737: if (dtd != null && dtd.getSystemId() != null)
0738: return generate(node, getSearchPath().lookup(
0739: dtd.getSystemId()));
0740: else if (doc instanceof CauchoDocument) {
0741: String systemId = ((CauchoDocument) doc).getFilename();
0742:
0743: return generate(node, getSearchPath().lookup(systemId));
0744: } else
0745: return generate(node, null);
0746: }
0747:
0748: /**
0749: * Convenience class to create a compiled stylesheet.
0750: *
0751: * @param systemId source path for the stylesheet.
0752: *
0753: * @return a compiled stylesheet
0754: */
0755: public javax.xml.transform.Templates newTemplates(String systemId)
0756: throws TransformerConfigurationException {
0757: StylesheetImpl stylesheet = loadPrecompiledStylesheet(systemId,
0758: systemId);
0759:
0760: if (stylesheet != null)
0761: return stylesheet;
0762:
0763: else if (systemId == null)
0764: return generate(new QDocument(), null);
0765:
0766: Path path = getSearchPath().lookup(systemId);
0767:
0768: try {
0769: ReadStream is = path.openRead();
0770: Document doc;
0771: try {
0772: doc = parseXSL(is);
0773: } finally {
0774: is.close();
0775: }
0776:
0777: return generate(doc, path);
0778: } catch (TransformerConfigurationException e) {
0779: throw e;
0780: } catch (IOException e) {
0781: System.out.println("MP: "
0782: + ((MergePath) getSearchPath()).getMergePaths());
0783: throw new TransformerConfigurationExceptionWrapper(e);
0784: } catch (Exception e) {
0785: throw new TransformerConfigurationExceptionWrapper(e);
0786: }
0787: }
0788:
0789: /**
0790: * Opens a relative path.
0791: */
0792: ReadStream openPath(String href, String base)
0793: throws TransformerException, IOException {
0794: if (_uriResolver != null) {
0795: Source source = _uriResolver.resolve(href, base);
0796:
0797: if (source != null)
0798: return openPath(source);
0799: }
0800:
0801: if (href.startsWith("/") || base.equals("/"))
0802: return getSearchPath().lookup(href).openRead();
0803: else {
0804: Path path = getSearchPath().lookup(base).getParent()
0805: .lookup(href);
0806:
0807: if (path.exists())
0808: return path.openRead();
0809: else
0810: return getSearchPath().lookup(href).openRead();
0811: }
0812: }
0813:
0814: /**
0815: * Opens a path based on a Source.
0816: */
0817: ReadStream openPath(Source source) throws TransformerException,
0818: IOException {
0819: String systemId = source.getSystemId();
0820:
0821: Path path;
0822: if (systemId != null)
0823: path = getSearchPath().lookup(systemId);
0824: else
0825: path = getSearchPath().lookup("anonymous.xsl");
0826:
0827: if (source instanceof StreamSource) {
0828: StreamSource stream = (StreamSource) source;
0829:
0830: InputStream is = stream.getInputStream();
0831:
0832: if (is instanceof ReadStream) {
0833: ReadStream rs = (ReadStream) is;
0834:
0835: rs.setPath(path);
0836:
0837: return rs;
0838: } else if (is != null) {
0839: ReadStream rs = Vfs.openRead(is);
0840: rs.setPath(path);
0841:
0842: return rs;
0843: }
0844:
0845: Reader reader = stream.getReader();
0846:
0847: if (reader != null) {
0848: ReadStream rs = Vfs.openRead(reader);
0849: rs.setPath(path);
0850:
0851: return rs;
0852: }
0853: }
0854:
0855: if (systemId != null)
0856: return getSearchPath().lookup(systemId).openRead();
0857:
0858: throw new TransformerException("bad source " + source);
0859: }
0860:
0861: Path lookupPath(String base, String href)
0862: throws TransformerException {
0863: if (_uriResolver != null) {
0864: Source source = _uriResolver.resolve(href, base);
0865:
0866: if (source != null) {
0867: String systemId = source.getSystemId();
0868:
0869: if (systemId != null)
0870: return getSearchPath().lookup(systemId);
0871: }
0872: }
0873:
0874: return getSearchPath().lookup(base).lookup(href);
0875: }
0876:
0877: /**
0878: * Convenience class to create a transformer instance.
0879: *
0880: * @param xsl DOM source for the stylesheet.
0881: *
0882: * @return a transformer instance.
0883: */
0884: public javax.xml.transform.Transformer newTransformer(Document xsl)
0885: throws TransformerConfigurationException {
0886: return newTemplates(xsl).newTransformer();
0887: }
0888:
0889: /**
0890: * Convenience class to transform a node.
0891: *
0892: * @param xsl DOM containing the parsed xsl.
0893: * @param xml DOM document node.
0894: * @param out output stream destination.
0895: */
0896: public void transform(Document xsl, Node xml, OutputStream out)
0897: throws Exception {
0898: TransformerImpl transformer = (TransformerImpl) newTransformer(xsl);
0899:
0900: transformer.transform(xml, out);
0901: }
0902:
0903: /**
0904: * Convenience class to transform a node.
0905: *
0906: * @param xsl path name to the xsl file.
0907: * @param xml dom source document.
0908: * @param out output stream destination.
0909: */
0910: public void transform(String xsl, Node xml, OutputStream out)
0911: throws Exception {
0912: TransformerImpl transformer;
0913:
0914: transformer = (TransformerImpl) newTemplates(xsl)
0915: .newTransformer();
0916:
0917: transformer.transform(xml, out);
0918: }
0919:
0920: /**
0921: * Parses the XSL into a DOM document.
0922: *
0923: * @param rs the input stream.
0924: */
0925: abstract protected Document parseXSL(ReadStream rs)
0926: throws TransformerConfigurationException;
0927:
0928: /**
0929: * Generates a compiled stylesheet from a parsed XSL document.
0930: *
0931: * @param xsl the parsed xsl document.
0932: * @param path the path to the document.
0933: */
0934: Templates generate(Node xsl, Path path)
0935: throws TransformerConfigurationException {
0936: log.fine("Generating XSL from " + path);
0937:
0938: // The code generation needs a static lock because the
0939: // application might have a separate factory object
0940: // for each thread. The static lock protects the code generation
0941: // from overwriting its own code.
0942: synchronized (AbstractStylesheetFactory.class) {
0943: Generator gen = null;
0944:
0945: try {
0946: if (path == null && xsl != null) {
0947: Document doc = xsl.getOwnerDocument();
0948: if (doc == null && xsl instanceof Document)
0949: doc = (Document) xsl;
0950:
0951: DocumentType dtd = doc.getDoctype();
0952: String systemId = null;
0953: if (dtd != null)
0954: systemId = dtd.getSystemId();
0955:
0956: if (systemId != null)
0957: path = getStylePath().lookup(systemId);
0958: }
0959:
0960: if (path == null && xsl instanceof CauchoNode) {
0961: String filename = ((CauchoNode) xsl).getFilename();
0962: if (filename != null)
0963: path = getStylePath().lookup(filename);
0964: }
0965:
0966: if (path == null)
0967: path = getStylePath().lookup("anonymous.xsl");
0968:
0969: Path stylePath = path.getParent();
0970:
0971: Expr expr = XPath
0972: .parseExpr("//xtp:directive.page/@language");
0973: String language = expr.evalString(xsl);
0974:
0975: String userName = path.getUserPath();
0976: String mangledName = getMangledName(userName);
0977:
0978: String encoding = XPath.evalString(
0979: "//xsl:output/@encoding", xsl);
0980: if (encoding != null && encoding.equals(""))
0981: encoding = null;
0982:
0983: if (language == null || language.equals("")
0984: || language.equals("java")) {
0985: language = "java";
0986: gen = new JavaGenerator(this , mangledName, encoding);
0987: } else
0988: throw new XslParseException(L.l(
0989: "unsupported language `{0}'", language));
0990:
0991: gen.setPath(path);
0992:
0993: Iterator iter = XPath.select("//xtp:directive.page/@*",
0994: xsl);
0995: while (iter.hasNext()) {
0996: Attr attr = (Attr) iter.next();
0997: String name = attr.getNodeName();
0998: String value = attr.getNodeValue();
0999:
1000: if (name.equals("errorPage"))
1001: gen.setErrorPage(value);
1002: else if (name.equals("import"))
1003: gen.addImport(value);
1004: else if (name.equals("contentType"))
1005: gen.setContentType(value);
1006: else if (name.equals("language")) {
1007: if (!language.equalsIgnoreCase(value))
1008: throw new XslParseException(L.l(
1009: "mismatched language `{0}'", value));
1010: } else if (name.equals("xml:space")) {
1011: } else
1012: throw new XslParseException(L.l(
1013: "unknown directive `{0}'", name));
1014: }
1015:
1016: StylesheetImpl stylesheet = gen.generate(xsl);
1017: gen = null;
1018: stylesheet.init(path);
1019: // XXX: why was this here? stylesheet.init(getStylePath());
1020: stylesheet.setURIResolver(_uriResolver);
1021:
1022: return stylesheet;
1023: } catch (TransformerConfigurationException e) {
1024: throw e;
1025: } catch (Exception e) {
1026: throw new XslParseException(e);
1027: } finally {
1028: try {
1029: if (gen != null)
1030: gen.close();
1031: } catch (IOException e) {
1032: }
1033: }
1034: }
1035: }
1036:
1037: /**
1038: * Returns the mangled classname for the stylesheet. If getClassName()
1039: * is not null, it will be used as the mangled name.
1040: *
1041: * @param userName the user specified name for the stylesheet.
1042: *
1043: * @return a valid Java classname for the generated stylesheet.
1044: */
1045: private String getMangledName(String userName) {
1046: String name = null;
1047:
1048: if (userName == null || userName.equals("anonymous.xsl")
1049: || userName.equals("string")
1050: || userName.equals("stream")) {
1051: userName = "x" + (new Random().nextInt() & 0x3ff) + ".xsl";
1052: }
1053:
1054: if (getClassName() == null)
1055: name = userName;
1056: else
1057: name = getClassName();
1058:
1059: if (name.startsWith("/"))
1060: name = "xsl" + name;
1061: else
1062: name = "xsl/" + name;
1063:
1064: return JavaCompiler.mangleName(name);
1065: }
1066:
1067: /**
1068: * Returns existing compiled Templates if it exists.
1069: *
1070: * @param systemId source path for the stylesheet.
1071: *
1072: * @return a compiled stylesheet
1073: */
1074: StylesheetImpl loadPrecompiledStylesheet(String systemId,
1075: String userId) {
1076: return loadPrecompiledStylesheet(systemId, userId,
1077: _isAutoCompile);
1078: }
1079:
1080: /**
1081: * Returns existing compiled Templates if it exists.
1082: *
1083: * @param systemId source path for the stylesheet.
1084: *
1085: * @return a compiled stylesheet
1086: */
1087: StylesheetImpl loadPrecompiledStylesheet(String systemId,
1088: String userId, boolean checkModified) {
1089: if (!_loadPrecompiledStylesheet)
1090: return null;
1091:
1092: try {
1093: // look for compiled template base on SystemID
1094: StylesheetImpl stylesheet = loadStylesheet(systemId,
1095: getMangledName(userId));
1096:
1097: if (stylesheet == null)
1098: return null;
1099:
1100: stylesheet.setURIResolver(_uriResolver);
1101: // and check if it's modified or not
1102: if (!checkModified || !stylesheet.isModified()) {
1103: stylesheet.setURIResolver(_uriResolver);
1104: return stylesheet;
1105: }
1106: } catch (Throwable e) {
1107: log.log(Level.FINER, e.toString(), e);
1108: }
1109:
1110: return null;
1111: }
1112:
1113: /**
1114: * Loads the compiled stylesheet .class file
1115: *
1116: * @param className the mangled classname for the stylesheet
1117: */
1118: protected StylesheetImpl loadStylesheet(String systemId,
1119: String className) throws Exception {
1120: LruCache<String, SoftReference<StylesheetImpl>> cache;
1121:
1122: ClassLoader parentLoader = Thread.currentThread()
1123: .getContextClassLoader();
1124:
1125: cache = _stylesheetCache.getLevel(parentLoader);
1126:
1127: if (cache == null) {
1128: cache = new LruCache<String, SoftReference<StylesheetImpl>>(
1129: 256);
1130: _stylesheetCache.set(cache, parentLoader);
1131: }
1132:
1133: SoftReference<StylesheetImpl> stylesheetRef;
1134:
1135: stylesheetRef = cache.get(className);
1136:
1137: StylesheetImpl stylesheet = null;
1138:
1139: if (stylesheetRef != null)
1140: stylesheet = stylesheetRef.get();
1141:
1142: try {
1143: if (stylesheet != null && !stylesheet.isModified())
1144: return stylesheet;
1145: } catch (Throwable e) {
1146: log.log(Level.FINER, e.toString(), e);
1147: }
1148:
1149: Path classPath = getWorkPath().lookup(
1150: className.replace('.', '/') + ".class");
1151: if (!classPath.canRead())
1152: throw new ClassNotFoundException(
1153: "can't find compiled XSL `" + className + "'");
1154:
1155: DynamicClassLoader loader;
1156: loader = SimpleLoader.create(parentLoader, getWorkPath(),
1157: className);
1158:
1159: Class cl = null;
1160:
1161: // If the loading fails, remove the class because it may be corrupted
1162: try {
1163: cl = CauchoSystem.loadClass(className, false, loader);
1164: } catch (Error e) {
1165: try {
1166: classPath.remove();
1167: } catch (IOException e1) {
1168: log.log(Level.FINE, e1.toString(), e1);
1169: }
1170:
1171: throw e;
1172: }
1173:
1174: stylesheet = (StylesheetImpl) cl.newInstance();
1175: Path path;
1176:
1177: path = getSearchPath().lookup("").lookup(systemId);
1178: /*
1179: try {
1180: } catch (TransformerException e) {
1181: log.log(Level.FINE, e.toString(), e);
1182:
1183: path = Vfs.lookup(systemId);
1184: }
1185: */
1186:
1187: // stylesheet.init(path);
1188: stylesheet.init(getStylePath());
1189: stylesheet.setURIResolver(_uriResolver);
1190:
1191: stylesheetRef = new SoftReference<StylesheetImpl>(stylesheet);
1192: cache.put(className, stylesheetRef);
1193:
1194: return stylesheet;
1195: }
1196: }
|