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