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