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