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