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