jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java
changeset 12457 c348e06f0e82
parent 6 7f561c08de6b
child 12458 d601e4bba306
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jaxp/src/com/sun/org/apache/xalan/internal/xsltc/compiler/XSLTC.java	Thu Apr 12 08:38:26 2012 -0700
@@ -0,0 +1,899 @@
+/*
+ * reserved comment block
+ * DO NOT REMOVE OR ALTER!
+ */
+/*
+ * Copyright 2001-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: XSLTC.java,v 1.2.4.1 2005/09/05 09:51:38 pvedula Exp $
+ */
+
+package com.sun.org.apache.xalan.internal.xsltc.compiler;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import com.sun.org.apache.bcel.internal.classfile.JavaClass;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.ErrorMsg;
+import com.sun.org.apache.xalan.internal.xsltc.compiler.util.Util;
+import com.sun.org.apache.xml.internal.dtm.DTM;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.XMLReader;
+
+/**
+ * @author Jacek Ambroziak
+ * @author Santiago Pericas-Geertsen
+ * @author G. Todd Miller
+ * @author Morten Jorgensen
+ * @author John Howard (johnh@schemasoft.com)
+ */
+public final class XSLTC {
+
+    // A reference to the main stylesheet parser object.
+    private Parser _parser;
+
+    // A reference to an external XMLReader (SAX parser) passed to us
+    private XMLReader _reader = null;
+
+    // A reference to an external SourceLoader (for use with include/import)
+    private SourceLoader _loader = null;
+
+    // A reference to the stylesheet being compiled.
+    private Stylesheet _stylesheet;
+
+    // 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 Hashtable _namespacePrefixes;// Hashtable of all registered namespace prefixes
+
+
+    // All literal text in the stylesheet
+    private Vector m_characterData;
+
+    // These define the various methods for outputting the translet
+    public static final int FILE_OUTPUT        = 0;
+    public static final int JAR_OUTPUT         = 1;
+    public static final int BYTEARRAY_OUTPUT   = 2;
+    public static final int CLASSLOADER_OUTPUT = 3;
+    public static final int BYTEARRAY_AND_FILE_OUTPUT = 4;
+    public static final int BYTEARRAY_AND_JAR_OUTPUT  = 5;
+
+
+    // Compiler options (passed from command line or XSLTC client)
+    private boolean _debug = false;      // -x
+    private String  _jarFileName = null; // -j <jar-file-name>
+    private String  _className = null;   // -o <class-name>
+    private String  _packageName = null; // -p <package-name>
+    private File    _destDir = null;     // -d <directory-name>
+    private int     _outputType = FILE_OUTPUT; // by default
+
+    private Vector  _classes;
+    private Vector  _bcelClasses;
+    private boolean _callsNodeset = false;
+    private boolean _multiDocument = false;
+    private boolean _hasIdCall = false;
+
+    /**
+     * Set to true if template inlining is requested. Template
+     * inlining used to be the default, but we have found that
+     * Hotspots does a better job with shorter methods, so the
+     * default is *not* to inline now.
+     */
+    private boolean _templateInlining = false;
+
+    /**
+     * State of the secure processing feature.
+     */
+    private boolean _isSecureProcessing = false;
+
+    /**
+     * XSLTC compiler constructor
+     */
+    public XSLTC() {
+        _parser = new Parser(this);
+    }
+
+    /**
+     * Set the state of the secure processing feature.
+     */
+    public void setSecureProcessing(boolean flag) {
+        _isSecureProcessing = flag;
+    }
+
+    /**
+     * Return the state of the secure processing feature.
+     */
+    public boolean isSecureProcessing() {
+        return _isSecureProcessing;
+    }
+
+    /**
+     * Only for user by the internal TrAX implementation.
+     */
+    public Parser getParser() {
+        return _parser;
+    }
+
+    /**
+     * Only for user by the internal TrAX implementation.
+     */
+    public void setOutputType(int type) {
+        _outputType = type;
+    }
+
+    /**
+     * Only for user by the internal TrAX implementation.
+     */
+    public Properties getOutputProperties() {
+        return _parser.getOutputProperties();
+    }
+
+    /**
+     * Initializes the compiler to compile a new stylesheet
+     */
+    public void init() {
+        reset();
+        _reader = null;
+        _classes = new Vector();
+        _bcelClasses = new Vector();
+    }
+
+    /**
+     * Initializes the compiler to produce a new translet
+     */
+    private void reset() {
+        _nextGType      = DTM.NTYPES;
+        _elements       = new Hashtable();
+        _attributes     = new Hashtable();
+        _namespaces     = new Hashtable();
+        _namespaces.put("",new Integer(_nextNSType));
+        _namesIndex     = new Vector(128);
+        _namespaceIndex = new Vector(32);
+        _namespacePrefixes = new Hashtable();
+        _stylesheet     = null;
+        _parser.init();
+        //_variableSerial     = 1;
+        _modeSerial         = 1;
+        _stylesheetSerial   = 1;
+        _stepPatternSerial  = 1;
+        _helperClassSerial  = 0;
+        _attributeSetSerial = 0;
+        _multiDocument      = false;
+        _hasIdCall          = false;
+        _numberFieldIndexes = new int[] {
+            -1,         // LEVEL_SINGLE
+            -1,         // LEVEL_MULTIPLE
+            -1          // LEVEL_ANY
+        };
+    }
+
+    /**
+     * Defines an external SourceLoader to provide the compiler with documents
+     * referenced in xsl:include/import
+     * @param loader The SourceLoader to use for include/import
+     */
+    public void setSourceLoader(SourceLoader loader) {
+        _loader = loader;
+    }
+
+    /**
+     * Set a flag indicating if templates are to be inlined or not. The
+     * default is to do inlining, but this causes problems when the
+     * stylesheets have a large number of templates (e.g. branch targets
+     * exceeding 64K or a length of a method exceeding 64K).
+     */
+    public void setTemplateInlining(boolean templateInlining) {
+        _templateInlining = templateInlining;
+    }
+
+    /**
+     * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
+     * processing instruction in the case where the input document to the
+     * compiler (and parser) is an XML document.
+     * @param media The media attribute to be matched. May be null, in which
+     * case the prefered templates will be used (i.e. alternate = no).
+     * @param title The value of the title attribute to match. May be null.
+     * @param charset The value of the charset attribute to match. May be null.
+     */
+    public void setPIParameters(String media, String title, String charset) {
+        _parser.setPIParameters(media, title, charset);
+    }
+
+    /**
+     * Compiles an XSL stylesheet pointed to by a URL
+     * @param url An URL containing the input XSL stylesheet
+     */
+    public boolean compile(URL url) {
+        try {
+            // Open input stream from URL and wrap inside InputSource
+            final InputStream stream = url.openStream();
+            final InputSource input = new InputSource(stream);
+            input.setSystemId(url.toString());
+            return compile(input, _className);
+        }
+        catch (IOException e) {
+            _parser.reportError(Constants.FATAL, new ErrorMsg(e));
+            return false;
+        }
+    }
+
+    /**
+     * Compiles an XSL stylesheet pointed to by a URL
+     * @param url An URL containing the input XSL stylesheet
+     * @param name The name to assign to the translet class
+     */
+    public boolean compile(URL url, String name) {
+        try {
+            // Open input stream from URL and wrap inside InputSource
+            final InputStream stream = url.openStream();
+            final InputSource input = new InputSource(stream);
+            input.setSystemId(url.toString());
+            return compile(input, name);
+        }
+        catch (IOException e) {
+            _parser.reportError(Constants.FATAL, new ErrorMsg(e));
+            return false;
+        }
+    }
+
+    /**
+     * Compiles an XSL stylesheet passed in through an InputStream
+     * @param stream An InputStream that will pass in the stylesheet contents
+     * @param name The name of the translet class to generate
+     * @return 'true' if the compilation was successful
+     */
+    public boolean compile(InputStream stream, String name) {
+        final InputSource input = new InputSource(stream);
+        input.setSystemId(name); // We have nothing else!!!
+        return compile(input, name);
+    }
+
+    /**
+     * Compiles an XSL stylesheet passed in through an InputStream
+     * @param input An InputSource that will pass in the stylesheet contents
+     * @param name The name of the translet class to generate - can be null
+     * @return 'true' if the compilation was successful
+     */
+    public boolean compile(InputSource input, String name) {
+        try {
+            // Reset globals in case we're called by compile(Vector v);
+            reset();
+
+            // The systemId may not be set, so we'll have to check the URL
+            String systemId = null;
+            if (input != null) {
+                systemId = input.getSystemId();
+            }
+
+            // Set the translet class name if not already set
+            if (_className == null) {
+                if (name != null) {
+                    setClassName(name);
+                }
+                else if (systemId != null && !systemId.equals("")) {
+                    setClassName(Util.baseName(systemId));
+                }
+
+                // Ensure we have a non-empty class name at this point
+                if (_className == null || _className.length() == 0) {
+                    setClassName("GregorSamsa"); // default translet name
+                }
+            }
+
+            // Get the root node of the abstract syntax tree
+            SyntaxTreeNode element = null;
+            if (_reader == null) {
+                element = _parser.parse(input);
+            }
+            else {
+                element = _parser.parse(_reader, input);
+            }
+
+            // Compile the translet - this is where the work is done!
+            if ((!_parser.errorsFound()) && (element != null)) {
+                // Create a Stylesheet element from the root node
+                _stylesheet = _parser.makeStylesheet(element);
+                _stylesheet.setSourceLoader(_loader);
+                _stylesheet.setSystemId(systemId);
+                _stylesheet.setParentStylesheet(null);
+                _stylesheet.setTemplateInlining(_templateInlining);
+                _parser.setCurrentStylesheet(_stylesheet);
+
+                // Create AST under the Stylesheet element (parse & type-check)
+                _parser.createAST(_stylesheet);
+            }
+            // Generate the bytecodes and output the translet class(es)
+            if ((!_parser.errorsFound()) && (_stylesheet != null)) {
+                _stylesheet.setCallsNodeset(_callsNodeset);
+                _stylesheet.setMultiDocument(_multiDocument);
+                _stylesheet.setHasIdCall(_hasIdCall);
+
+                // Class synchronization is needed for BCEL
+                synchronized (getClass()) {
+                    _stylesheet.translate();
+                }
+            }
+        }
+        catch (Exception e) {
+            /*if (_debug)*/ e.printStackTrace();
+            _parser.reportError(Constants.FATAL, new ErrorMsg(e));
+        }
+        catch (Error e) {
+            if (_debug) e.printStackTrace();
+            _parser.reportError(Constants.FATAL, new ErrorMsg(e));
+        }
+        finally {
+            _reader = null; // reset this here to be sure it is not re-used
+        }
+        return !_parser.errorsFound();
+    }
+
+    /**
+     * Compiles a set of stylesheets pointed to by a Vector of URLs
+     * @param stylesheets A Vector containing URLs pointing to the stylesheets
+     * @return 'true' if the compilation was successful
+     */
+    public boolean compile(Vector stylesheets) {
+        // Get the number of stylesheets (ie. URLs) in the vector
+        final int count = stylesheets.size();
+
+        // Return straight away if the vector is empty
+        if (count == 0) return true;
+
+        // Special handling needed if the URL count is one, becuase the
+        // _className global must not be reset if it was set explicitly
+        if (count == 1) {
+            final Object url = stylesheets.firstElement();
+            if (url instanceof URL)
+                return compile((URL)url);
+            else
+                return false;
+        }
+        else {
+            // Traverse all elements in the vector and compile
+            final Enumeration urls = stylesheets.elements();
+            while (urls.hasMoreElements()) {
+                _className = null; // reset, so that new name will be computed
+                final Object url = urls.nextElement();
+                if (url instanceof URL) {
+                    if (!compile((URL)url)) return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Returns an array of bytecode arrays generated by a compilation.
+     * @return JVM bytecodes that represent translet class definition
+     */
+    public byte[][] getBytecodes() {
+        final int count = _classes.size();
+        final byte[][] result = new byte[count][1];
+        for (int i = 0; i < count; i++)
+            result[i] = (byte[])_classes.elementAt(i);
+        return result;
+    }
+
+    /**
+     * Compiles a stylesheet pointed to by a URL. The result is put in a
+     * set of byte arrays. One byte array for each generated class.
+     * @param name The name of the translet class to generate
+     * @param input An InputSource that will pass in the stylesheet contents
+     * @param outputType The output type
+     * @return JVM bytecodes that represent translet class definition
+     */
+    public byte[][] compile(String name, InputSource input, int outputType) {
+        _outputType = outputType;
+        if (compile(input, name))
+            return getBytecodes();
+        else
+            return null;
+    }
+
+    /**
+     * Compiles a stylesheet pointed to by a URL. The result is put in a
+     * set of byte arrays. One byte array for each generated class.
+     * @param name The name of the translet class to generate
+     * @param input An InputSource that will pass in the stylesheet contents
+     * @return JVM bytecodes that represent translet class definition
+     */
+    public byte[][] compile(String name, InputSource input) {
+        return compile(name, input, BYTEARRAY_OUTPUT);
+    }
+
+    /**
+     * Set the XMLReader to use for parsing the next input stylesheet
+     * @param reader XMLReader (SAX2 parser) to use
+     */
+    public void setXMLReader(XMLReader reader) {
+        _reader = reader;
+    }
+
+    /**
+     * Get the XMLReader to use for parsing the next input stylesheet
+     */
+    public XMLReader getXMLReader() {
+        return _reader ;
+    }
+
+    /**
+     * Get a Vector containing all compile error messages
+     * @return A Vector containing all compile error messages
+     */
+    public Vector getErrors() {
+        return _parser.getErrors();
+    }
+
+    /**
+     * Get a Vector containing all compile warning messages
+     * @return A Vector containing all compile error messages
+     */
+    public Vector getWarnings() {
+        return _parser.getWarnings();
+    }
+
+    /**
+     * Print all compile error messages to standard output
+     */
+    public void printErrors() {
+        _parser.printErrors();
+    }
+
+    /**
+     * Print all compile warning messages to standard output
+     */
+    public void printWarnings() {
+        _parser.printWarnings();
+    }
+
+    /**
+     * This method is called by the XPathParser when it encounters a call
+     * to the document() function. Affects the DOM used by the translet.
+     */
+    protected void setMultiDocument(boolean flag) {
+        _multiDocument = flag;
+    }
+
+    public boolean isMultiDocument() {
+        return _multiDocument;
+    }
+
+    /**
+     * This method is called by the XPathParser when it encounters a call
+     * to the nodeset() extension function. Implies multi document.
+     */
+    protected void setCallsNodeset(boolean flag) {
+        if (flag) setMultiDocument(flag);
+        _callsNodeset = flag;
+    }
+
+    public boolean callsNodeset() {
+        return _callsNodeset;
+    }
+
+    protected void setHasIdCall(boolean flag) {
+        _hasIdCall = flag;
+    }
+
+    public boolean hasIdCall() {
+        return _hasIdCall;
+    }
+
+    /**
+     * Set the class name for the generated translet. This class name is
+     * overridden if multiple stylesheets are compiled in one go using the
+     * compile(Vector urls) method.
+     * @param className The name to assign to the translet class
+     */
+    public void setClassName(String className) {
+        final String base  = Util.baseName(className);
+        final String noext = Util.noExtName(base);
+        String name  = Util.toJavaName(noext);
+
+        if (_packageName == null)
+            _className = name;
+        else
+            _className = _packageName + '.' + name;
+    }
+
+    /**
+     * Get the class name for the generated translet.
+     */
+    public String getClassName() {
+        return _className;
+    }
+
+    /**
+     * Convert for Java class name of local system file name.
+     * (Replace '.' with '/' on UNIX and replace '.' by '\' on Windows/DOS.)
+     */
+    private String classFileName(final String className) {
+        return className.replace('.', File.separatorChar) + ".class";
+    }
+
+    /**
+     * Generate an output File object to send the translet to
+     */
+    private File getOutputFile(String className) {
+        if (_destDir != null)
+            return new File(_destDir, classFileName(className));
+        else
+            return new File(classFileName(className));
+    }
+
+    /**
+     * Set the destination directory for the translet.
+     * The current working directory will be used by default.
+     */
+    public boolean setDestDirectory(String dstDirName) {
+        final File dir = new File(dstDirName);
+        if (dir.exists() || dir.mkdirs()) {
+            _destDir = dir;
+            return true;
+        }
+        else {
+            _destDir = null;
+            return false;
+        }
+    }
+
+    /**
+     * Set an optional package name for the translet and auxiliary classes
+     */
+    public void setPackageName(String packageName) {
+        _packageName = packageName;
+        if (_className != null) setClassName(_className);
+    }
+
+    /**
+     * Set the name of an optional JAR-file to dump the translet and
+     * auxiliary classes to
+     */
+    public void setJarFileName(String jarFileName) {
+        final String JAR_EXT = ".jar";
+        if (jarFileName.endsWith(JAR_EXT))
+            _jarFileName = jarFileName;
+        else
+            _jarFileName = jarFileName + JAR_EXT;
+        _outputType = JAR_OUTPUT;
+    }
+
+    public String getJarFileName() {
+        return _jarFileName;
+    }
+
+    /**
+     * Set the top-level stylesheet
+     */
+    public void setStylesheet(Stylesheet stylesheet) {
+        if (_stylesheet == null) _stylesheet = stylesheet;
+    }
+
+    /**
+     * Returns the top-level stylesheet
+     */
+    public Stylesheet getStylesheet() {
+        return _stylesheet;
+    }
+
+    /**
+     * 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.toString());
+        if (code == null) {
+            code = new Integer(_nextGType++);
+            _attributes.put(name.toString(), code);
+            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 prefix and gives it a type so that it can be mapped to
+      * DOM namespace types at run-time.
+      */
+
+    public int registerNamespacePrefix(QName name) {
+
+    Integer code = (Integer)_namespacePrefixes.get(name.toString());
+    if (code == null) {
+        code = new Integer(_nextGType++);
+        _namespacePrefixes.put(name.toString(), code);
+        final String uri = name.getNamespace();
+        if ((uri != null) && (!uri.equals(""))){
+            // namespace::ext2:ped2 will be made empty in TypedNamespaceIterator
+            _namesIndex.addElement("?");
+        } else{
+           _namesIndex.addElement("?"+name.getLocalPart());
+        }
+    }
+    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();
+    }
+
+    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++;
+    }
+
+    public Vector getNamesIndex() {
+        return _namesIndex;
+    }
+
+    public Vector getNamespaceIndex() {
+        return _namespaceIndex;
+    }
+
+    /**
+     * Returns a unique name for every helper class needed to
+     * execute a translet.
+     */
+    public String getHelperClassName() {
+        return getClassName() + '$' + _helperClassSerial++;
+    }
+
+    public void dumpClass(JavaClass clazz) {
+
+        if (_outputType == FILE_OUTPUT ||
+            _outputType == BYTEARRAY_AND_FILE_OUTPUT)
+        {
+            File outFile = getOutputFile(clazz.getClassName());
+            String parentDir = outFile.getParent();
+            if (parentDir != null) {
+                File parentFile = new File(parentDir);
+                if (!parentFile.exists())
+                    parentFile.mkdirs();
+            }
+        }
+
+        try {
+            switch (_outputType) {
+            case FILE_OUTPUT:
+                clazz.dump(
+                    new BufferedOutputStream(
+                        new FileOutputStream(
+                            getOutputFile(clazz.getClassName()))));
+                break;
+            case JAR_OUTPUT:
+                _bcelClasses.addElement(clazz);
+                break;
+            case BYTEARRAY_OUTPUT:
+            case BYTEARRAY_AND_FILE_OUTPUT:
+            case BYTEARRAY_AND_JAR_OUTPUT:
+            case CLASSLOADER_OUTPUT:
+                ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
+                clazz.dump(out);
+                _classes.addElement(out.toByteArray());
+
+                if (_outputType == BYTEARRAY_AND_FILE_OUTPUT)
+                  clazz.dump(new BufferedOutputStream(
+                        new FileOutputStream(getOutputFile(clazz.getClassName()))));
+                else if (_outputType == BYTEARRAY_AND_JAR_OUTPUT)
+                  _bcelClasses.addElement(clazz);
+
+                break;
+            }
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * File separators are converted to forward slashes for ZIP files.
+     */
+    private String entryName(File f) throws IOException {
+        return f.getName().replace(File.separatorChar, '/');
+    }
+
+    /**
+     * Generate output JAR-file and packages
+     */
+    public void outputToJar() throws IOException {
+        // create the manifest
+        final Manifest manifest = new Manifest();
+        final java.util.jar.Attributes atrs = manifest.getMainAttributes();
+        atrs.put(java.util.jar.Attributes.Name.MANIFEST_VERSION,"1.2");
+
+        final Map map = manifest.getEntries();
+        // create manifest
+        Enumeration classes = _bcelClasses.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 String className = clazz.getClassName().replace('.','/');
+            final java.util.jar.Attributes attr = new java.util.jar.Attributes();
+            attr.put(dateAttr, now);
+            map.put(className+".class", attr);
+        }
+
+        final File jarFile = new File(_destDir, _jarFileName);
+        final JarOutputStream jos =
+            new JarOutputStream(new FileOutputStream(jarFile), manifest);
+        classes = _bcelClasses.elements();
+        while (classes.hasMoreElements()) {
+            final JavaClass clazz = (JavaClass)classes.nextElement();
+            final String className = clazz.getClassName().replace('.','/');
+            jos.putNextEntry(new JarEntry(className+".class"));
+            final ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
+            clazz.dump(out); // dump() closes it's output stream
+            out.writeTo(jos);
+        }
+        jos.close();
+    }
+
+    /**
+     * Turn debugging messages on/off
+     */
+    public void setDebug(boolean debug) {
+        _debug = debug;
+    }
+
+    /**
+     * Get current debugging message setting
+     */
+    public boolean debug() {
+        return _debug;
+    }
+
+
+    /**
+     * Retrieve a string representation of the character data to be stored
+     * in the translet as a <code>char[]</code>.  There may be more than
+     * one such array required.
+     * @param index The index of the <code>char[]</code>.  Zero-based.
+     * @return String The character data to be stored in the corresponding
+     *               <code>char[]</code>.
+     */
+    public String getCharacterData(int index) {
+        return ((StringBuffer) m_characterData.elementAt(index)).toString();
+    }
+
+    /**
+     * Get the number of char[] arrays, thus far, that will be created to
+     * store literal text in the stylesheet.
+     */
+    public int getCharacterDataCount() {
+        return (m_characterData != null) ? m_characterData.size() : 0;
+    }
+
+    /**
+     * Add literal text to char arrays that will be used to store character
+     * data in the stylesheet.
+     * @param newData String data to be added to char arrays.
+     *                Pre-condition:  <code>newData.length() &le; 21845</code>
+     * @return int offset at which character data will be stored
+     */
+    public int addCharacterData(String newData) {
+        StringBuffer currData;
+        if (m_characterData == null) {
+            m_characterData = new Vector();
+            currData = new StringBuffer();
+            m_characterData.addElement(currData);
+        } else {
+            currData = (StringBuffer) m_characterData
+                                           .elementAt(m_characterData.size()-1);
+        }
+
+        // Character data could take up to three-times as much space when
+        // written to the class file as UTF-8.  The maximum size for a
+        // constant is 65535/3.  If we exceed that,
+        // (We really should use some "bin packing".)
+        if (newData.length() + currData.length() > 21845) {
+            currData = new StringBuffer();
+            m_characterData.addElement(currData);
+        }
+
+        int newDataOffset = currData.length();
+        currData.append(newData);
+
+        return newDataOffset;
+    }
+}