001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: XSLTC.java 1225366 2011-12-28 22:49:12Z mrglavas $
020     */
021    
022    package org.apache.xalan.xsltc.compiler;
023    
024    import java.io.BufferedOutputStream;
025    import java.io.ByteArrayOutputStream;
026    import java.io.File;
027    import java.io.FileOutputStream;
028    import java.io.IOException;
029    import java.io.InputStream;
030    import java.net.URL;
031    import java.util.Date;
032    import java.util.Enumeration;
033    import java.util.Hashtable;
034    import java.util.Iterator;
035    import java.util.Map;
036    import java.util.Properties;
037    import java.util.Vector;
038    import java.util.jar.JarEntry;
039    import java.util.jar.JarOutputStream;
040    import java.util.jar.Manifest;
041    
042    import org.apache.bcel.classfile.JavaClass;
043    import org.apache.xalan.xsltc.compiler.util.ErrorMsg;
044    import org.apache.xalan.xsltc.compiler.util.Util;
045    import org.apache.xml.dtm.DTM;
046    
047    import org.xml.sax.InputSource;
048    import org.xml.sax.XMLReader;
049    
050    /**
051     * @author Jacek Ambroziak
052     * @author Santiago Pericas-Geertsen
053     * @author G. Todd Miller
054     * @author Morten Jorgensen
055     * @author John Howard (johnh@schemasoft.com)
056     */
057    public final class XSLTC {
058    
059        // A reference to the main stylesheet parser object.
060        private Parser _parser;
061    
062        // A reference to an external XMLReader (SAX parser) passed to us
063        private XMLReader _reader = null;
064    
065        // A reference to an external SourceLoader (for use with include/import)
066        private SourceLoader _loader = null;
067    
068        // A reference to the stylesheet being compiled.
069        private Stylesheet _stylesheet;
070    
071        // Counters used by various classes to generate unique names.
072        // private int _variableSerial     = 1;
073        private int _modeSerial         = 1;
074        private int _stylesheetSerial   = 1;
075        private int _stepPatternSerial  = 1;
076        private int _helperClassSerial  = 0;
077        private int _attributeSetSerial = 0;
078    
079        private int[] _numberFieldIndexes;
080    
081        // Name index tables
082        private int       _nextGType;  // Next available element type
083        private Vector    _namesIndex; // Index of all registered QNames
084        private Hashtable _elements;   // Hashtable of all registered elements
085        private Hashtable _attributes; // Hashtable of all registered attributes
086    
087        // Namespace index tables
088        private int       _nextNSType; // Next available namespace type
089        private Vector    _namespaceIndex; // Index of all registered namespaces
090        private Hashtable _namespaces; // Hashtable of all registered namespaces
091        private Hashtable _namespacePrefixes;// Hashtable of all registered namespace prefixes
092    
093    
094        // All literal text in the stylesheet
095        private Vector m_characterData;
096    
097        // These define the various methods for outputting the translet
098        public static final int FILE_OUTPUT        = 0;
099        public static final int JAR_OUTPUT         = 1;
100        public static final int BYTEARRAY_OUTPUT   = 2;
101        public static final int CLASSLOADER_OUTPUT = 3;
102        public static final int BYTEARRAY_AND_FILE_OUTPUT = 4;
103        public static final int BYTEARRAY_AND_JAR_OUTPUT  = 5;
104    
105    
106        // Compiler options (passed from command line or XSLTC client)
107        private boolean _debug = false;      // -x
108        private String  _jarFileName = null; // -j <jar-file-name>
109        private String  _className = null;   // -o <class-name>
110        private String  _packageName = null; // -p <package-name>
111        private File    _destDir = null;     // -d <directory-name>
112        private int     _outputType = FILE_OUTPUT; // by default
113    
114        private Vector  _classes;
115        private Vector  _bcelClasses;
116        private boolean _callsNodeset = false;
117        private boolean _multiDocument = false;
118        private boolean _hasIdCall = false;
119    
120        private Vector _stylesheetNSAncestorPointers;
121        private Vector _prefixURIPairs;
122        private Vector _prefixURIPairsIdx;
123    
124        /**
125         * Set to true if template inlining is requested. Template
126         * inlining used to be the default, but we have found that
127         * Hotspots does a better job with shorter methods, so the
128         * default is *not* to inline now.
129         */
130        private boolean _templateInlining = false;
131    
132        /**
133         * State of the secure processing feature.
134         */
135        private boolean _isSecureProcessing = false;
136    
137        /**
138         * XSLTC compiler constructor
139         */
140        public XSLTC() {
141            _parser = new Parser(this);
142        }
143        
144        /**
145         * Set the state of the secure processing feature.
146         */
147        public void setSecureProcessing(boolean flag) {
148            _isSecureProcessing = flag;
149        }
150        
151        /**
152         * Return the state of the secure processing feature.
153         */
154        public boolean isSecureProcessing() {
155            return _isSecureProcessing;
156        }
157    
158        /**
159         * Only for user by the internal TrAX implementation.
160         */
161        public Parser getParser() {
162            return _parser;
163        }
164    
165        /**
166         * Only for user by the internal TrAX implementation.
167         */
168        public void setOutputType(int type) {
169            _outputType = type;
170        }
171    
172        /**
173         * Only for user by the internal TrAX implementation.
174         */
175        public Properties getOutputProperties() {
176            return _parser.getOutputProperties();
177        }
178    
179        /**
180         * Initializes the compiler to compile a new stylesheet
181         */
182        public void init() {
183            reset();
184            _reader = null;
185            _classes = new Vector();
186            _bcelClasses = new Vector();
187        }
188    
189        /**
190         * Initializes the compiler to produce a new translet
191         */
192        private void reset() {
193            _nextGType      = DTM.NTYPES;
194            _elements       = new Hashtable();
195            _attributes     = new Hashtable();
196            _namespaces     = new Hashtable();
197            _namespaces.put("",new Integer(_nextNSType));
198            _namesIndex     = new Vector(128);
199            _namespaceIndex = new Vector(32);
200            _namespacePrefixes = new Hashtable();
201            _stylesheet     = null;
202            _parser.init();
203            //_variableSerial     = 1;
204            _modeSerial         = 1;
205            _stylesheetSerial   = 1;
206            _stepPatternSerial  = 1;
207            _helperClassSerial  = 0;
208            _attributeSetSerial = 0;
209            _multiDocument      = false;
210            _hasIdCall          = false;
211            _stylesheetNSAncestorPointers = null;
212            _prefixURIPairs     = null;
213            _prefixURIPairsIdx  = null;
214            _numberFieldIndexes = new int[] {
215                -1,         // LEVEL_SINGLE
216                -1,         // LEVEL_MULTIPLE
217                -1          // LEVEL_ANY
218            };
219        }
220    
221        /**
222         * Defines an external SourceLoader to provide the compiler with documents
223         * referenced in xsl:include/import
224         * @param loader The SourceLoader to use for include/import
225         */
226        public void setSourceLoader(SourceLoader loader) {
227            _loader = loader;
228        }
229    
230        /**
231         * Set a flag indicating if templates are to be inlined or not. The
232         * default is to do inlining, but this causes problems when the
233         * stylesheets have a large number of templates (e.g. branch targets
234         * exceeding 64K or a length of a method exceeding 64K).
235         */
236        public void setTemplateInlining(boolean templateInlining) {
237            _templateInlining = templateInlining;
238        }
239    
240        /**
241         * Return the state of the template inlining feature.
242         */
243        public boolean getTemplateInlining() {
244            return _templateInlining;
245        }
246    
247        /**
248         * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
249         * processing instruction in the case where the input document to the
250         * compiler (and parser) is an XML document.
251         * @param media The media attribute to be matched. May be null, in which
252         * case the prefered templates will be used (i.e. alternate = no).
253         * @param title The value of the title attribute to match. May be null.
254         * @param charset The value of the charset attribute to match. May be null.
255         */
256        public void setPIParameters(String media, String title, String charset) {
257            _parser.setPIParameters(media, title, charset);
258        }
259    
260        /**
261         * Compiles an XSL stylesheet pointed to by a URL
262         * @param url An URL containing the input XSL stylesheet
263         */
264        public boolean compile(URL url) {
265            try {
266                // Open input stream from URL and wrap inside InputSource
267                final InputStream stream = url.openStream();
268                final InputSource input = new InputSource(stream);
269                input.setSystemId(url.toString());
270                return compile(input, _className);
271            }
272            catch (IOException e) {
273                _parser.reportError(Constants.FATAL, new ErrorMsg(e));
274                return false;
275            }
276        }
277    
278        /**
279         * Compiles an XSL stylesheet pointed to by a URL
280         * @param url An URL containing the input XSL stylesheet
281         * @param name The name to assign to the translet class
282         */
283        public boolean compile(URL url, String name) {
284            try {
285                // Open input stream from URL and wrap inside InputSource
286                final InputStream stream = url.openStream();
287                final InputSource input = new InputSource(stream);
288                input.setSystemId(url.toString());
289                return compile(input, name);
290            }
291            catch (IOException e) {
292                _parser.reportError(Constants.FATAL, new ErrorMsg(e));
293                return false;
294            }
295        }
296    
297        /**
298         * Compiles an XSL stylesheet passed in through an InputStream
299         * @param stream An InputStream that will pass in the stylesheet contents
300         * @param name The name of the translet class to generate
301         * @return 'true' if the compilation was successful
302         */
303        public boolean compile(InputStream stream, String name) {
304            final InputSource input = new InputSource(stream);
305            input.setSystemId(name); // We have nothing else!!!
306            return compile(input, name);
307        }
308    
309        /**
310         * Compiles an XSL stylesheet passed in through an InputStream
311         * @param input An InputSource that will pass in the stylesheet contents
312         * @param name The name of the translet class to generate - can be null
313         * @return 'true' if the compilation was successful
314         */
315        public boolean compile(InputSource input, String name) {
316            try {
317                // Reset globals in case we're called by compile(Vector v);
318                reset();
319    
320                // The systemId may not be set, so we'll have to check the URL
321                String systemId = null;
322                if (input != null) {
323                    systemId = input.getSystemId();
324                }
325    
326                // Set the translet class name if not already set
327                if (_className == null) {
328                    if (name != null) {
329                        setClassName(name);
330                    }
331                    else if (systemId != null && systemId.length() != 0) {
332                        setClassName(Util.baseName(systemId));
333                    }
334                    
335                    // Ensure we have a non-empty class name at this point
336                    if (_className == null || _className.length() == 0) {
337                        setClassName("GregorSamsa"); // default translet name
338                    }
339                }
340    
341                // Get the root node of the abstract syntax tree
342                SyntaxTreeNode element = null;
343                if (_reader == null) {
344                    element = _parser.parse(input);
345                }
346                else {
347                    element = _parser.parse(_reader, input);
348                }
349    
350                // Compile the translet - this is where the work is done!
351                if ((!_parser.errorsFound()) && (element != null)) {
352                    // Create a Stylesheet element from the root node
353                    _stylesheet = _parser.makeStylesheet(element);
354                    _stylesheet.setSourceLoader(_loader);
355                    _stylesheet.setSystemId(systemId);
356                    _stylesheet.setParentStylesheet(null);
357                    _stylesheet.setTemplateInlining(_templateInlining);
358                    _parser.setCurrentStylesheet(_stylesheet);
359    
360                    // Create AST under the Stylesheet element (parse & type-check)
361                    _parser.createAST(_stylesheet);
362                }
363                // Generate the bytecodes and output the translet class(es)
364                if ((!_parser.errorsFound()) && (_stylesheet != null)) {
365                    _stylesheet.setCallsNodeset(_callsNodeset);
366                    _stylesheet.setMultiDocument(_multiDocument);
367                    _stylesheet.setHasIdCall(_hasIdCall);
368    
369                    // Class synchronization is needed for BCEL
370                    synchronized (getClass()) {
371                        _stylesheet.translate();
372                    }
373                }
374            }
375            catch (Exception e) {
376                /*if (_debug)*/ e.printStackTrace();
377                _parser.reportError(Constants.FATAL, new ErrorMsg(e));
378            }
379            catch (Error e) {
380                if (_debug) e.printStackTrace();
381                _parser.reportError(Constants.FATAL, new ErrorMsg(e));
382            }
383            finally {
384                _reader = null; // reset this here to be sure it is not re-used
385            }
386            return !_parser.errorsFound();
387        }
388    
389        /**
390         * Compiles a set of stylesheets pointed to by a Vector of URLs
391         * @param stylesheets A Vector containing URLs pointing to the stylesheets
392         * @return 'true' if the compilation was successful
393         */
394        public boolean compile(Vector stylesheets) {
395            // Get the number of stylesheets (ie. URLs) in the vector
396            final int count = stylesheets.size();
397    
398            // Return straight away if the vector is empty
399            if (count == 0) return true;
400    
401            // Special handling needed if the URL count is one, becuase the
402            // _className global must not be reset if it was set explicitly
403            if (count == 1) {
404                final Object url = stylesheets.firstElement();
405                if (url instanceof URL)
406                    return compile((URL)url);
407                else
408                    return false;
409            }
410            else {
411                // Traverse all elements in the vector and compile
412                final Enumeration urls = stylesheets.elements();
413                while (urls.hasMoreElements()) {
414                    _className = null; // reset, so that new name will be computed
415                    final Object url = urls.nextElement();
416                    if (url instanceof URL) {
417                        if (!compile((URL)url)) return false;
418                    }
419                }
420            }
421            return true;
422        }
423    
424        /**
425         * Returns an array of bytecode arrays generated by a compilation.
426         * @return JVM bytecodes that represent translet class definition
427         */
428        public byte[][] getBytecodes() {
429            final int count = _classes.size();
430            final byte[][] result = new byte[count][1];
431            for (int i = 0; i < count; i++)
432                result[i] = (byte[])_classes.elementAt(i);
433            return result;
434        }
435    
436        /**
437         * Compiles a stylesheet pointed to by a URL. The result is put in a
438         * set of byte arrays. One byte array for each generated class.
439         * @param name The name of the translet class to generate
440         * @param input An InputSource that will pass in the stylesheet contents
441         * @param outputType The output type
442         * @return JVM bytecodes that represent translet class definition
443         */
444        public byte[][] compile(String name, InputSource input, int outputType) {
445            _outputType = outputType;
446            if (compile(input, name))
447                return getBytecodes();
448            else
449                return null;
450        }
451    
452        /**
453         * Compiles a stylesheet pointed to by a URL. The result is put in a
454         * set of byte arrays. One byte array for each generated class.
455         * @param name The name of the translet class to generate
456         * @param input An InputSource that will pass in the stylesheet contents
457         * @return JVM bytecodes that represent translet class definition
458         */
459        public byte[][] compile(String name, InputSource input) {
460            return compile(name, input, BYTEARRAY_OUTPUT);
461        }
462    
463        /**
464         * Set the XMLReader to use for parsing the next input stylesheet
465         * @param reader XMLReader (SAX2 parser) to use
466         */
467        public void setXMLReader(XMLReader reader) {
468            _reader = reader;
469        }
470    
471        /**
472         * Get the XMLReader to use for parsing the next input stylesheet
473         */
474        public XMLReader getXMLReader() {
475            return _reader ;
476        }
477    
478        /**
479         * Get a Vector containing all compile error messages
480         * @return A Vector containing all compile error messages
481         */
482        public Vector getErrors() {
483            return _parser.getErrors();
484        }
485    
486        /**
487         * Get a Vector containing all compile warning messages
488         * @return A Vector containing all compile error messages
489         */
490        public Vector getWarnings() {
491            return _parser.getWarnings();
492        }
493    
494        /**
495         * Print all compile error messages to standard output
496         */
497        public void printErrors() {
498            _parser.printErrors();
499        }
500    
501        /**
502         * Print all compile warning messages to standard output
503         */
504        public void printWarnings() {
505            _parser.printWarnings();
506        }
507    
508        /**
509         * This method is called by the XPathParser when it encounters a call
510         * to the document() function. Affects the DOM used by the translet.
511         */
512        protected void setMultiDocument(boolean flag) {
513            _multiDocument = flag;
514        }
515    
516        public boolean isMultiDocument() {
517            return _multiDocument;
518        }
519    
520        /**
521         * This method is called by the XPathParser when it encounters a call
522         * to the nodeset() extension function. Implies multi document.
523         */
524        protected void setCallsNodeset(boolean flag) {
525            if (flag) setMultiDocument(flag);
526            _callsNodeset = flag;
527        }
528    
529        public boolean callsNodeset() {
530            return _callsNodeset;
531        }
532    
533        protected void setHasIdCall(boolean flag) {
534            _hasIdCall = flag;
535        }
536    
537        public boolean hasIdCall() {
538            return _hasIdCall;
539        }
540    
541        /**
542         * Set the class name for the generated translet. This class name is
543         * overridden if multiple stylesheets are compiled in one go using the
544         * compile(Vector urls) method.
545         * @param className The name to assign to the translet class
546         */
547        public void setClassName(String className) {
548            final String base  = Util.baseName(className);
549            final String noext = Util.noExtName(base);
550            String name  = Util.toJavaName(noext);
551    
552            if (_packageName == null)
553                _className = name;
554            else
555                _className = _packageName + '.' + name;
556        }
557    
558        /**
559         * Get the class name for the generated translet.
560         */
561        public String getClassName() {
562            return _className;
563        }
564    
565        /**
566         * Convert for Java class name of local system file name.
567         * (Replace '.' with '/' on UNIX and replace '.' by '\' on Windows/DOS.)
568         */
569        private String classFileName(final String className) {
570            return className.replace('.', File.separatorChar) + ".class";
571        }
572    
573        /**
574         * Generate an output File object to send the translet to
575         */
576        private File getOutputFile(String className) {
577            if (_destDir != null)
578                return new File(_destDir, classFileName(className));
579            else
580                return new File(classFileName(className));
581        }
582    
583        /**
584         * Set the destination directory for the translet.
585         * The current working directory will be used by default.
586         */
587        public boolean setDestDirectory(String dstDirName) {
588            final File dir = new File(dstDirName);
589            if (dir.exists() || dir.mkdirs()) {
590                _destDir = dir;
591                return true;
592            }
593            else {
594                _destDir = null;
595                return false;
596            }
597        }
598    
599        /**
600         * Set an optional package name for the translet and auxiliary classes
601         */
602        public void setPackageName(String packageName) {
603            _packageName = packageName;
604            if (_className != null) setClassName(_className);
605        }
606    
607        /**
608         * Set the name of an optional JAR-file to dump the translet and
609         * auxiliary classes to
610         */
611        public void setJarFileName(String jarFileName) {
612            final String JAR_EXT = ".jar";
613            if (jarFileName.endsWith(JAR_EXT))
614                _jarFileName = jarFileName;
615            else
616                _jarFileName = jarFileName + JAR_EXT;
617            _outputType = JAR_OUTPUT;
618        }
619    
620        public String getJarFileName() {
621            return _jarFileName;
622        }
623    
624        /**
625         * Set the top-level stylesheet
626         */
627        public void setStylesheet(Stylesheet stylesheet) {
628            if (_stylesheet == null) _stylesheet = stylesheet;
629        }
630    
631        /**
632         * Returns the top-level stylesheet
633         */
634        public Stylesheet getStylesheet() {
635            return _stylesheet;
636        }
637    
638        /**
639         * Registers an attribute and gives it a type so that it can be mapped to
640         * DOM attribute types at run-time.
641         */
642        public int registerAttribute(QName name) {
643            Integer code = (Integer)_attributes.get(name.toString());
644            if (code == null) {
645                code = new Integer(_nextGType++);
646                _attributes.put(name.toString(), code);
647                final String uri = name.getNamespace();
648                final String local = "@"+name.getLocalPart();
649                if ((uri != null) && (uri.length() != 0))
650                    _namesIndex.addElement(uri+":"+local);
651                else
652                    _namesIndex.addElement(local);
653                if (name.getLocalPart().equals("*")) {
654                    registerNamespace(name.getNamespace());
655                }
656            }
657            return code.intValue();
658        }
659    
660        /**
661         * Registers an element and gives it a type so that it can be mapped to
662         * DOM element types at run-time.
663         */
664        public int registerElement(QName name) {
665            // Register element (full QName)
666            Integer code = (Integer)_elements.get(name.toString());
667            if (code == null) {
668                _elements.put(name.toString(), code = new Integer(_nextGType++));
669                _namesIndex.addElement(name.toString());
670            }
671            if (name.getLocalPart().equals("*")) {
672                registerNamespace(name.getNamespace());
673            }
674            return code.intValue();
675        }
676    
677         /** 
678          * Registers a namespace prefix and gives it a type so that it can be mapped to
679          * DOM namespace types at run-time.
680          */
681      
682        public int registerNamespacePrefix(QName name) {
683        
684        Integer code = (Integer)_namespacePrefixes.get(name.toString());
685        if (code == null) {   
686            code = new Integer(_nextGType++);
687            _namespacePrefixes.put(name.toString(), code); 
688            final String uri = name.getNamespace();
689            if ((uri != null) && (uri.length() != 0)){
690                // namespace::ext2:ped2 will be made empty in TypedNamespaceIterator
691                _namesIndex.addElement("?"); 
692            } else{        
693               _namesIndex.addElement("?"+name.getLocalPart());        
694            }
695        }   
696        return code.intValue();
697        }
698    
699        /**
700         * Registers a namespace and gives it a type so that it can be mapped to
701         * DOM namespace types at run-time.
702         */
703        public int registerNamespacePrefix(String name) {
704            Integer code = (Integer)_namespacePrefixes.get(name);
705            if (code == null) {   
706                code = new Integer(_nextGType++);
707                _namespacePrefixes.put(name, code);
708                _namesIndex.addElement("?"+name);
709            }
710            return code.intValue();
711        }
712    
713        /**
714         * Registers a namespace and gives it a type so that it can be mapped to
715         * DOM namespace types at run-time.
716         */
717        public int registerNamespace(String namespaceURI) {
718            Integer code = (Integer)_namespaces.get(namespaceURI);
719            if (code == null) {
720                code = new Integer(_nextNSType++);
721                _namespaces.put(namespaceURI,code);
722                _namespaceIndex.addElement(namespaceURI);
723            }
724            return code.intValue();
725        }
726    
727        /**
728         * Registers namespace declarations that the stylesheet might need to
729         * look up dynamically - for instance, if an <code>xsl:element</code> has a
730         * a <code>name</code> attribute with variable parts and has no
731         * <code>namespace</code> attribute.
732         * 
733         * @param prefixMap a <code>Hashtable</code> mapping namespace prefixes to
734         *                  URIs.  Must not be <code>null</code>.  The default
735         *                  namespace and namespace undeclarations are represented
736         *                  by a zero-length string.
737         * @param ancestorID The <code>int</code> node ID of the nearest ancestor in
738         *                 the stylesheet that declares namespaces, or a value less
739         *                 than zero if there is no such ancestor
740         * @return A new node ID for the stylesheet element 
741         */
742        public int registerStylesheetPrefixMappingForRuntime(Hashtable prefixMap,
743                                                             int ancestorID) {
744            if (_stylesheetNSAncestorPointers == null) {
745                _stylesheetNSAncestorPointers = new Vector();
746            }
747    
748            if (_prefixURIPairs == null) {
749                _prefixURIPairs = new Vector();
750            }
751    
752            if (_prefixURIPairsIdx == null) {
753                _prefixURIPairsIdx = new Vector();
754            }
755    
756            int currentNodeID = _stylesheetNSAncestorPointers.size();
757            _stylesheetNSAncestorPointers.add(new Integer(ancestorID));
758    
759            Iterator prefixMapIterator = prefixMap.entrySet().iterator();
760            int prefixNSPairStartIdx = _prefixURIPairs.size();
761            _prefixURIPairsIdx.add(new Integer(prefixNSPairStartIdx));
762    
763            while (prefixMapIterator.hasNext()) {
764                Map.Entry entry = (Map.Entry) prefixMapIterator.next();
765                String prefix = (String) entry.getKey();
766                String uri = (String) entry.getValue();
767                _prefixURIPairs.add(prefix);
768                _prefixURIPairs.add(uri);
769            }
770    
771            return currentNodeID;
772        }
773    
774        public Vector getNSAncestorPointers() {
775            return _stylesheetNSAncestorPointers;
776        }
777    
778        public Vector getPrefixURIPairs() {
779            return _prefixURIPairs;
780        }
781    
782        public Vector getPrefixURIPairsIdx() {
783            return _prefixURIPairsIdx;
784        }
785    
786        public int nextModeSerial() {
787            return _modeSerial++;
788        }
789    
790        public int nextStylesheetSerial() {
791            return _stylesheetSerial++;
792        }
793    
794        public int nextStepPatternSerial() {
795            return _stepPatternSerial++;
796        }
797    
798        public int[] getNumberFieldIndexes() {
799            return _numberFieldIndexes;
800        }
801    
802        public int nextHelperClassSerial() {
803            return _helperClassSerial++;
804        }
805    
806        public int nextAttributeSetSerial() {
807            return _attributeSetSerial++;
808        }
809    
810        public Vector getNamesIndex() {
811            return _namesIndex;
812        }
813    
814        public Vector getNamespaceIndex() {
815            return _namespaceIndex;
816        }
817    
818        /**
819         * Returns a unique name for every helper class needed to
820         * execute a translet.
821         */
822        public String getHelperClassName() {
823            return getClassName() + '$' + _helperClassSerial++;
824        }
825    
826        public void dumpClass(JavaClass clazz) {
827    
828            if (_outputType == FILE_OUTPUT ||
829                _outputType == BYTEARRAY_AND_FILE_OUTPUT)
830            {
831                File outFile = getOutputFile(clazz.getClassName());
832                String parentDir = outFile.getParent();
833                if (parentDir != null) {
834                    File parentFile = new File(parentDir);
835                    if (!parentFile.exists())
836                        parentFile.mkdirs();
837                }
838            }
839    
840            try {
841                switch (_outputType) {
842                case FILE_OUTPUT:
843                    clazz.dump(
844                        new BufferedOutputStream(
845                            new FileOutputStream(
846                                getOutputFile(clazz.getClassName()))));
847                    break;
848                case JAR_OUTPUT:
849                    _bcelClasses.addElement(clazz);
850                    break;
851                case BYTEARRAY_OUTPUT:
852                case BYTEARRAY_AND_FILE_OUTPUT:
853                case BYTEARRAY_AND_JAR_OUTPUT:
854                case CLASSLOADER_OUTPUT:
855                    ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
856                    clazz.dump(out);
857                    _classes.addElement(out.toByteArray());
858    
859                    if (_outputType == BYTEARRAY_AND_FILE_OUTPUT)
860                      clazz.dump(new BufferedOutputStream(
861                            new FileOutputStream(getOutputFile(clazz.getClassName()))));
862                    else if (_outputType == BYTEARRAY_AND_JAR_OUTPUT)
863                      _bcelClasses.addElement(clazz);
864    
865                    break;
866                }
867            }
868            catch (Exception e) {
869                e.printStackTrace();
870            }
871        }
872    
873        /**
874         * File separators are converted to forward slashes for ZIP files.
875         */
876        private String entryName(File f) throws IOException {
877            return f.getName().replace(File.separatorChar, '/');
878        }
879    
880        /**
881         * Generate output JAR-file and packages
882         */
883        public void outputToJar() throws IOException {
884            // create the manifest
885            final Manifest manifest = new Manifest();
886            final java.util.jar.Attributes atrs = manifest.getMainAttributes();
887            atrs.put(java.util.jar.Attributes.Name.MANIFEST_VERSION,"1.2");
888    
889            final Map map = manifest.getEntries();
890            // create manifest
891            Enumeration classes = _bcelClasses.elements();
892            final String now = (new Date()).toString();
893            final java.util.jar.Attributes.Name dateAttr =
894                new java.util.jar.Attributes.Name("Date");
895            while (classes.hasMoreElements()) {
896                final JavaClass clazz = (JavaClass)classes.nextElement();
897                final String className = clazz.getClassName().replace('.','/');
898                final java.util.jar.Attributes attr = new java.util.jar.Attributes();
899                attr.put(dateAttr, now);
900                map.put(className+".class", attr);
901            }
902    
903            final File jarFile = new File(_destDir, _jarFileName);
904            final JarOutputStream jos =
905                new JarOutputStream(new FileOutputStream(jarFile), manifest);
906            classes = _bcelClasses.elements();
907            while (classes.hasMoreElements()) {
908                final JavaClass clazz = (JavaClass)classes.nextElement();
909                final String className = clazz.getClassName().replace('.','/');
910                jos.putNextEntry(new JarEntry(className+".class"));
911                final ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
912                clazz.dump(out); // dump() closes it's output stream
913                out.writeTo(jos);
914            }
915            jos.close();
916        }
917    
918        /**
919         * Turn debugging messages on/off
920         */
921        public void setDebug(boolean debug) {
922            _debug = debug;
923        }
924    
925        /**
926         * Get current debugging message setting
927         */
928        public boolean debug() {
929            return _debug;
930        }
931    
932    
933        /**
934         * Retrieve a string representation of the character data to be stored
935         * in the translet as a <code>char[]</code>.  There may be more than
936         * one such array required.
937         * @param index The index of the <code>char[]</code>.  Zero-based.
938         * @return String The character data to be stored in the corresponding
939         *               <code>char[]</code>.
940         */
941        public String getCharacterData(int index) {
942            return ((StringBuffer) m_characterData.elementAt(index)).toString();
943        }
944    
945        /**
946         * Get the number of char[] arrays, thus far, that will be created to
947         * store literal text in the stylesheet.
948         */
949        public int getCharacterDataCount() {
950            return (m_characterData != null) ? m_characterData.size() : 0;
951        }
952    
953        /**
954         * Add literal text to char arrays that will be used to store character
955         * data in the stylesheet.
956         * @param newData String data to be added to char arrays.
957         *                Pre-condition:  <code>newData.length() &le; 21845</code>
958         * @return int offset at which character data will be stored
959         */
960        public int addCharacterData(String newData) {
961            StringBuffer currData;
962            if (m_characterData == null) {
963                m_characterData = new Vector();
964                currData = new StringBuffer();
965                m_characterData.addElement(currData);
966            } else {
967                currData = (StringBuffer) m_characterData
968                                               .elementAt(m_characterData.size()-1);
969            }
970    
971            // Character data could take up to three-times as much space when
972            // written to the class file as UTF-8.  The maximum size for a
973            // constant is 65535/3.  If we exceed that,
974            // (We really should use some "bin packing".)
975            if (newData.length() + currData.length() > 21845) {
976                currData = new StringBuffer();
977                m_characterData.addElement(currData);
978            }
979    
980            int newDataOffset = currData.length();
981            currData.append(newData);
982    
983            return newDataOffset;
984        }
985    }