| /* |
| * @(#)$Id$ |
| * |
| * The Apache Software License, Version 1.1 |
| * |
| * |
| * Copyright (c) 2001 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xalan" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation and was |
| * originally based on software copyright (c) 2001, Sun |
| * Microsystems., http://www.sun.com. For more |
| * information on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| * |
| * @author Jacek Ambroziak |
| * @author Santiago Pericas-Geertsen |
| * @author G. Todd Miller |
| * @author Morten Jorgensen |
| * |
| */ |
| |
| package org.apache.xalan.xsltc.compiler; |
| |
| import java.io.*; |
| import java.util.Vector; |
| import java.util.Hashtable; |
| import java.util.Set; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.Enumeration; |
| import java.util.StringTokenizer; |
| import java.util.Date; |
| import java.util.Map; |
| import java.net.URL; |
| import java.net.MalformedURLException; |
| import java.util.jar.*; |
| |
| import org.xml.sax.*; |
| |
| import javax.xml.parsers.*; |
| import javax.xml.transform.ErrorListener; |
| |
| import org.apache.xalan.xsltc.compiler.util.*; |
| import org.apache.xalan.xsltc.util.getopt.*; |
| import org.apache.xalan.xsltc.DOM; |
| import de.fub.bytecode.classfile.JavaClass; |
| |
| public final class XSLTC { |
| |
| // A reference to the main parsergobject. |
| private final Parser _parser; |
| |
| // A reference to the stylesheet being compiled. |
| private Stylesheet _stylesheet = null; |
| |
| // Command line options/args |
| private boolean _debug; |
| private boolean _isJarFileSpecified; |
| private String _className; // -o effects this |
| private String _packageName; |
| private File _destDir; |
| private File _dumpDir; |
| |
| // Counters used by various classes to generate unique names. |
| private int _variableSerial = 1; |
| private int _modeSerial = 1; |
| private int _stylesheetSerial = 1; |
| private int _stepPatternSerial = 1; |
| private int _helperClassSerial = 0; |
| private int _attributeSetSerial = 0; |
| |
| private int[] _numberFieldIndexes; |
| |
| // Name index tables |
| private int _nextGType; // Next available element type |
| private Vector _namesIndex; // Index of all registered QNames |
| private Hashtable _elements; // Hashtable of all registered elements |
| private Hashtable _attributes; // Hashtable of all registered attributes |
| // Namespace index tables |
| private int _nextNSType; // Next available namespace type |
| private Vector _namespaceIndex; // Index of all registered namespaces |
| private Hashtable _namespaces; // Hashtable of all registered namespaces |
| |
| private static final int FILE_OUTPUT = 0; |
| private static final int JAR_OUTPUT = 1; |
| private static final int BYTEARRAY_OUTPUT = 2; |
| private static final int CLASSLOADER_OUTPUT = 3; |
| |
| private int _outputType = FILE_OUTPUT; // by default |
| private Vector _classes; |
| private boolean _multiDocument = false; |
| |
| public XSLTC() { |
| _parser = new Parser(this); |
| } |
| |
| public void init() { |
| reset(); |
| _classes = new Vector(); |
| } |
| |
| private void reset() { |
| _nextGType = DOM.NTYPES; |
| _elements = new Hashtable(); |
| _attributes = new Hashtable(); |
| _namespaces = new Hashtable(); |
| _namespaces.put("",new Integer(_nextNSType)); |
| _namesIndex = new Vector(128); |
| _namespaceIndex = new Vector(32); |
| _parser.init(); |
| _variableSerial = 1; |
| _modeSerial = 1; |
| _stylesheetSerial = 1; |
| _stepPatternSerial = 1; |
| _helperClassSerial = 0; |
| _attributeSetSerial = 0; |
| _multiDocument = false; |
| _numberFieldIndexes = new int[] { |
| -1, // LEVEL_SINGLE |
| -1, // LEVEL_MULTIPLE |
| -1 // LEVEL_ANY |
| }; |
| } |
| |
| public void setMultiDocument(boolean flag) { |
| _multiDocument = flag; |
| } |
| |
| // GTM: TBD this is a prototype to handle input stream input.. |
| public boolean compile(InputStream input, String transletName) { |
| return compile(input, transletName, null); |
| } |
| // GTM: TBD this is a prototype, copied and modified fr compile(URL..) |
| public boolean compile(InputStream input, String transletName, |
| ErrorListener elistener) |
| { |
| if (elistener != null) { |
| _parser.setErrorListener(elistener); |
| } |
| try { |
| reset(); |
| final String name = transletName; |
| setClassName(transletName); |
| |
| // Get the root node of the abstract syntax tree |
| final SyntaxTreeNode element = _parser.parse(input); |
| // Process any error and/or warning messages |
| _parser.printWarnings(); |
| if (_parser.errorsFound()) { |
| _parser.printErrors(); |
| return false; |
| } |
| |
| if ((!_parser.errorsFound()) && (element != null)) { |
| _stylesheet = _parser.makeStylesheet(element); |
| //_stylesheet.setURL(url); |
| // This is the top level stylesheet - it has no parent |
| _stylesheet.setParentStylesheet(null); |
| _parser.setCurrentStylesheet(_stylesheet); |
| _parser.createAST(_stylesheet); |
| if (_stylesheet != null && _parser.errorsFound() == false) { |
| _stylesheet.setMultiDocument(_multiDocument); |
| _stylesheet.translate(); |
| } |
| else { |
| _parser.printErrors(); |
| } |
| } |
| else { |
| _parser.printErrors(); |
| } |
| _parser.printWarnings(); |
| return !_parser.errorsFound(); |
| } |
| catch (CompilerException e) { |
| e.printStackTrace(); |
| _parser.reportError(Constants.FATAL, new ErrorMsg(e.getMessage())); |
| _parser.printErrors(); |
| return false; |
| } |
| } |
| |
| |
| /** |
| * Compiles the stylesheet into Java bytecode. Returns 'true' if the |
| * compilation was successful - 'false' otherwise. |
| */ |
| public boolean compile(URL stylesheet) { |
| return compile(stylesheet, null); |
| } |
| |
| /** |
| * Compile stylesheet into Java bytecode. Returns 'true' if compilation |
| * is successful. - 'false' otherwise. ErrorListener arg (may be null) |
| * added to support TrAX API. |
| */ |
| public boolean compile(URL url, ErrorListener elistener) { |
| if (elistener != null) { |
| _parser.setErrorListener(elistener); |
| } |
| try { |
| reset(); |
| final String name = url.getFile(); |
| if (_className == null) { |
| final String baseName = Util.baseName(name); |
| setClassName(Util.toJavaName(Util.noExtName(baseName))); |
| } |
| |
| // Get the root node of the abstract syntax tree |
| final SyntaxTreeNode element = _parser.parse(url); |
| |
| // Process any error and/or warning messages found so far... |
| if ((_parser.errorsFound()) || (element == null)) { |
| _parser.printWarnings(); |
| _parser.printErrors(); |
| return false; |
| } |
| |
| // Create the abstract syntax tree and parse each node in it |
| _stylesheet = _parser.makeStylesheet(element); |
| _stylesheet.setURL(url); |
| _stylesheet.setParentStylesheet(null); |
| _parser.setCurrentStylesheet(_stylesheet); |
| _parser.createAST(_stylesheet); |
| |
| // Process any error and/or warning messages found so far... |
| if ((_parser.errorsFound()) || (_stylesheet == null)) { |
| _parser.printWarnings(); |
| _parser.printErrors(); |
| return false; |
| } |
| |
| // Compile the translet |
| _stylesheet.setMultiDocument(_multiDocument); |
| _stylesheet.translate(); |
| |
| // Process any error and/or warning messages found so far... |
| if ((_parser.errorsFound()) || (_stylesheet == null)) { |
| _parser.printWarnings(); |
| _parser.printErrors(); |
| return false; |
| } |
| |
| _parser.printWarnings(); |
| return true; |
| } |
| catch (CompilerException e) { |
| e.printStackTrace(); |
| _parser.reportError(Constants.FATAL, new ErrorMsg(e.getMessage())); |
| _parser.printErrors(); |
| return false; |
| } |
| } |
| |
| private boolean compile(Vector stylesheets) { |
| final int nStylesheets = stylesheets.size(); |
| /* |
| * Note: if there are multiple stylesheets, then '_className' must |
| * be reset to null each time; otherwise it should not be |
| * reset because user might have specified a new name with the |
| * -o <name> option. |
| */ |
| if (nStylesheets > 1) { |
| final Enumeration urls = stylesheets.elements(); |
| while (urls.hasMoreElements()) { |
| final URL stylesheetURL = (URL)urls.nextElement(); |
| _className = null; // reset, so that new name will be computed |
| if (!compile(stylesheetURL)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| else { |
| final URL stylesheetURL = (URL)stylesheets.firstElement(); |
| // do not reset _className to null in case -o option was used. |
| return compile(stylesheetURL); |
| } |
| } |
| |
| private void setClassName(String className) { |
| final String name = |
| Util.toJavaName(Util.noExtName(Util.baseName(className))); |
| _className = (_packageName != null) ? _packageName + '.' + name : name; |
| } |
| |
| public String getClassName() { |
| return _className; |
| } |
| |
| private void setDebug(boolean debug) { |
| _debug = debug; |
| } |
| |
| |
| public boolean debug() { |
| return _debug; |
| } |
| |
| private void setDestDirectory(String dstDirName) throws CompilerException { |
| final File dir = new File(dstDirName); |
| if (dir.exists() || dir.mkdirs()) { |
| _destDir = dir; |
| } |
| else { |
| throw new CompilerException("Could not create output directory"); |
| } |
| } |
| |
| private void setPackageName(String packageName) { |
| _packageName = packageName; |
| } |
| |
| private void setJarFileSpecified(boolean value) { |
| if (_isJarFileSpecified = value) { |
| _outputType = JAR_OUTPUT; |
| } |
| } |
| |
| public Stylesheet getStylesheet() { |
| return _stylesheet; |
| } |
| |
| public void setStylesheet(Stylesheet stylesheet) { |
| if (_stylesheet == null) |
| _stylesheet = stylesheet; |
| } |
| |
| private File getOutputFile(String className) { |
| return new File(_dumpDir != null ? _dumpDir : _destDir, |
| classFileName(className)); |
| } |
| |
| private String classFileName(final String className) { |
| return className.replace('.', File.separatorChar) + ".class"; |
| } |
| |
| public Vector getNamesIndex() { |
| return _namesIndex; |
| } |
| |
| public Vector getNamespaceIndex() { |
| return _namespaceIndex; |
| } |
| |
| /** |
| * Registers an attribute and gives it a type so that it can be mapped to |
| * DOM attribute types at run-time. |
| */ |
| public int registerAttribute(QName name) { |
| Integer code = (Integer)_attributes.get(name); |
| if (code == null) { |
| _attributes.put(name, code = new Integer(_nextGType++)); |
| final String uri = name.getNamespace(); |
| final String local = "@"+name.getLocalPart(); |
| if ((uri != null) && (!uri.equals(""))) |
| _namesIndex.addElement(uri+":"+local); |
| else |
| _namesIndex.addElement(local); |
| if (name.getLocalPart().equals("*")) { |
| registerNamespace(name.getNamespace()); |
| } |
| } |
| return code.intValue(); |
| } |
| |
| /** |
| * Registers an element and gives it a type so that it can be mapped to |
| * DOM element types at run-time. |
| */ |
| public int registerElement(QName name) { |
| // Register element (full QName) |
| Integer code = (Integer)_elements.get(name.toString()); |
| if (code == null) { |
| _elements.put(name.toString(), code = new Integer(_nextGType++)); |
| _namesIndex.addElement(name.toString()); |
| } |
| if (name.getLocalPart().equals("*")) { |
| registerNamespace(name.getNamespace()); |
| } |
| return code.intValue(); |
| } |
| |
| /** |
| * Registers a namespace and gives it a type so that it can be mapped to |
| * DOM namespace types at run-time. |
| */ |
| public int registerNamespace(String namespaceURI) { |
| Integer code = (Integer)_namespaces.get(namespaceURI); |
| if (code == null) { |
| code = new Integer(_nextNSType++); |
| _namespaces.put(namespaceURI,code); |
| _namespaceIndex.addElement(namespaceURI); |
| } |
| return code.intValue(); |
| } |
| |
| /** |
| * Aborts the execution of the compiler if something found in the source |
| * file can't be compiled. It also prints which feature is not implemented |
| * if specified. |
| */ |
| /* |
| public void notYetImplemented(String feature) { |
| System.err.println("'"+feature+"' is not supported by XSLTC."); |
| if (debug()) { |
| _parser.errorsFound(); // print stack |
| } |
| doSystemExit(1); throw new RuntimeException("System.exit(1) here!"); |
| } |
| */ |
| |
| /** |
| * Aborts the execution of the compiler if something found in the source |
| * file can't be compiled. It also prints which feature is not implemented |
| * if specified. |
| */ |
| /* |
| public void extensionNotSupported(String feature) { |
| System.err.println("Extension element '"+feature+ |
| "' is not supported by XSLTC."); |
| if (debug()) { |
| _parser.errorsFound(); // print stack |
| } |
| doSystemExit(1); throw new RuntimeException("System.exit(1) here!"); |
| } |
| */ |
| |
| public int nextVariableSerial() { |
| return _variableSerial++; |
| } |
| |
| public int nextModeSerial() { |
| return _modeSerial++; |
| } |
| |
| public int nextStylesheetSerial() { |
| return _stylesheetSerial++; |
| } |
| |
| public int nextStepPatternSerial() { |
| return _stepPatternSerial++; |
| } |
| |
| public int[] getNumberFieldIndexes() { |
| return _numberFieldIndexes; |
| } |
| |
| public int nextHelperClassSerial() { |
| return _helperClassSerial++; |
| } |
| |
| public int nextAttributeSetSerial() { |
| return _attributeSetSerial++; |
| } |
| |
| /** |
| * Returns a unique name for every helper class needed to |
| * execute a translet. |
| */ |
| public String getHelperClassName() { |
| return getClassName() + '$' + _helperClassSerial++; |
| } |
| |
| public void dumpClass(JavaClass clazz) { |
| try { |
| switch (_outputType) { |
| case FILE_OUTPUT: |
| clazz.dump(getOutputFile(clazz.getClassName())); |
| break; |
| |
| case JAR_OUTPUT: |
| _classes.addElement(clazz); |
| break; |
| |
| case BYTEARRAY_OUTPUT: |
| case CLASSLOADER_OUTPUT: |
| ByteArrayOutputStream out = new ByteArrayOutputStream(2048); |
| clazz.dump(out); |
| _classes.addElement(out.toByteArray()); |
| break; |
| } |
| } |
| catch (Exception e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| private byte[][] outputToArrays() { |
| final int nClasses = _classes.size(); |
| final byte[][] result = new byte[1][nClasses]; |
| for (int i = 0; i < nClasses; i++) { |
| result[i] = (byte[])_classes.elementAt(i); |
| } |
| return result; |
| } |
| |
| /** |
| * File separators are converted to forward slashes for ZIP files. |
| */ |
| private String entryName( File f ) throws IOException { |
| return f.getName().replace(File.separatorChar, '/'); |
| } |
| |
| /** |
| * Jar and packages |
| */ |
| private void outputToJar(String jarFileName) throws IOException { |
| // create the manifest |
| final Manifest manifest = new Manifest(); |
| manifest.getMainAttributes() |
| .put(java.util.jar.Attributes.Name.MANIFEST_VERSION, "1.0"); |
| |
| final Map map = manifest.getEntries(); |
| // create manifest |
| Enumeration classes = _classes.elements(); |
| final String now = (new Date()).toString(); |
| final java.util.jar.Attributes.Name dateAttr = |
| new java.util.jar.Attributes.Name("Date"); |
| while (classes.hasMoreElements()) { |
| final JavaClass clazz = (JavaClass)classes.nextElement(); |
| final java.util.jar.Attributes attr = |
| new java.util.jar.Attributes(); |
| attr.put(dateAttr, now); |
| map.put(classFileName(clazz.getClassName()), attr); |
| } |
| |
| final File jarFile = new File(_destDir, jarFileName+".jar"); |
| final JarOutputStream jos = |
| new JarOutputStream(new FileOutputStream(jarFile), manifest); |
| classes = _classes.elements(); |
| while (classes.hasMoreElements()) { |
| final JavaClass cl = (JavaClass)classes.nextElement(); |
| jos.putNextEntry(new JarEntry(classFileName(cl.getClassName()))); |
| final ByteArrayOutputStream out = new ByteArrayOutputStream(2048); |
| cl.dump(out); // dump() closes it's output stream |
| out.writeTo(jos); |
| } |
| jos.close(); |
| } |
| |
| public byte[][] compileStylesheet(URL stylesheetURL, String className) { |
| _outputType = BYTEARRAY_OUTPUT; |
| setClassName(className); |
| return compile(stylesheetURL) ? outputToArrays() : null; |
| } |
| |
| /** |
| * Command line runnability. |
| * o className |
| * d destDirectory |
| * p packageName |
| * j jarFileName |
| * u (isUriSpecified) |
| * x (isDebugSpecified) |
| * h printUsage() |
| * s (don't allow System.exit) |
| */ |
| public static void main(String[] args) { |
| try { |
| final GetOpt getopt = new GetOpt(args, "o:d:j:p:uxhs"); |
| if (args.length < 1) { |
| printUsage(); |
| doSystemExit(1); return; |
| } |
| boolean isUriSpecified = false; |
| boolean isDebugSpecified = false; |
| boolean isJarFileSpecified = false; |
| String jarFileName = null; |
| String destDirectory = "."; // cwd by default |
| String packageName = null; |
| String className = null; |
| int c; |
| while ((c = getopt.getNextOption()) != -1) { |
| switch(c) { |
| case 'o': |
| className = getopt.getOptionArg(); |
| break; |
| case 'd': |
| destDirectory = getopt.getOptionArg(); |
| break; |
| case 'p': |
| packageName = getopt.getOptionArg(); |
| break; |
| case 'j': |
| jarFileName = getopt.getOptionArg(); |
| isJarFileSpecified = true; |
| break; |
| case 'u': |
| isUriSpecified = true; |
| break; |
| case 'x': |
| isDebugSpecified = true; |
| break; |
| case 's': |
| allowSystemExit = false; |
| break; |
| case 'h': |
| printUsage(); |
| break; |
| default: |
| printUsage(); |
| break; |
| } |
| } |
| |
| final String[] stylesheets = getopt.getCmdArgs(); |
| final int nStyleSheets = stylesheets.length; |
| |
| File dir = new File(destDirectory); |
| |
| if (!dir.isDirectory() || (className != null && nStyleSheets > 1)) { |
| printUsage(); |
| doSystemExit(1); return; |
| } |
| |
| dir = null; |
| |
| final XSLTC xsltc = new XSLTC(); |
| xsltc.init(); |
| xsltc.setDebug(isDebugSpecified); |
| xsltc.setPackageName(packageName); |
| xsltc.setDestDirectory(destDirectory); |
| xsltc.setJarFileSpecified(isJarFileSpecified); |
| if (className != null) { |
| xsltc.setClassName(className); |
| } |
| final Vector stylesheetVector = new Vector(); |
| for (int i = 0; i < nStyleSheets; i++) { |
| final String currStyleSheetName = stylesheets[i]; |
| final URL stylesheetURL; |
| if (isUriSpecified) { |
| stylesheetURL = new URL(currStyleSheetName); |
| } |
| else { |
| File stylesheetFile = new File(currStyleSheetName); |
| stylesheetURL = stylesheetFile.toURL(); |
| } |
| stylesheetVector.addElement(stylesheetURL); |
| } |
| final long startTime = System.currentTimeMillis(); |
| if (xsltc.compile(stylesheetVector)) { |
| if (isJarFileSpecified) { |
| xsltc.outputToJar(jarFileName); |
| } |
| if (isDebugSpecified) { |
| Util.println("compile time " + |
| (System.currentTimeMillis() - startTime) + |
| " msec"); |
| } |
| } |
| else { |
| Util.println("compilation failed"); |
| doSystemExit(1); return; |
| } |
| } |
| catch (GetOptsException ex) { |
| System.err.println(ex); |
| printUsage(); |
| doSystemExit(1); return; |
| } |
| catch (Exception e) { |
| e.printStackTrace(); |
| doSystemExit(1); return; |
| } |
| } |
| |
| /** If we should call System.exit or not */ |
| protected static boolean allowSystemExit = true; |
| |
| /** Worker method to call System.exit or not */ |
| protected static void doSystemExit(int retVal) { |
| if (allowSystemExit) |
| System.exit(retVal); |
| } |
| |
| private final static String USAGE_STRING = |
| "Usage:\n" + |
| " xsltc [-o <output>] [-d <directory>] [-j <jarfile>]\n" + |
| " [-p <package name>] \n" + |
| " [-u] <stylesheet>... \n\n" + |
| " where <stylesheet> is a file or if -u option is used, \n" + |
| " is a URL such as http://myserver/stylesheet1.xsl.\n"+ |
| " <jarfile> is the name of jar file, do not specify \n" + |
| " the .jar extension. Example: -j MyJar \n"+ |
| " Note: the -o option should not be used when processing\n"+ |
| " multiple stylesheets. \n"+ |
| " also: [-x] (debug), [-s] (don't allow System.exit)"; |
| |
| public static void printUsage() { |
| System.err.println(USAGE_STRING); |
| } |
| } |