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