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