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