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