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