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