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 }