rev 1063 : 8172974: [JAXP] XALAN: Wrong result when transforming namespace unaware StAX Input

   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 _modeSerial         = 1;
  80     private int _stylesheetSerial   = 1;
  81     private int _stepPatternSerial  = 1;
  82     private int _helperClassSerial  = 0;
  83     private int _attributeSetSerial = 0;
  84 
  85     private int[] _numberFieldIndexes;
  86 
  87     // Name index tables
  88     private int       _nextGType;  // Next available element type
  89     private Vector    _namesIndex; // Index of all registered QNames
  90     private Map<String, Integer> _elements;   // Map of all registered elements
  91     private Map<String, Integer> _attributes; // Map of all registered attributes
  92 
  93     // Namespace index tables
  94     private int       _nextNSType; // Next available namespace type
  95     private Vector    _namespaceIndex; // Index of all registered namespaces
  96     private Map<String, Integer> _namespaces; // Map of all registered namespaces
  97     private Map<String, Integer> _namespacePrefixes;// Map of all registered namespace prefixes
  98 
  99 
 100     // All literal text in the stylesheet
 101     private ArrayList<StringBuilder> m_characterData;
 102 
 103     // These define the various methods for outputting the translet
 104     public static final int FILE_OUTPUT        = 0;
 105     public static final int JAR_OUTPUT         = 1;
 106     public static final int BYTEARRAY_OUTPUT   = 2;
 107     public static final int CLASSLOADER_OUTPUT = 3;
 108     public static final int BYTEARRAY_AND_FILE_OUTPUT = 4;
 109     public static final int BYTEARRAY_AND_JAR_OUTPUT  = 5;
 110 
 111 
 112     // Compiler options (passed from command line or XSLTC client)
 113     private boolean _debug = false;      // -x
 114     private String  _jarFileName = null; // -j <jar-file-name>
 115     private String  _className = null;   // -o <class-name>
 116     private String  _packageName = "die.verwandlung"; // override with -p <package-name>
 117     private File    _destDir = null;     // -d <directory-name>
 118     private int     _outputType = FILE_OUTPUT; // by default
 119 
 120     private ArrayList<ByteArrayOutputStream>  _classes;
 121     private ArrayList<JavaClass>  _bcelClasses;
 122     private boolean _callsNodeset = false;
 123     private boolean _multiDocument = false;
 124     private boolean _hasIdCall = false;
 125 
 126     /**
 127      * Set to true if template inlining is requested. Template
 128      * inlining used to be the default, but we have found that
 129      * Hotspots does a better job with shorter methods, so the
 130      * default is *not* to inline now.
 131      */
 132     private boolean _templateInlining = false;
 133 
 134     /**
 135      * State of the secure processing feature.
 136      */
 137     private boolean _isSecureProcessing = false;
 138 
 139     private boolean _useServicesMechanism = true;
 140 
 141     /**
 142      * protocols allowed for external references set by the stylesheet processing instruction, Import and Include element.
 143      */
 144     private String _accessExternalStylesheet = XalanConstants.EXTERNAL_ACCESS_DEFAULT;
 145 
 146     /**
 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     /**
 160      * Class loader reference that will be used for external extension functions loading
 161      */
 162     private ClassLoader _extensionClassLoader;
 163 
 164     /**
 165      *  HashMap with the loaded classes
 166      */
 167     private final Map<String, Class<?>> _externalExtensionFunctions;
 168 
 169     /**
 170      * Catalog features
 171      */
 172     CatalogFeatures _catalogFeatures;
 173 
 174     /**
 175      * CDATA chunk size
 176      */
 177     int _cdataChunkSize;
 178 
 179     /**
 180      * XSLTC compiler constructor
 181      */
 182     public XSLTC(boolean useServicesMechanism, JdkXmlFeatures featureManager) {
 183         _parser = new Parser(this, useServicesMechanism);
 184         _xmlFeatures = featureManager;
 185         _extensionClassLoader = null;
 186         _externalExtensionFunctions = new HashMap<>();
 187     }
 188 
 189     /**
 190      * Set the state of the secure processing feature.
 191      */
 192     public void setSecureProcessing(boolean flag) {
 193         _isSecureProcessing = flag;
 194     }
 195 
 196     /**
 197      * Return the state of the secure processing feature.
 198      */
 199     public boolean isSecureProcessing() {
 200         return _isSecureProcessing;
 201     }
 202     /**
 203      * Return the state of the services mechanism feature.
 204      */
 205     public boolean useServicesMechnism() {
 206         return _useServicesMechanism;
 207     }
 208 
 209     /**
 210      * Set the state of the services mechanism feature.
 211      */
 212     public void setServicesMechnism(boolean flag) {
 213         _useServicesMechanism = flag;
 214     }
 215 
 216      /**
 217      * Return the value of the specified feature
 218      * @param name name of the feature
 219      * @return true if the feature is enabled, false otherwise
 220      */
 221     public boolean getFeature(JdkXmlFeatures.XmlFeature name) {
 222         return _xmlFeatures.getFeature(name);
 223     }
 224 
 225     /**
 226      * Return allowed protocols for accessing external stylesheet.
 227      * @param name the name of the property
 228      * @return the value of the property
 229      */
 230     public Object getProperty(String name) {
 231         if (name.equals(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)) {
 232             return _accessExternalStylesheet;
 233         }
 234         else if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) {
 235             return _accessExternalDTD;
 236         } else if (name.equals(XalanConstants.SECURITY_MANAGER)) {
 237             return _xmlSecurityManager;
 238         } else if (name.equals(XalanConstants.JDK_EXTENSION_CLASSLOADER)) {
 239             return _extensionClassLoader;
 240         } else if (JdkXmlFeatures.CATALOG_FEATURES.equals(name)) {
 241             return _catalogFeatures;
 242         } else if (JdkXmlUtils.CDATA_CHUNK_SIZE.equals(name)) {
 243             return _cdataChunkSize;
 244         }
 245         return null;
 246     }
 247 
 248     /**
 249      * Set allowed protocols for accessing external stylesheet.
 250      * @param name the name of the property
 251      * @param value the value of the property
 252      */
 253     public void setProperty(String name, Object value) {
 254         if (name.equals(XMLConstants.ACCESS_EXTERNAL_STYLESHEET)) {
 255             _accessExternalStylesheet = (String)value;
 256         }
 257         else if (name.equals(XMLConstants.ACCESS_EXTERNAL_DTD)) {
 258             _accessExternalDTD = (String)value;
 259         } else if (name.equals(XalanConstants.SECURITY_MANAGER)) {
 260             _xmlSecurityManager = (XMLSecurityManager)value;
 261         } else if (name.equals(XalanConstants.JDK_EXTENSION_CLASSLOADER)) {
 262             _extensionClassLoader = (ClassLoader) value;
 263             /* Clear the external extension functions HashMap if extension class
 264                loader was changed */
 265             _externalExtensionFunctions.clear();
 266         } else if (JdkXmlFeatures.CATALOG_FEATURES.equals(name)) {
 267             _catalogFeatures = (CatalogFeatures)value;
 268         } else if (JdkXmlUtils.CDATA_CHUNK_SIZE.equals(name)) {
 269             _cdataChunkSize = Integer.parseInt((String)value);
 270         }
 271     }
 272 
 273     /**
 274      * Only for user by the internal TrAX implementation.
 275      */
 276     public Parser getParser() {
 277         return _parser;
 278     }
 279 
 280     /**
 281      * Only for user by the internal TrAX implementation.
 282      */
 283     public void setOutputType(int type) {
 284         _outputType = type;
 285     }
 286 
 287     /**
 288      * Only for user by the internal TrAX implementation.
 289      */
 290     public Properties getOutputProperties() {
 291         return _parser.getOutputProperties();
 292     }
 293 
 294     /**
 295      * Initializes the compiler to compile a new stylesheet
 296      */
 297     public void init() {
 298         reset();
 299         _reader = null;
 300         _classes = new ArrayList<>();
 301         _bcelClasses = new ArrayList<>();
 302     }
 303 
 304     private void setExternalExtensionFunctions(String name, Class<?> clazz) {
 305         if (_isSecureProcessing && clazz != null && !_externalExtensionFunctions.containsKey(name)) {
 306             _externalExtensionFunctions.put(name, clazz);
 307         }
 308     }
 309 
 310     /**
 311      * Function loads an external extension function.
 312      * The filtering of function types (external,internal) takes place in FunctionCall class
 313      *
 314      */
 315     Class loadExternalFunction(String name) throws ClassNotFoundException {
 316         Class loaded = null;
 317         //Check if the function is not loaded already
 318         if (_externalExtensionFunctions.containsKey(name)) {
 319             loaded = _externalExtensionFunctions.get(name);
 320         } else if (_extensionClassLoader != null) {
 321             loaded = Class.forName(name, true, _extensionClassLoader);
 322             setExternalExtensionFunctions(name, loaded);
 323         }
 324         if (loaded == null) {
 325             throw new ClassNotFoundException(name);
 326         }
 327         //Return loaded class
 328         return (Class) loaded;
 329     }
 330 
 331     /**
 332      * Returns unmodifiable view of HashMap with loaded external extension
 333      * functions - will be needed for the TransformerImpl
 334      */
 335     public Map<String, Class<?>> getExternalExtensionFunctions() {
 336         return Collections.unmodifiableMap(_externalExtensionFunctions);
 337     }
 338 
 339     /**
 340      * Initializes the compiler to produce a new translet
 341      */
 342     private void reset() {
 343         _nextGType      = DTM.NTYPES;
 344         _elements       = new HashMap<>();
 345         _attributes     = new HashMap<>();
 346         _namespaces     = new HashMap<>();
 347         _namespaces.put("",new Integer(_nextNSType));
 348         _namesIndex     = new Vector(128);
 349         _namespaceIndex = new Vector(32);
 350         _namespacePrefixes = new HashMap<>();
 351         _stylesheet     = null;
 352         _parser.init();

 353         _modeSerial         = 1;
 354         _stylesheetSerial   = 1;
 355         _stepPatternSerial  = 1;
 356         _helperClassSerial  = 0;
 357         _attributeSetSerial = 0;
 358         _multiDocument      = false;
 359         _hasIdCall          = false;
 360         _numberFieldIndexes = new int[] {
 361             -1,         // LEVEL_SINGLE
 362             -1,         // LEVEL_MULTIPLE
 363             -1          // LEVEL_ANY
 364         };
 365         _externalExtensionFunctions.clear();
 366     }
 367 
 368     /**
 369      * Defines an external SourceLoader to provide the compiler with documents
 370      * referenced in xsl:include/import
 371      * @param loader The SourceLoader to use for include/import
 372      */
 373     public void setSourceLoader(SourceLoader loader) {
 374         _loader = loader;
 375     }
 376 
 377     /**
 378      * Set a flag indicating if templates are to be inlined or not. The
 379      * default is to do inlining, but this causes problems when the
 380      * stylesheets have a large number of templates (e.g. branch targets
 381      * exceeding 64K or a length of a method exceeding 64K).
 382      */
 383     public void setTemplateInlining(boolean templateInlining) {
 384         _templateInlining = templateInlining;
 385     }
 386      /**
 387      * Return the state of the template inlining feature.
 388      */
 389     public boolean getTemplateInlining() {
 390         return _templateInlining;
 391     }
 392 
 393     /**
 394      * Set the parameters to use to locate the correct <?xml-stylesheet ...?>
 395      * processing instruction in the case where the input document to the
 396      * compiler (and parser) is an XML document.
 397      * @param media The media attribute to be matched. May be null, in which
 398      * case the prefered templates will be used (i.e. alternate = no).
 399      * @param title The value of the title attribute to match. May be null.
 400      * @param charset The value of the charset attribute to match. May be null.
 401      */
 402     public void setPIParameters(String media, String title, String charset) {
 403         _parser.setPIParameters(media, title, charset);
 404     }
 405 
 406     /**
 407      * Compiles an XSL stylesheet pointed to by a URL
 408      * @param url An URL containing the input XSL stylesheet
 409      */
 410     public boolean compile(URL url) {
 411         try {
 412             // Open input stream from URL and wrap inside InputSource
 413             final InputStream stream = url.openStream();
 414             final InputSource input = new InputSource(stream);
 415             input.setSystemId(url.toString());
 416             return compile(input, _className);
 417         }
 418         catch (IOException e) {
 419             _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
 420             return false;
 421         }
 422     }
 423 
 424     /**
 425      * Compiles an XSL stylesheet pointed to by a URL
 426      * @param url An URL containing the input XSL stylesheet
 427      * @param name The name to assign to the translet class
 428      */
 429     public boolean compile(URL url, String name) {
 430         try {
 431             // Open input stream from URL and wrap inside InputSource
 432             final InputStream stream = url.openStream();
 433             final InputSource input = new InputSource(stream);
 434             input.setSystemId(url.toString());
 435             return compile(input, name);
 436         }
 437         catch (IOException e) {
 438             _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
 439             return false;
 440         }
 441     }
 442 
 443     /**
 444      * Compiles an XSL stylesheet passed in through an InputStream
 445      * @param stream An InputStream that will pass in the stylesheet contents
 446      * @param name The name of the translet class to generate
 447      * @return 'true' if the compilation was successful
 448      */
 449     public boolean compile(InputStream stream, String name) {
 450         final InputSource input = new InputSource(stream);
 451         input.setSystemId(name); // We have nothing else!!!
 452         return compile(input, name);
 453     }
 454 
 455     /**
 456      * Compiles an XSL stylesheet passed in through an InputStream
 457      * @param input An InputSource that will pass in the stylesheet contents
 458      * @param name The name of the translet class to generate - can be null
 459      * @return 'true' if the compilation was successful
 460      */
 461     public boolean compile(InputSource input, String name) {
 462         try {
 463             // Reset globals in case we're called by compile(Vector v);
 464             reset();
 465 
 466             // The systemId may not be set, so we'll have to check the URL
 467             String systemId = null;
 468             if (input != null) {
 469                 systemId = input.getSystemId();
 470             }
 471 
 472             // Set the translet class name if not already set
 473             if (_className == null) {
 474                 if (name != null) {
 475                     setClassName(name);
 476                 }
 477                 else if (systemId != null && !systemId.equals("")) {
 478                     setClassName(Util.baseName(systemId));
 479                 }
 480 
 481                 // Ensure we have a non-empty class name at this point
 482                 if (_className == null || _className.length() == 0) {
 483                     setClassName("GregorSamsa"); // default translet name
 484                 }
 485             }
 486 
 487             // Get the root node of the abstract syntax tree
 488             SyntaxTreeNode element = null;
 489             if (_reader == null) {
 490                 element = _parser.parse(input);
 491             }
 492             else {
 493                 element = _parser.parse(_reader, input);
 494             }
 495 
 496             // Compile the translet - this is where the work is done!
 497             if ((!_parser.errorsFound()) && (element != null)) {
 498                 // Create a Stylesheet element from the root node
 499                 _stylesheet = _parser.makeStylesheet(element);
 500                 _stylesheet.setSourceLoader(_loader);
 501                 _stylesheet.setSystemId(systemId);
 502                 _stylesheet.setParentStylesheet(null);
 503                 _stylesheet.setTemplateInlining(_templateInlining);
 504                 _parser.setCurrentStylesheet(_stylesheet);
 505 
 506                 // Create AST under the Stylesheet element (parse & type-check)
 507                 _parser.createAST(_stylesheet);
 508             }
 509             // Generate the bytecodes and output the translet class(es)
 510             if ((!_parser.errorsFound()) && (_stylesheet != null)) {
 511                 _stylesheet.setCallsNodeset(_callsNodeset);
 512                 _stylesheet.setMultiDocument(_multiDocument);
 513                 _stylesheet.setHasIdCall(_hasIdCall);
 514 
 515                 // Class synchronization is needed for BCEL
 516                 synchronized (getClass()) {
 517                     _stylesheet.translate();
 518                 }
 519             }
 520         }
 521         catch (Exception e) {
 522             /*if (_debug)*/ e.printStackTrace();
 523             _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
 524         }
 525         catch (Error e) {
 526             if (_debug) e.printStackTrace();
 527             _parser.reportError(Constants.FATAL, new ErrorMsg(ErrorMsg.JAXP_COMPILE_ERR, e));
 528         }
 529         finally {
 530             _reader = null; // reset this here to be sure it is not re-used
 531         }
 532         return !_parser.errorsFound();
 533     }
 534 
 535     /**
 536      * Compiles a set of stylesheets pointed to by a Vector of URLs
 537      * @param stylesheets A Vector containing URLs pointing to the stylesheets
 538      * @return 'true' if the compilation was successful
 539      */
 540     public boolean compile(Vector stylesheets) {
 541         // Get the number of stylesheets (ie. URLs) in the vector
 542         final int count = stylesheets.size();
 543 
 544         // Return straight away if the vector is empty
 545         if (count == 0) return true;
 546 
 547         // Special handling needed if the URL count is one, becuase the
 548         // _className global must not be reset if it was set explicitly
 549         if (count == 1) {
 550             final Object url = stylesheets.firstElement();
 551             if (url instanceof URL)
 552                 return compile((URL)url);
 553             else
 554                 return false;
 555         }
 556         else {
 557             // Traverse all elements in the vector and compile
 558             final Enumeration urls = stylesheets.elements();
 559             while (urls.hasMoreElements()) {
 560                 _className = null; // reset, so that new name will be computed
 561                 final Object url = urls.nextElement();
 562                 if (url instanceof URL) {
 563                     if (!compile((URL)url)) return false;
 564                 }
 565             }
 566         }
 567         return true;
 568     }
 569 
 570     /**
 571      * Returns an array of bytecode arrays generated by a compilation.
 572      * @return JVM bytecodes that represent translet class definition
 573      */
 574     public byte[][] getBytecodes() {
 575         final int count = _classes.size();
 576         final byte[][] result = new byte[count][1];
 577         for (int i = 0; i < count; i++)
 578             result[i] = _classes.get(i).toByteArray();
 579         return result;
 580     }
 581 
 582     /**
 583      * Compiles a stylesheet pointed to by a URL. The result is put in a
 584      * set of byte arrays. One byte array for each generated class.
 585      * @param name The name of the translet class to generate
 586      * @param input An InputSource that will pass in the stylesheet contents
 587      * @param outputType The output type
 588      * @return JVM bytecodes that represent translet class definition
 589      */
 590     public byte[][] compile(String name, InputSource input, int outputType) {
 591         _outputType = outputType;
 592         if (compile(input, name))
 593             return getBytecodes();
 594         else
 595             return null;
 596     }
 597 
 598     /**
 599      * Compiles a stylesheet pointed to by a URL. The result is put in a
 600      * set of byte arrays. One byte array for each generated class.
 601      * @param name The name of the translet class to generate
 602      * @param input An InputSource that will pass in the stylesheet contents
 603      * @return JVM bytecodes that represent translet class definition
 604      */
 605     public byte[][] compile(String name, InputSource input) {
 606         return compile(name, input, BYTEARRAY_OUTPUT);
 607     }
 608 
 609     /**
 610      * Set the XMLReader to use for parsing the next input stylesheet
 611      * @param reader XMLReader (SAX2 parser) to use
 612      */
 613     public void setXMLReader(XMLReader reader) {
 614         _reader = reader;
 615     }
 616 
 617     /**
 618      * Get the XMLReader to use for parsing the next input stylesheet
 619      */
 620     public XMLReader getXMLReader() {
 621         return _reader ;
 622     }
 623 
 624     /**
 625      * Get a list of all compile error messages
 626      * @return A List containing all compile error messages
 627      */
 628     public ArrayList<ErrorMsg> getErrors() {
 629         return _parser.getErrors();
 630     }
 631 
 632     /**
 633      * Get a list of all compile warning messages
 634      * @return A List containing all compile error messages
 635      */
 636     public ArrayList<ErrorMsg> getWarnings() {
 637         return _parser.getWarnings();
 638     }
 639 
 640     /**
 641      * Print all compile error messages to standard output
 642      */
 643     public void printErrors() {
 644         _parser.printErrors();
 645     }
 646 
 647     /**
 648      * Print all compile warning messages to standard output
 649      */
 650     public void printWarnings() {
 651         _parser.printWarnings();
 652     }
 653 
 654     /**
 655      * This method is called by the XPathParser when it encounters a call
 656      * to the document() function. Affects the DOM used by the translet.
 657      */
 658     protected void setMultiDocument(boolean flag) {
 659         _multiDocument = flag;
 660     }
 661 
 662     public boolean isMultiDocument() {
 663         return _multiDocument;
 664     }
 665 
 666     /**
 667      * This method is called by the XPathParser when it encounters a call
 668      * to the nodeset() extension function. Implies multi document.
 669      */
 670     protected void setCallsNodeset(boolean flag) {
 671         if (flag) setMultiDocument(flag);
 672         _callsNodeset = flag;
 673     }
 674 
 675     public boolean callsNodeset() {
 676         return _callsNodeset;
 677     }
 678 
 679     protected void setHasIdCall(boolean flag) {
 680         _hasIdCall = flag;
 681     }
 682 
 683     public boolean hasIdCall() {
 684         return _hasIdCall;
 685     }
 686 
 687     /**
 688      * Set the class name for the generated translet. This class name is
 689      * overridden if multiple stylesheets are compiled in one go using the
 690      * compile(Vector urls) method.
 691      * @param className The name to assign to the translet class
 692      */
 693     public void setClassName(String className) {
 694         final String base  = Util.baseName(className);
 695         final String noext = Util.noExtName(base);
 696         String name  = Util.toJavaName(noext);
 697 
 698         if (_packageName == null)
 699             _className = name;
 700         else
 701             _className = _packageName + '.' + name;
 702     }
 703 
 704     /**
 705      * Get the class name for the generated translet.
 706      */
 707     public String getClassName() {
 708         return _className;
 709     }
 710 
 711     /**
 712      * Convert for Java class name of local system file name.
 713      * (Replace '.' with '/' on UNIX and replace '.' by '\' on Windows/DOS.)
 714      */
 715     private String classFileName(final String className) {
 716         return className.replace('.', File.separatorChar) + ".class";
 717     }
 718 
 719     /**
 720      * Generate an output File object to send the translet to
 721      */
 722     private File getOutputFile(String className) {
 723         if (_destDir != null)
 724             return new File(_destDir, classFileName(className));
 725         else
 726             return new File(classFileName(className));
 727     }
 728 
 729     /**
 730      * Set the destination directory for the translet.
 731      * The current working directory will be used by default.
 732      */
 733     public boolean setDestDirectory(String dstDirName) {
 734         final File dir = new File(dstDirName);
 735         if (SecuritySupport.getFileExists(dir) || dir.mkdirs()) {
 736             _destDir = dir;
 737             return true;
 738         }
 739         else {
 740             _destDir = null;
 741             return false;
 742         }
 743     }
 744 
 745     /**
 746      * Set an optional package name for the translet and auxiliary classes
 747      */
 748     public void setPackageName(String packageName) {
 749         _packageName = Objects.requireNonNull(packageName);
 750         if (_className != null) setClassName(_className);
 751     }
 752 
 753     /**
 754      * Set the name of an optional JAR-file to dump the translet and
 755      * auxiliary classes to
 756      */
 757     public void setJarFileName(String jarFileName) {
 758         final String JAR_EXT = ".jar";
 759         if (jarFileName.endsWith(JAR_EXT))
 760             _jarFileName = jarFileName;
 761         else
 762             _jarFileName = jarFileName + JAR_EXT;
 763         _outputType = JAR_OUTPUT;
 764     }
 765 
 766     public String getJarFileName() {
 767         return _jarFileName;
 768     }
 769 
 770     /**
 771      * Set the top-level stylesheet
 772      */
 773     public void setStylesheet(Stylesheet stylesheet) {
 774         if (_stylesheet == null) _stylesheet = stylesheet;
 775     }
 776 
 777     /**
 778      * Returns the top-level stylesheet
 779      */
 780     public Stylesheet getStylesheet() {
 781         return _stylesheet;
 782     }
 783 
 784     /**
 785      * Registers an attribute and gives it a type so that it can be mapped to
 786      * DOM attribute types at run-time.
 787      */
 788     public int registerAttribute(QName name) {
 789         Integer code = _attributes.get(name.toString());
 790         if (code == null) {
 791             code = _nextGType++;
 792             _attributes.put(name.toString(), code);
 793             final String uri = name.getNamespace();
 794             final String local = "@"+name.getLocalPart();
 795             if ((uri != null) && (!uri.equals("")))
 796                 _namesIndex.addElement(uri+":"+local);
 797             else
 798                 _namesIndex.addElement(local);
 799             if (name.getLocalPart().equals("*")) {
 800                 registerNamespace(name.getNamespace());
 801             }
 802         }
 803         return code.intValue();
 804     }
 805 
 806     /**
 807      * Registers an element and gives it a type so that it can be mapped to
 808      * DOM element types at run-time.
 809      */
 810     public int registerElement(QName name) {
 811         // Register element (full QName)
 812         Integer code = _elements.get(name.toString());
 813         if (code == null) {
 814             _elements.put(name.toString(), code = _nextGType++);
 815             _namesIndex.addElement(name.toString());
 816         }
 817         if (name.getLocalPart().equals("*")) {
 818             registerNamespace(name.getNamespace());
 819         }
 820         return code.intValue();
 821     }
 822 
 823      /**
 824       * Registers a namespace prefix and gives it a type so that it can be mapped to
 825       * DOM namespace types at run-time.
 826       */
 827 
 828     public int registerNamespacePrefix(QName name) {
 829 
 830     Integer code = _namespacePrefixes.get(name.toString());
 831     if (code == null) {
 832         code = _nextGType++;
 833         _namespacePrefixes.put(name.toString(), code);
 834         final String uri = name.getNamespace();
 835         if ((uri != null) && (!uri.equals(""))){
 836             // namespace::ext2:ped2 will be made empty in TypedNamespaceIterator
 837             _namesIndex.addElement("?");
 838         } else{
 839            _namesIndex.addElement("?"+name.getLocalPart());
 840         }
 841     }
 842     return code.intValue();
 843     }
 844 
 845     /**
 846      * Registers a namespace and gives it a type so that it can be mapped to
 847      * DOM namespace types at run-time.
 848      */
 849     public int registerNamespace(String namespaceURI) {
 850         Integer code = _namespaces.get(namespaceURI);
 851         if (code == null) {
 852             code = _nextNSType++;
 853             _namespaces.put(namespaceURI,code);
 854             _namespaceIndex.addElement(namespaceURI);
 855         }
 856         return code.intValue();
 857     }
 858 
 859     public int nextModeSerial() {
 860         return _modeSerial++;
 861     }
 862 
 863     public int nextStylesheetSerial() {
 864         return _stylesheetSerial++;
 865     }
 866 
 867     public int nextStepPatternSerial() {
 868         return _stepPatternSerial++;
 869     }
 870 
 871     public int[] getNumberFieldIndexes() {
 872         return _numberFieldIndexes;
 873     }
 874 
 875     public int nextHelperClassSerial() {
 876         return _helperClassSerial++;
 877     }
 878 
 879     public int nextAttributeSetSerial() {
 880         return _attributeSetSerial++;
 881     }
 882 
 883     public Vector getNamesIndex() {
 884         return _namesIndex;
 885     }
 886 
 887     public Vector getNamespaceIndex() {
 888         return _namespaceIndex;
 889     }
 890 
 891     /**
 892      * Returns a unique name for every helper class needed to
 893      * execute a translet.
 894      */
 895     public String getHelperClassName() {
 896         return getClassName() + '$' + _helperClassSerial++;
 897     }
 898 
 899     public void dumpClass(JavaClass clazz) {
 900 
 901         if (_outputType == FILE_OUTPUT ||
 902             _outputType == BYTEARRAY_AND_FILE_OUTPUT)
 903         {
 904             File outFile = getOutputFile(clazz.getClassName());
 905             String parentDir = outFile.getParent();
 906             if (parentDir != null) {
 907                 File parentFile = new File(parentDir);
 908                 if (!SecuritySupport.getFileExists(parentFile))
 909                     parentFile.mkdirs();
 910             }
 911         }
 912 
 913         try {
 914             switch (_outputType) {
 915             case FILE_OUTPUT:
 916                 clazz.dump(
 917                     new BufferedOutputStream(
 918                         new FileOutputStream(
 919                             getOutputFile(clazz.getClassName()))));
 920                 break;
 921             case JAR_OUTPUT:
 922                 _bcelClasses.add(clazz);
 923                 break;
 924             case BYTEARRAY_OUTPUT:
 925             case BYTEARRAY_AND_FILE_OUTPUT:
 926             case BYTEARRAY_AND_JAR_OUTPUT:
 927             case CLASSLOADER_OUTPUT:
 928                 ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
 929                 clazz.dump(out);
 930                 _classes.add(out);
 931 
 932                 if (_outputType == BYTEARRAY_AND_FILE_OUTPUT)
 933                   clazz.dump(new BufferedOutputStream(
 934                         new FileOutputStream(getOutputFile(clazz.getClassName()))));
 935                 else if (_outputType == BYTEARRAY_AND_JAR_OUTPUT)
 936                   _bcelClasses.add(clazz);
 937 
 938                 break;
 939             }
 940         }
 941         catch (Exception e) {
 942             e.printStackTrace();
 943         }
 944     }
 945 
 946     /**
 947      * File separators are converted to forward slashes for ZIP files.
 948      */
 949     private String entryName(File f) throws IOException {
 950         return f.getName().replace(File.separatorChar, '/');
 951     }
 952 
 953     /**
 954      * Generate output JAR-file and packages
 955      */
 956     public void outputToJar() throws IOException {
 957         // create the manifest
 958         final Manifest manifest = new Manifest();
 959         final java.util.jar.Attributes atrs = manifest.getMainAttributes();
 960         atrs.put(java.util.jar.Attributes.Name.MANIFEST_VERSION, "1.2");
 961 
 962         final Map<String, Attributes> map = manifest.getEntries();
 963         // create manifest
 964         final String now = (new Date()).toString();
 965         final java.util.jar.Attributes.Name dateAttr =
 966             new java.util.jar.Attributes.Name("Date");
 967 
 968         final File jarFile = new File(_destDir, _jarFileName);
 969         final JarOutputStream jos =
 970             new JarOutputStream(new FileOutputStream(jarFile), manifest);
 971 
 972         for (JavaClass clazz : _bcelClasses) {
 973             final String className = clazz.getClassName().replace('.', '/');
 974             final java.util.jar.Attributes attr = new java.util.jar.Attributes();
 975             attr.put(dateAttr, now);
 976             map.put(className + ".class", attr);
 977             jos.putNextEntry(new JarEntry(className + ".class"));
 978             final ByteArrayOutputStream out = new ByteArrayOutputStream(2048);
 979             clazz.dump(out); // dump() closes it's output stream
 980             out.writeTo(jos);
 981         }
 982         jos.close();
 983     }
 984 
 985     /**
 986      * Turn debugging messages on/off
 987      */
 988     public void setDebug(boolean debug) {
 989         _debug = debug;
 990     }
 991 
 992     /**
 993      * Get current debugging message setting
 994      */
 995     public boolean debug() {
 996         return _debug;
 997     }
 998 
 999 
