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