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