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