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