blob: 6348f22e72891855a6ea9835dbba523d68ff4760 [file] [log] [blame]
/*
* @(#)$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);
}
}