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