1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xalan.internal.xsltc.dom;
23
24import javax.xml.stream.XMLEventReader;
25import javax.xml.stream.XMLStreamReader;
26import javax.xml.transform.Source;
27import javax.xml.transform.dom.DOMSource;
28import javax.xml.transform.sax.SAXSource;
29import javax.xml.transform.stream.StreamSource;
30import javax.xml.transform.stax.StAXSource;
31
32import com.sun.org.apache.xml.internal.dtm.DTM;
33import com.sun.org.apache.xml.internal.dtm.ref.DTMDefaultBase;
34import com.sun.org.apache.xml.internal.dtm.DTMException;
35import com.sun.org.apache.xml.internal.dtm.DTMWSFilter;
36import com.sun.org.apache.xml.internal.dtm.ref.DTMManagerDefault;
37import com.sun.org.apache.xml.internal.res.XMLErrorResources;
38import com.sun.org.apache.xml.internal.res.XMLMessages;
39import com.sun.org.apache.xml.internal.utils.SystemIDResolver;
40import com.sun.org.apache.xalan.internal.xsltc.trax.DOM2SAX;
41import com.sun.org.apache.xalan.internal.xsltc.trax.StAXEvent2SAX;
42import com.sun.org.apache.xalan.internal.xsltc.trax.StAXStream2SAX;
43
44import org.xml.sax.InputSource;
45import org.xml.sax.SAXNotRecognizedException;
46import org.xml.sax.SAXNotSupportedException;
47import org.xml.sax.XMLReader;
48
49/**
50 * The default implementation for the DTMManager.
51 */
52public class XSLTCDTMManager extends DTMManagerDefault
53{
54
55    /** Set this to true if you want a dump of the DTM after creation */
56    private static final boolean DUMPTREE = false;
57
58    /** Set this to true if you want basic diagnostics */
59    private static final boolean DEBUG = false;
60
61    /**
62     * Constructor DTMManagerDefault
63     *
64     */
65    public XSLTCDTMManager()
66    {
67        super();
68    }
69
70    /**
71     * Obtain a new instance of a <code>DTMManager</code>.
72     * This static method creates a new factory instance.
73     * The current implementation just returns a new XSLTCDTMManager instance.
74     */
75    public static XSLTCDTMManager newInstance()
76    {
77        return new XSLTCDTMManager();
78    }
79
80    /**
81     * Creates a new instance of the XSLTC DTM Manager service.
82     * Creates a new instance of the default class
83     * <code>com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager</code>.
84     */
85      public static XSLTCDTMManager createNewDTMManagerInstance() {
86         return newInstance();
87      }
88
89    /**
90     * Get an instance of a DTM, loaded with the content from the
91     * specified source.  If the unique flag is true, a new instance will
92     * always be returned.  Otherwise it is up to the DTMManager to return a
93     * new instance or an instance that it already created and may be being used
94     * by someone else.
95     * (I think more parameters will need to be added for error handling, and
96     * entity resolution).
97     *
98     * @param source the specification of the source object.
99     * @param unique true if the returned DTM must be unique, probably because it
100     * is going to be mutated.
101     * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
102     *                         be null.
103     * @param incremental true if the DTM should be built incrementally, if
104     *                    possible.
105     * @param doIndexing true if the caller considers it worth it to use
106     *                   indexing schemes.
107     *
108     * @return a non-null DTM reference.
109     */
110    @Override
111    public DTM getDTM(Source source, boolean unique,
112                      DTMWSFilter whiteSpaceFilter, boolean incremental,
113                      boolean doIndexing)
114    {
115        return getDTM(source, unique, whiteSpaceFilter, incremental,
116                      doIndexing, false, 0, true, false);
117    }
118
119    /**
120     * Get an instance of a DTM, loaded with the content from the
121     * specified source.  If the unique flag is true, a new instance will
122     * always be returned.  Otherwise it is up to the DTMManager to return a
123     * new instance or an instance that it already created and may be being used
124     * by someone else.
125     * (I think more parameters will need to be added for error handling, and
126     * entity resolution).
127     *
128     * @param source the specification of the source object.
129     * @param unique true if the returned DTM must be unique, probably because it
130     * is going to be mutated.
131     * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
132     *                         be null.
133     * @param incremental true if the DTM should be built incrementally, if
134     *                    possible.
135     * @param doIndexing true if the caller considers it worth it to use
136     *                   indexing schemes.
137     * @param buildIdIndex true if the id index table should be built.
138     *
139     * @return a non-null DTM reference.
140     */
141    public DTM getDTM(Source source, boolean unique,
142                      DTMWSFilter whiteSpaceFilter, boolean incremental,
143                      boolean doIndexing, boolean buildIdIndex)
144    {
145        return getDTM(source, unique, whiteSpaceFilter, incremental,
146                      doIndexing, false, 0, buildIdIndex, false);
147    }
148
149    /**
150     * Get an instance of a DTM, loaded with the content from the
151     * specified source.  If the unique flag is true, a new instance will
152     * always be returned.  Otherwise it is up to the DTMManager to return a
153     * new instance or an instance that it already created and may be being used
154     * by someone else.
155     * (I think more parameters will need to be added for error handling, and
156     * entity resolution).
157     *
158     * @param source the specification of the source object.
159     * @param unique true if the returned DTM must be unique, probably because it
160     * is going to be mutated.
161     * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
162     *                         be null.
163     * @param incremental true if the DTM should be built incrementally, if
164     *                    possible.
165     * @param doIndexing true if the caller considers it worth it to use
166     *                   indexing schemes.
167     * @param buildIdIndex true if the id index table should be built.
168     * @param newNameTable true if we want to use a separate ExpandedNameTable
169     *                     for this DTM.
170     *
171     * @return a non-null DTM reference.
172     */
173  public DTM getDTM(Source source, boolean unique,
174                    DTMWSFilter whiteSpaceFilter, boolean incremental,
175                    boolean doIndexing, boolean buildIdIndex,
176                    boolean newNameTable)
177  {
178    return getDTM(source, unique, whiteSpaceFilter, incremental,
179                  doIndexing, false, 0, buildIdIndex, newNameTable);
180  }
181
182  /**
183     * Get an instance of a DTM, loaded with the content from the
184     * specified source.  If the unique flag is true, a new instance will
185     * always be returned.  Otherwise it is up to the DTMManager to return a
186     * new instance or an instance that it already created and may be being used
187     * by someone else.
188     * (I think more parameters will need to be added for error handling, and
189     * entity resolution).
190     *
191     * @param source the specification of the source object.
192     * @param unique true if the returned DTM must be unique, probably because it
193     * is going to be mutated.
194     * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
195     *                         be null.
196     * @param incremental true if the DTM should be built incrementally, if
197     *                    possible.
198     * @param doIndexing true if the caller considers it worth it to use
199     *                   indexing schemes.
200     * @param hasUserReader true if <code>source</code> is a
201     *                      <code>SAXSource</code> object that has an
202     *                      <code>XMLReader</code>, that was specified by the
203     *                      user.
204     * @param size  Specifies initial size of tables that represent the DTM
205     * @param buildIdIndex true if the id index table should be built.
206     *
207     * @return a non-null DTM reference.
208     */
209    public DTM getDTM(Source source, boolean unique,
210                      DTMWSFilter whiteSpaceFilter, boolean incremental,
211                      boolean doIndexing, boolean hasUserReader, int size,
212                      boolean buildIdIndex)
213    {
214      return getDTM(source, unique, whiteSpaceFilter, incremental,
215                    doIndexing, hasUserReader, size,
216                    buildIdIndex, false);
217  }
218
219  /**
220     * Get an instance of a DTM, loaded with the content from the
221     * specified source.  If the unique flag is true, a new instance will
222     * always be returned.  Otherwise it is up to the DTMManager to return a
223     * new instance or an instance that it already created and may be being used
224     * by someone else.
225     * (I think more parameters will need to be added for error handling, and
226     * entity resolution).
227     *
228     * @param source the specification of the source object.
229     * @param unique true if the returned DTM must be unique, probably because it
230     * is going to be mutated.
231     * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
232     *                         be null.
233     * @param incremental true if the DTM should be built incrementally, if
234     *                    possible.
235     * @param doIndexing true if the caller considers it worth it to use
236     *                   indexing schemes.
237     * @param hasUserReader true if <code>source</code> is a
238     *                      <code>SAXSource</code> object that has an
239     *                      <code>XMLReader</code>, that was specified by the
240     *                      user.
241     * @param size  Specifies initial size of tables that represent the DTM
242     * @param buildIdIndex true if the id index table should be built.
243     * @param newNameTable true if we want to use a separate ExpandedNameTable
244     *                     for this DTM.
245     *
246     * @return a non-null DTM reference.
247     */
248  public DTM getDTM(Source source, boolean unique,
249                    DTMWSFilter whiteSpaceFilter, boolean incremental,
250                    boolean doIndexing, boolean hasUserReader, int size,
251                    boolean buildIdIndex, boolean newNameTable)
252  {
253        if(DEBUG && null != source) {
254            System.out.println("Starting "+
255                         (unique ? "UNIQUE" : "shared")+
256                         " source: "+source.getSystemId());
257        }
258
259        int dtmPos = getFirstFreeDTMID();
260        int documentID = dtmPos << IDENT_DTM_NODE_BITS;
261
262        if ((null != source) && source instanceof StAXSource) {
263            final StAXSource staxSource = (StAXSource)source;
264            StAXEvent2SAX staxevent2sax = null;
265            StAXStream2SAX staxStream2SAX = null;
266            if (staxSource.getXMLEventReader() != null) {
267                final XMLEventReader xmlEventReader = staxSource.getXMLEventReader();
268                staxevent2sax = new StAXEvent2SAX(xmlEventReader);
269            } else if (staxSource.getXMLStreamReader() != null) {
270                final XMLStreamReader xmlStreamReader = staxSource.getXMLStreamReader();
271                staxStream2SAX = new StAXStream2SAX(xmlStreamReader);
272            }
273
274            SAXImpl dtm;
275
276            if (size <= 0) {
277                dtm = new SAXImpl(this, source, documentID,
278                                  whiteSpaceFilter, null, doIndexing,
279                                  DTMDefaultBase.DEFAULT_BLOCKSIZE,
280                                  buildIdIndex, newNameTable);
281            } else {
282                dtm = new SAXImpl(this, source, documentID,
283                                  whiteSpaceFilter, null, doIndexing,
284                                  size, buildIdIndex, newNameTable);
285            }
286
287            dtm.setDocumentURI(source.getSystemId());
288
289            addDTM(dtm, dtmPos, 0);
290
291            try {
292                if (staxevent2sax != null) {
293                    staxevent2sax.setContentHandler(dtm);
294                    staxevent2sax.parse();
295                }
296                else if (staxStream2SAX != null) {
297                    staxStream2SAX.setContentHandler(dtm);
298                    staxStream2SAX.parse();
299                }
300
301            }
302            catch (RuntimeException re) {
303                throw re;
304            }
305            catch (Exception e) {
306                throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
307            }
308
309            return dtm;
310        }else if ((null != source) && source instanceof DOMSource) {
311            final DOMSource domsrc = (DOMSource) source;
312            final org.w3c.dom.Node node = domsrc.getNode();
313            final DOM2SAX dom2sax = new DOM2SAX(node);
314
315            SAXImpl dtm;
316
317            if (size <= 0) {
318                dtm = new SAXImpl(this, source, documentID,
319                                  whiteSpaceFilter, null, doIndexing,
320                                  DTMDefaultBase.DEFAULT_BLOCKSIZE,
321                                  buildIdIndex, newNameTable);
322            } else {
323                dtm = new SAXImpl(this, source, documentID,
324                                  whiteSpaceFilter, null, doIndexing,
325                                  size, buildIdIndex, newNameTable);
326            }
327
328            dtm.setDocumentURI(source.getSystemId());
329
330            addDTM(dtm, dtmPos, 0);
331
332            dom2sax.setContentHandler(dtm);
333
334            try {
335                dom2sax.parse();
336            }
337            catch (RuntimeException re) {
338                throw re;
339            }
340            catch (Exception e) {
341                throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
342            }
343
344            return dtm;
345        }
346        else
347        {
348            boolean isSAXSource = (null != source)
349                                  ? (source instanceof SAXSource) : true;
350            boolean isStreamSource = (null != source)
351                                  ? (source instanceof StreamSource) : false;
352
353            if (isSAXSource || isStreamSource) {
354                XMLReader reader;
355                InputSource xmlSource;
356
357                if (null == source) {
358                    xmlSource = null;
359                    reader = null;
360                    hasUserReader = false;  // Make sure the user didn't lie
361                }
362                else {
363                    reader = getXMLReader(source);
364                    xmlSource = SAXSource.sourceToInputSource(source);
365
366                    String urlOfSource = xmlSource.getSystemId();
367
368                    if (null != urlOfSource) {
369                        try {
370                            urlOfSource = SystemIDResolver.getAbsoluteURI(urlOfSource);
371                        }
372                        catch (Exception e) {
373                            // %REVIEW% Is there a better way to send a warning?
374                            System.err.println("Can not absolutize URL: " + urlOfSource);
375                        }
376
377                        xmlSource.setSystemId(urlOfSource);
378                    }
379                }
380
381                // Create the basic SAX2DTM.
382                SAXImpl dtm;
383                if (size <= 0) {
384                    dtm = new SAXImpl(this, source, documentID, whiteSpaceFilter,
385                                      null, doIndexing,
386                                      DTMDefaultBase.DEFAULT_BLOCKSIZE,
387                                      buildIdIndex, newNameTable);
388                } else {
389                    dtm = new SAXImpl(this, source, documentID, whiteSpaceFilter,
390                            null, doIndexing, size, buildIdIndex, newNameTable);
391                }
392
393                // Go ahead and add the DTM to the lookup table.  This needs to be
394                // done before any parsing occurs. Note offset 0, since we've just
395                // created a new DTM.
396                addDTM(dtm, dtmPos, 0);
397
398                if (null == reader) {
399                    // Then the user will construct it themselves.
400                    return dtm;
401                }
402
403                reader.setContentHandler(dtm.getBuilder());
404
405                if (!hasUserReader || null == reader.getDTDHandler()) {
406                    reader.setDTDHandler(dtm);
407                }
408
409                if(!hasUserReader || null == reader.getErrorHandler()) {
410                    reader.setErrorHandler(dtm);
411                }
412
413                try {
414                    reader.setProperty("http://xml.org/sax/properties/lexical-handler", dtm);
415                }
416                catch (SAXNotRecognizedException e){}
417                catch (SAXNotSupportedException e){}
418
419                try {
420                    reader.parse(xmlSource);
421                }
422                catch (RuntimeException re) {
423                    throw re;
424                }
425                catch (Exception e) {
426                    throw new com.sun.org.apache.xml.internal.utils.WrappedRuntimeException(e);
427                } finally {
428                    if (!hasUserReader) {
429                        releaseXMLReader(reader);
430                    }
431                }
432
433                if (DUMPTREE) {
434                    System.out.println("Dumping SAX2DOM");
435                    dtm.dumpDTM(System.err);
436                }
437
438                return dtm;
439            }
440            else {
441                // It should have been handled by a derived class or the caller
442                // made a mistake.
443                throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NOT_SUPPORTED, new Object[]{source}));
444            }
445        }
446    }
447}
448