1 /*
   2  * Copyright (c) 2006, 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: AbstractTranslet.java,v 1.6 2006/06/19 19:49:03 spericas Exp $
  22  */
  23 
  24 package com.sun.org.apache.xalan.internal.xsltc.runtime;
  25 
  26 import com.sun.org.apache.xalan.internal.XalanConstants;
  27 import com.sun.org.apache.xalan.internal.utils.FactoryImpl;
  28 import com.sun.org.apache.xalan.internal.xsltc.DOM;
  29 import com.sun.org.apache.xalan.internal.xsltc.DOMCache;
  30 import com.sun.org.apache.xalan.internal.xsltc.DOMEnhancedForDTM;
  31 import com.sun.org.apache.xalan.internal.xsltc.Translet;
  32 import com.sun.org.apache.xalan.internal.xsltc.TransletException;
  33 import com.sun.org.apache.xalan.internal.xsltc.dom.DOMAdapter;
  34 import com.sun.org.apache.xalan.internal.xsltc.dom.KeyIndex;
  35 import com.sun.org.apache.xalan.internal.xsltc.runtime.output.TransletOutputHandlerFactory;
  36 import com.sun.org.apache.xml.internal.dtm.DTM;
  37 import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
  38 import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
  39 import com.sun.org.apache.xml.internal.serializer.ToStream;
  40 import java.io.BufferedOutputStream;
  41 import java.io.File;
  42 import java.io.FileOutputStream;
  43 import java.text.DecimalFormat;
  44 import java.text.DecimalFormatSymbols;
  45 import java.util.ArrayList;
  46 import java.util.Enumeration;
  47 import java.util.HashMap;
  48 import java.util.Map;
  49 import javax.xml.parsers.DocumentBuilderFactory;
  50 import javax.xml.parsers.ParserConfigurationException;
  51 import javax.xml.transform.Templates;
  52 import org.w3c.dom.DOMImplementation;
  53 import org.w3c.dom.Document;
  54 
  55 /**
  56  * @author Jacek Ambroziak
  57  * @author Santiago Pericas-Geertsen
  58  * @author Morten Jorgensen
  59  * @author G. Todd Miller
  60  * @author John Howard, JohnH@schemasoft.com
  61  */
  62 public abstract class AbstractTranslet implements Translet {
  63 
  64     // These attributes are extracted from the xsl:output element. They also
  65     // appear as fields (with the same type, only public) in Output.java
  66     public String  _version = "1.0";
  67     public String  _method = null;
  68     public String  _encoding = "UTF-8";
  69     public boolean _omitHeader = false;
  70     public String  _standalone = null;
  71     //see OutputPropertiesFactory.ORACLE_IS_STANDALONE
  72     public boolean  _isStandalone = false;
  73     public String  _doctypePublic = null;
  74     public String  _doctypeSystem = null;
  75     public boolean _indent = false;
  76     public String  _mediaType = null;
  77     public ArrayList<String> _cdata = null;
  78     public int _indentamount = -1;
  79 
  80     public static final int FIRST_TRANSLET_VERSION = 100;
  81     public static final int VER_SPLIT_NAMES_ARRAY = 101;
  82     public static final int CURRENT_TRANSLET_VERSION = VER_SPLIT_NAMES_ARRAY;
  83 
  84     // Initialize Translet version field to base value.  A class that extends
  85     // AbstractTranslet may override this value to a more recent translet
  86     // version; if it doesn't override the value (because it was compiled
  87     // before the notion of a translet version was introduced, it will get
  88     // this default value).
  89     protected int transletVersion = FIRST_TRANSLET_VERSION;
  90 
  91     // DOM/translet handshaking - the arrays are set by the compiled translet
  92     protected String[] namesArray;
  93     protected String[] urisArray;
  94     protected int[]    typesArray;
  95     protected String[] namespaceArray;
  96 
  97     // The Templates object that is used to create this Translet instance
  98     protected Templates _templates = null;
  99 
 100     // Boolean flag to indicate whether this translet has id functions.
 101     protected boolean _hasIdCall = false;
 102 
 103     // TODO - these should only be instanciated when needed
 104     protected StringValueHandler stringValueHandler = new StringValueHandler();
 105 
 106     // Use one empty string instead of constantly instanciating String("");
 107     private final static String EMPTYSTRING = "";
 108 
 109     // This is the name of the index used for ID attributes
 110     private final static String ID_INDEX_NAME = "##id";
 111 
 112     private boolean _useServicesMechanism;
 113 
 114     // The OutputStream for redirect function
 115     private FileOutputStream output = null;
 116 
 117     /**
 118      * protocols allowed for external references set by the stylesheet processing instruction, Document() function, Import and Include element.
 119      */
 120     private String _accessExternalStylesheet = XalanConstants.EXTERNAL_ACCESS_DEFAULT;
 121 
 122     /************************************************************************
 123      * Debugging
 124      ************************************************************************/
 125     public void printInternalState() {
 126         System.out.println("-------------------------------------");
 127         System.out.println("AbstractTranslet this = " + this);
 128         System.out.println("pbase = " + pbase);
 129         System.out.println("vframe = " + pframe);
 130         System.out.println("paramsStack.size() = " + paramsStack.size());
 131         System.out.println("namesArray.size = " + namesArray.length);
 132         System.out.println("namespaceArray.size = " + namespaceArray.length);
 133         System.out.println("");
 134         System.out.println("Total memory = " + Runtime.getRuntime().totalMemory());
 135     }
 136 
 137     /**
 138      * Wrap the initial input DOM in a dom adapter. This adapter is wrapped in
 139      * a DOM multiplexer if the document() function is used (handled by compiled
 140      * code in the translet - see compiler/Stylesheet.compileTransform()).
 141      */
 142     public final DOMAdapter makeDOMAdapter(DOM dom)
 143         throws TransletException {
 144         setRootForKeys(dom.getDocument());
 145         return new DOMAdapter(dom, namesArray, urisArray, typesArray, namespaceArray);
 146     }
 147 
 148     /************************************************************************
 149      * Parameter handling
 150      ************************************************************************/
 151 
 152     // Parameter's stack: <tt>pbase</tt> and <tt>pframe</tt> are used
 153     // to denote the current parameter frame.
 154     protected int pbase = 0, pframe = 0;
 155     protected ArrayList paramsStack = new ArrayList();
 156 
 157     /**
 158      * Push a new parameter frame.
 159      */
 160     public final void pushParamFrame() {
 161         paramsStack.add(pframe, new Integer(pbase));
 162         pbase = ++pframe;
 163     }
 164 
 165     /**
 166      * Pop the topmost parameter frame.
 167      */
 168     public final void popParamFrame() {
 169         if (pbase > 0) {
 170             final int oldpbase = ((Integer)paramsStack.get(--pbase)).intValue();
 171             for (int i = pframe - 1; i >= pbase; i--) {
 172                 paramsStack.remove(i);
 173             }
 174             pframe = pbase; pbase = oldpbase;
 175         }
 176     }
 177 
 178     /**
 179      * Add a new global parameter if not already in the current frame.
 180      * To setParameters of the form {http://foo.bar}xyz
 181      * This needs to get mapped to an instance variable in the class
 182      * The mapping  created so that
 183      * the global variables in the generated class become
 184      * http$colon$$flash$$flash$foo$dot$bar$colon$xyz
 185      */
 186     public final Object addParameter(String name, Object value) {
 187         name = BasisLibrary.mapQNameToJavaName (name);
 188         return addParameter(name, value, false);
 189     }
 190 
 191     /**
 192      * Add a new global or local parameter if not already in the current frame.
 193      * The 'isDefault' parameter is set to true if the value passed is the
 194      * default value from the <xsl:parameter> element's select attribute or
 195      * element body.
 196      */
 197     public final Object addParameter(String name, Object value,
 198         boolean isDefault)
 199     {
 200         // Local parameters need to be re-evaluated for each iteration
 201         for (int i = pframe - 1; i >= pbase; i--) {
 202             final Parameter param = (Parameter) paramsStack.get(i);
 203 
 204             if (param._name.equals(name)) {
 205                 // Only overwrite if current value is the default value and
 206                 // the new value is _NOT_ the default value.
 207                 if (param._isDefault || !isDefault) {
 208                     param._value = value;
 209                     param._isDefault = isDefault;
 210                     return value;
 211                 }
 212                 return param._value;
 213             }
 214         }
 215 
 216         // Add new parameter to parameter stack
 217         paramsStack.add(pframe++, new Parameter(name, value, isDefault));
 218         return value;
 219     }
 220 
 221     /**
 222      * Clears the parameter stack.
 223      */
 224     public void clearParameters() {
 225         pbase = pframe = 0;
 226         paramsStack.clear();
 227     }
 228 
 229     /**
 230      * Get the value of a parameter from the current frame or
 231      * <tt>null</tt> if undefined.
 232      */
 233     public final Object getParameter(String name) {
 234 
 235         name = BasisLibrary.mapQNameToJavaName (name);
 236 
 237         for (int i = pframe - 1; i >= pbase; i--) {
 238             final Parameter param = (Parameter)paramsStack.get(i);
 239             if (param._name.equals(name)) return param._value;
 240         }
 241         return null;
 242     }
 243 
 244     /************************************************************************
 245      * Message handling - implementation of <xsl:message>
 246      ************************************************************************/
 247 
 248     // Holds the translet's message handler - used for <xsl:message>.
 249     // The deault message handler dumps a string stdout, but anything can be
 250     // used, such as a dialog box for applets, etc.
 251     private MessageHandler _msgHandler = null;
 252 
 253     /**
 254      * Set the translet's message handler - must implement MessageHandler
 255      */
 256     public final void setMessageHandler(MessageHandler handler) {
 257         _msgHandler = handler;
 258     }
 259 
 260     /**
 261      * Pass a message to the message handler - used by Message class.
 262      */
 263     public final void displayMessage(String msg) {
 264         if (_msgHandler == null) {
 265             System.err.println(msg);
 266         }
 267         else {
 268             _msgHandler.displayMessage(msg);
 269         }
 270     }
 271 
 272     /************************************************************************
 273      * Decimal number format symbol handling
 274      ************************************************************************/
 275 
 276     // Contains decimal number formatting symbols used by FormatNumberCall
 277     public Map<String, DecimalFormat> _formatSymbols = null;
 278 
 279     /**
 280      * Adds a DecimalFormat object to the _formatSymbols map.
 281      * The entry is created with the input DecimalFormatSymbols.
 282      */
 283     public void addDecimalFormat(String name, DecimalFormatSymbols symbols) {
 284         // Instanciate map for formatting symbols if needed
 285         if (_formatSymbols == null) _formatSymbols = new HashMap<>();
 286 
 287         // The name cannot be null - use empty string instead
 288         if (name == null) name = EMPTYSTRING;
 289 
 290         // Construct a DecimalFormat object containing the symbols we got
 291         final DecimalFormat df = new DecimalFormat();
 292         if (symbols != null) {
 293             df.setDecimalFormatSymbols(symbols);
 294         }
 295         _formatSymbols.put(name, df);
 296     }
 297 
 298     /**
 299      * Retrieves a named DecimalFormat object from the _formatSymbols map.
 300      */
 301     public final DecimalFormat getDecimalFormat(String name) {
 302 
 303         if (_formatSymbols != null) {
 304             // The name cannot be null - use empty string instead
 305             if (name == null) name = EMPTYSTRING;
 306 
 307             DecimalFormat df = _formatSymbols.get(name);
 308             if (df == null) df = _formatSymbols.get(EMPTYSTRING);
 309             return df;
 310         }
 311         return(null);
 312     }
 313 
 314     /**
 315      * Give the translet an opportunity to perform a prepass on the document
 316      * to extract any information that it can store in an optimized form.
 317      *
 318      * Currently, it only extracts information about attributes of type ID.
 319      */
 320     public final void prepassDocument(DOM document) {
 321         setIndexSize(document.getSize());
 322         buildIDIndex(document);
 323     }
 324 
 325     /**
 326      * Leverages the Key Class to implement the XSLT id() function.
 327      * buildIdIndex creates the index (##id) that Key Class uses.
 328      * The index contains the element node index (int) and Id value (String).
 329      */
 330     private final void buildIDIndex(DOM document) {
 331         setRootForKeys(document.getDocument());
 332 
 333         if (document instanceof DOMEnhancedForDTM) {
 334             DOMEnhancedForDTM enhancedDOM = (DOMEnhancedForDTM)document;
 335 
 336             // If the input source is DOMSource, the KeyIndex table is not
 337             // built at this time. It will be built later by the lookupId()
 338             // and containsId() methods of the KeyIndex class.
 339             if (enhancedDOM.hasDOMSource()) {
 340                 buildKeyIndex(ID_INDEX_NAME, document);
 341                 return;
 342             }
 343             else {
 344                 final Map<String, Integer> elementsByID = enhancedDOM.getElementsWithIDs();
 345 
 346                 if (elementsByID == null) {
 347                     return;
 348                 }
 349 
 350                 // Given a Map of DTM nodes indexed by ID attribute values,
 351                 // loop through the table copying information to a KeyIndex
 352                 // for the mapping from ID attribute value to DTM node
 353                 boolean hasIDValues = false;
 354                 for (Map.Entry<String, Integer> entry : elementsByID.entrySet()) {
 355                     final int element = document.getNodeHandle(entry.getValue());
 356                     buildKeyIndex(ID_INDEX_NAME, element, entry.getKey());
 357                     hasIDValues = true;
 358                 }
 359 
 360                 if (hasIDValues) {
 361                     setKeyIndexDom(ID_INDEX_NAME, document);
 362                 }
 363             }
 364         }
 365     }
 366 
 367     /**
 368      * After constructing the translet object, this method must be called to
 369      * perform any version-specific post-initialization that's required.
 370      */
 371     public final void postInitialization() {
 372         // If the version of the translet had just one namesArray, split
 373         // it into multiple fields.
 374         if (transletVersion < VER_SPLIT_NAMES_ARRAY) {
 375             int arraySize = namesArray.length;
 376             String[] newURIsArray = new String[arraySize];
 377             String[] newNamesArray = new String[arraySize];
 378             int[] newTypesArray = new int[arraySize];
 379 
 380             for (int i = 0; i < arraySize; i++) {
 381                 String name = namesArray[i];
 382                 int colonIndex = name.lastIndexOf(':');
 383                 int lNameStartIdx = colonIndex+1;
 384 
 385                 if (colonIndex > -1) {
 386                     newURIsArray[i] = name.substring(0, colonIndex);
 387                 }
 388 
 389                // Distinguish attribute and element names.  Attribute has
 390                // @ before local part of name.
 391                if (name.charAt(lNameStartIdx) == '@') {
 392                    lNameStartIdx++;
 393                    newTypesArray[i] = DTM.ATTRIBUTE_NODE;
 394                } else if (name.charAt(lNameStartIdx) == '?') {
 395                    lNameStartIdx++;
 396                    newTypesArray[i] = DTM.NAMESPACE_NODE;
 397                } else {
 398                    newTypesArray[i] = DTM.ELEMENT_NODE;
 399                }
 400                newNamesArray[i] =
 401                           (lNameStartIdx == 0) ? name
 402                                                : name.substring(lNameStartIdx);
 403             }
 404 
 405             namesArray = newNamesArray;
 406             urisArray  = newURIsArray;
 407             typesArray = newTypesArray;
 408         }
 409 
 410         // Was translet compiled using a more recent version of the XSLTC
 411         // compiler than is known by the AbstractTranslet class?  If, so
 412         // and we've made it this far (which is doubtful), we should give up.
 413         if (transletVersion > CURRENT_TRANSLET_VERSION) {
 414             BasisLibrary.runTimeError(BasisLibrary.UNKNOWN_TRANSLET_VERSION_ERR,
 415                                       this.getClass().getName());
 416         }
 417     }
 418 
 419     /************************************************************************
 420      * Index(es) for <xsl:key> / key() / id()
 421      ************************************************************************/
 422 
 423     // Container for all indexes for xsl:key elements
 424     private Map<String, KeyIndex> _keyIndexes = null;
 425     private KeyIndex  _emptyKeyIndex = null;
 426     private int       _indexSize = 0;
 427     private int       _currentRootForKeys = 0;
 428 
 429     /**
 430      * This method is used to pass the largest DOM size to the translet.
 431      * Needed to make sure that the translet can index the whole DOM.
 432      */
 433     public void setIndexSize(int size) {
 434         if (size > _indexSize) _indexSize = size;
 435     }
 436 
 437     /**
 438      * Creates a KeyIndex object of the desired size - don't want to resize!!!
 439      */
 440     public KeyIndex createKeyIndex() {
 441         return(new KeyIndex(_indexSize));
 442     }
 443 
 444     /**
 445      * Adds a value to a key/id index
 446      *   @param name is the name of the index (the key or ##id)
 447      *   @param node is the node handle of the node to insert
 448      *   @param value is the value that will look up the node in the given index
 449      */
 450     public void buildKeyIndex(String name, int node, String value) {
 451         KeyIndex index = buildKeyIndexHelper(name);
 452         index.add(value, node, _currentRootForKeys);
 453     }
 454 
 455     /**
 456      * Create an empty KeyIndex in the DOM case
 457      *   @param name is the name of the index (the key or ##id)
 458      *   @param dom is the DOM
 459      */
 460     public void buildKeyIndex(String name, DOM dom) {
 461         KeyIndex index = buildKeyIndexHelper(name);
 462         index.setDom(dom, dom.getDocument());
 463     }
 464 
 465     /**
 466      * Return KeyIndex for the buildKeyIndex methods. Note the difference from the
 467      * public getKeyIndex method, this method creates a new Map if keyIndexes does
 468      * not exist.
 469      *
 470      * @param name the name of the index (the key or ##id)
 471      * @return a KeyIndex.
 472      */
 473     private KeyIndex buildKeyIndexHelper(String name) {
 474         if (_keyIndexes == null) _keyIndexes = new HashMap<>();
 475 
 476         KeyIndex index = _keyIndexes.get(name);
 477         if (index == null) {
 478             _keyIndexes.put(name, index = new KeyIndex(_indexSize));
 479         }
 480         return index;
 481     }
 482 
 483     /**
 484      * Returns the index for a given key (or id).
 485      * The index implements our internal iterator interface
 486      * @param name the name of the index (the key or ##id)
 487      * @return a KeyIndex.
 488      */
 489     public KeyIndex getKeyIndex(String name) {
 490         // Return an empty key index iterator if none are defined
 491         if (_keyIndexes == null) {
 492             return (_emptyKeyIndex != null)
 493                 ? _emptyKeyIndex
 494                 : (_emptyKeyIndex = new KeyIndex(1));
 495         }
 496 
 497         // Look up the requested key index
 498         final KeyIndex index = _keyIndexes.get(name);
 499 
 500         // Return an empty key index iterator if the requested index not found
 501         if (index == null) {
 502             return (_emptyKeyIndex != null)
 503                 ? _emptyKeyIndex
 504                 : (_emptyKeyIndex = new KeyIndex(1));
 505         }
 506 
 507         return(index);
 508     }
 509 
 510     private void setRootForKeys(int root) {
 511         _currentRootForKeys = root;
 512     }
 513 
 514     /**
 515      * This method builds key indexes - it is overridden in the compiled
 516      * translet in cases where the <xsl:key> element is used
 517      */
 518     public void buildKeys(DOM document, DTMAxisIterator iterator,
 519                           SerializationHandler handler,
 520                           int root) throws TransletException {
 521 
 522     }
 523 
 524     /**
 525      * This method builds key indexes - it is overridden in the compiled
 526      * translet in cases where the <xsl:key> element is used
 527      */
 528     public void setKeyIndexDom(String name, DOM document) {
 529         getKeyIndex(name).setDom(document, document.getDocument());
 530     }
 531 
 532     /************************************************************************
 533      * DOM cache handling
 534      ************************************************************************/
 535 
 536     // Hold the DOM cache (if any) used with this translet
 537     private DOMCache _domCache = null;
 538 
 539     /**
 540      * Sets the DOM cache used for additional documents loaded using the
 541      * document() function.
 542      */
 543     public void setDOMCache(DOMCache cache) {
 544         _domCache = cache;
 545     }
 546 
 547     /**
 548      * Returns the DOM cache used for this translet. Used by the LoadDocument
 549      * class (if present) when the document() function is used.
 550      */
 551     public DOMCache getDOMCache() {
 552         return(_domCache);
 553     }
 554 
 555     /************************************************************************
 556      * Multiple output document extension.
 557      * See compiler/TransletOutput for actual implementation.
 558      ************************************************************************/
 559 
 560     public SerializationHandler openOutputHandler(String filename, boolean append)
 561         throws TransletException
 562     {
 563         try {
 564             final TransletOutputHandlerFactory factory
 565                 = TransletOutputHandlerFactory.newInstance();
 566 
 567             String dirStr = new File(filename).getParent();
 568             if ((null != dirStr) && (dirStr.length() > 0)) {
 569                File dir = new File(dirStr);
 570                dir.mkdirs();
 571             }
 572 
 573             output = new FileOutputStream(filename, append);
 574             factory.setEncoding(_encoding);
 575             factory.setOutputMethod(_method);
 576             factory.setOutputStream(new BufferedOutputStream(output));
 577             factory.setOutputType(TransletOutputHandlerFactory.STREAM);
 578 
 579             final SerializationHandler handler
 580                 = factory.getSerializationHandler();
 581 
 582             transferOutputSettings(handler);
 583             handler.startDocument();
 584             return handler;
 585         }
 586         catch (Exception e) {
 587             throw new TransletException(e);
 588         }
 589     }
 590 
 591     public SerializationHandler openOutputHandler(String filename)
 592        throws TransletException
 593     {
 594        return openOutputHandler(filename, false);
 595     }
 596 
 597     public void closeOutputHandler(SerializationHandler handler) {
 598         try {
 599             handler.endDocument();
 600             handler.close();
 601             if (output != null) {
 602                 output.close();
 603             }
 604         }
 605         catch (Exception e) {
 606             // what can you do?
 607         }
 608     }
 609 
 610     /************************************************************************
 611      * Native API transformation methods - _NOT_ JAXP/TrAX
 612      ************************************************************************/
 613 
 614     /**
 615      * Main transform() method - this is overridden by the compiled translet
 616      */
 617     public abstract void transform(DOM document, DTMAxisIterator iterator,
 618                                    SerializationHandler handler)
 619         throws TransletException;
 620 
 621     /**
 622      * Calls transform() with a given output handler
 623      */
 624     public final void transform(DOM document, SerializationHandler handler)
 625         throws TransletException {
 626         try {
 627             transform(document, document.getIterator(), handler);
 628         } finally {
 629             _keyIndexes = null;
 630         }
 631     }
 632 
 633     /**
 634      * Used by some compiled code as a shortcut for passing strings to the
 635      * output handler
 636      */
 637     public final void characters(final String string,
 638                                  SerializationHandler handler)
 639         throws TransletException {
 640         if (string != null) {
 641            //final int length = string.length();
 642            try {
 643                handler.characters(string);
 644            } catch (Exception e) {
 645                throw new TransletException(e);
 646            }
 647         }
 648     }
 649 
 650     /**
 651      * Add's a name of an element whose text contents should be output as CDATA
 652      */
 653     public void addCdataElement(String name) {
 654         if (_cdata == null) {
 655             _cdata = new ArrayList<>();
 656         }
 657 
 658         int lastColon = name.lastIndexOf(':');
 659 
 660         if (lastColon > 0) {
 661             String uri = name.substring(0, lastColon);
 662             String localName = name.substring(lastColon+1);
 663             _cdata.add(uri);
 664             _cdata.add(localName);
 665         } else {
 666             _cdata.add(null);
 667             _cdata.add(name);
 668         }
 669     }
 670 
 671     /**
 672      * Transfer the output settings to the output post-processor
 673      */
 674     protected void transferOutputSettings(SerializationHandler handler) {
 675         if (_method != null) {
 676             if (_method.equals("xml")) {
 677                 if (_standalone != null) {
 678                     handler.setStandalone(_standalone);
 679                 }
 680                 if (_omitHeader) {
 681                     handler.setOmitXMLDeclaration(true);
 682                 }
 683                 handler.setCdataSectionElements(_cdata);
 684                 if (_version != null) {
 685                     handler.setVersion(_version);
 686                 }
 687                 handler.setIndent(_indent);
 688                 handler.setIndentAmount(_indentamount);
 689                 if (_doctypeSystem != null) {
 690                     handler.setDoctype(_doctypeSystem, _doctypePublic);
 691                 }
 692                 handler.setIsStandalone(_isStandalone);
 693             }
 694             else if (_method.equals("html")) {
 695                 handler.setIndent(_indent);
 696                 handler.setDoctype(_doctypeSystem, _doctypePublic);
 697                 if (_mediaType != null) {
 698                     handler.setMediaType(_mediaType);
 699                 }
 700             }
 701         }
 702         else {
 703             handler.setCdataSectionElements(_cdata);
 704             if (_version != null) {
 705                 handler.setVersion(_version);
 706             }
 707             if (_standalone != null) {
 708                 handler.setStandalone(_standalone);
 709             }
 710             if (_omitHeader) {
 711                 handler.setOmitXMLDeclaration(true);
 712             }
 713             handler.setIndent(_indent);
 714             handler.setDoctype(_doctypeSystem, _doctypePublic);
 715             handler.setIsStandalone(_isStandalone);
 716         }
 717     }
 718 
 719     private Map<String, Class<?>> _auxClasses = null;
 720 
 721     public void addAuxiliaryClass(Class auxClass) {
 722         if (_auxClasses == null) _auxClasses = new HashMap<>();
 723         _auxClasses.put(auxClass.getName(), auxClass);
 724     }
 725 
 726     public void setAuxiliaryClasses(Map<String, Class<?>> auxClasses) {
 727         _auxClasses = auxClasses;
 728     }
 729 
 730     public Class getAuxiliaryClass(String className) {
 731         if (_auxClasses == null) return null;
 732         return((Class)_auxClasses.get(className));
 733     }
 734 
 735     // GTM added (see pg 110)
 736     public String[] getNamesArray() {
 737         return namesArray;
 738     }
 739 
 740     public String[] getUrisArray() {
 741         return urisArray;
 742     }
 743 
 744     public int[] getTypesArray() {
 745         return typesArray;
 746     }
 747 
 748     public String[] getNamespaceArray() {
 749         return namespaceArray;
 750     }
 751 
 752     public boolean hasIdCall() {
 753         return _hasIdCall;
 754     }
 755 
 756     public Templates getTemplates() {
 757         return _templates;
 758     }
 759 
 760     public void setTemplates(Templates templates) {
 761         _templates = templates;
 762     }
 763     /**
 764      * Return the state of the services mechanism feature.
 765      */
 766     public boolean useServicesMechnism() {
 767         return _useServicesMechanism;
 768     }
 769 
 770     /**
 771      * Set the state of the services mechanism feature.
 772      */
 773     public void setServicesMechnism(boolean flag) {
 774         _useServicesMechanism = flag;
 775     }
 776 
 777     /**
 778      * Return allowed protocols for accessing external stylesheet.
 779      */
 780     public String getAllowedProtocols() {
 781         return _accessExternalStylesheet;
 782     }
 783 
 784     /**
 785      * Set allowed protocols for accessing external stylesheet.
 786      */
 787     public void setAllowedProtocols(String protocols) {
 788         _accessExternalStylesheet = protocols;
 789     }
 790 
 791     /************************************************************************
 792      * DOMImplementation caching for basis library
 793      ************************************************************************/
 794     protected DOMImplementation _domImplementation = null;
 795 
 796     public Document newDocument(String uri, String qname)
 797         throws ParserConfigurationException
 798     {
 799         if (_domImplementation == null) {
 800             DocumentBuilderFactory dbf = FactoryImpl.getDOMFactory(_useServicesMechanism);
 801             _domImplementation = dbf.newDocumentBuilder().getDOMImplementation();
 802         }
 803         return _domImplementation.createDocument(uri, qname, null);
 804     }
 805 }