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