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