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