1000     /**
1001      * Retrieve a string representation of the character data to be stored
1002      * in the translet as a <code>char[]</code>.  There may be more than
1003      * one such array required.
1004      * @param index The index of the <code>char[]</code>.  Zero-based.
1005      * @return String The character data to be stored in the corresponding
1006      *               <code>char[]</code>.
1007      */
1008     public String getCharacterData(int index) {
1009         return (m_characterData.get(index)).toString();
1010     }
1011 
1012     /**
1013      * Get the number of char[] arrays, thus far, that will be created to
1014      * store literal text in the stylesheet.
1015      */
1016     public int getCharacterDataCount() {
1017         return (m_characterData != null) ? m_characterData.size() : 0;
1018     }
1019 
1020     /**
1021      * Add literal text to char arrays that will be used to store character
1022      * data in the stylesheet.
1023      * @param newData String data to be added to char arrays.
1024      *                Pre-condition:  <code>newData.length() &le; 21845</code>
1025      * @return int offset at which character data will be stored
1026      */
1027     public int addCharacterData(String newData) {
1028         StringBuilder currData;
1029         if (m_characterData == null) {
1030             m_characterData = new ArrayList<>();
1031             currData = new StringBuilder();
1032             m_characterData.add(currData);
1033         } else {
1034             currData = m_characterData.get(m_characterData.size()-1);
1035         }
1036 
1037         // Character data could take up to three-times as much space when
1038         // written to the class file as UTF-8.  The maximum size for a
1039         // constant is 65535/3.  If we exceed that,
1040         // (We really should use some "bin packing".)
1041         if (newData.length() + currData.length() > 21845) {
1042             currData = new StringBuilder();
1043             m_characterData.add(currData);
1044         }
1045 
1046         int newDataOffset = currData.length();
1047         currData.append(newData);
1048 
1049         return newDataOffset;
1050     }
1051 }
--- EOF ---