001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements. See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership. The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the  "License");
007     * you may not use this file except in compliance with the License.
008     * You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing, software
013     * distributed under the License is distributed on an "AS IS" BASIS,
014     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015     * See the License for the specific language governing permissions and
016     * limitations under the License.
017     */
018    /*
019     * $Id: XSLTCDTMManager.java 468651 2006-10-28 07:04:25Z minchau $
020     */
021    package org.apache.xalan.xsltc.dom;
022    
023    import javax.xml.transform.Source;
024    import javax.xml.transform.dom.DOMSource;
025    import javax.xml.transform.sax.SAXSource;
026    import javax.xml.transform.stream.StreamSource;
027    
028    import org.apache.xml.dtm.DTM;
029    import org.apache.xml.dtm.ref.DTMDefaultBase;
030    import org.apache.xml.dtm.DTMException;
031    import org.apache.xml.dtm.DTMWSFilter;
032    import org.apache.xml.dtm.ref.DTMManagerDefault;
033    import org.apache.xml.res.XMLErrorResources;
034    import org.apache.xml.res.XMLMessages;
035    import org.apache.xml.utils.SystemIDResolver;
036    import org.apache.xalan.xsltc.trax.DOM2SAX;
037    
038    import org.xml.sax.InputSource;
039    import org.xml.sax.SAXNotRecognizedException;
040    import org.xml.sax.SAXNotSupportedException;
041    import org.xml.sax.XMLReader;
042    
043    /**
044     * The default implementation for the DTMManager.
045     */
046    public class XSLTCDTMManager extends DTMManagerDefault
047    {
048            
049        /** The default class name to use as the manager. */
050        private static final String DEFAULT_CLASS_NAME =
051            "org.apache.xalan.xsltc.dom.XSLTCDTMManager";
052    
053        private static final String DEFAULT_PROP_NAME =
054            "org.apache.xalan.xsltc.dom.XSLTCDTMManager";
055    
056        /** Set this to true if you want a dump of the DTM after creation */
057        private static final boolean DUMPTREE = false;
058      
059        /** Set this to true if you want basic diagnostics */
060        private static final boolean DEBUG = false;
061    
062        /**
063         * Constructor DTMManagerDefault
064         *
065         */
066        public XSLTCDTMManager()
067        {
068            super();
069        } 
070    
071        /**
072         * Obtain a new instance of a <code>DTMManager</code>.
073         * This static method creates a new factory instance.
074         * The current implementation just returns a new XSLTCDTMManager instance.
075         */
076        public static XSLTCDTMManager newInstance()
077        {
078            return new XSLTCDTMManager();
079        } 
080    
081        /**
082         * Look up the class that provides the XSLTC DTM Manager service.
083         * The following lookup procedure is used to find the service provider.
084         * <ol>
085         * <li>The value of the
086         * <code>org.apache.xalan.xsltc.dom.XSLTCDTMManager</code> property, is
087         * checked.</li>
088         * <li>The <code>xalan.propeties</code> file is checked for a property
089         * of the same name.</li>
090         * <li>The
091         * <code>META-INF/services/org.apache.xalan.xsltc.dom.XSLTCDTMManager</code>
092         * file is checked.
093         * </ol>
094         * The default is <code>org.apache.xalan.xsltc.dom.XSLTCDTMManager</code>.
095         */
096        public static Class getDTMManagerClass() {
097            Class mgrClass = ObjectFactory.lookUpFactoryClass(DEFAULT_PROP_NAME,
098                                                              null,
099                                                              DEFAULT_CLASS_NAME);
100            // If no class found, default to this one.  (This should never happen -
101            // the ObjectFactory has already been told that the current class is
102            // the default).
103            return (mgrClass != null) ? mgrClass : XSLTCDTMManager.class;
104        }
105    
106        /**
107         * Get an instance of a DTM, loaded with the content from the
108         * specified source.  If the unique flag is true, a new instance will
109         * always be returned.  Otherwise it is up to the DTMManager to return a
110         * new instance or an instance that it already created and may be being used
111         * by someone else.
112         * (I think more parameters will need to be added for error handling, and
113         * entity resolution).
114         *
115         * @param source the specification of the source object.
116         * @param unique true if the returned DTM must be unique, probably because it
117         * is going to be mutated.
118         * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
119         *                         be null.
120         * @param incremental true if the DTM should be built incrementally, if
121         *                    possible.
122         * @param doIndexing true if the caller considers it worth it to use
123         *                   indexing schemes.
124         *
125         * @return a non-null DTM reference.
126         */
127        public DTM getDTM(Source source, boolean unique,
128                          DTMWSFilter whiteSpaceFilter, boolean incremental,
129                          boolean doIndexing)
130        {
131            return getDTM(source, unique, whiteSpaceFilter, incremental,
132                          doIndexing, false, 0, true, false);
133        }
134    
135        /**
136         * Get an instance of a DTM, loaded with the content from the
137         * specified source.  If the unique flag is true, a new instance will
138         * always be returned.  Otherwise it is up to the DTMManager to return a
139         * new instance or an instance that it already created and may be being used
140         * by someone else.
141         * (I think more parameters will need to be added for error handling, and
142         * entity resolution).
143         *
144         * @param source the specification of the source object.
145         * @param unique true if the returned DTM must be unique, probably because it
146         * is going to be mutated.
147         * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
148         *                         be null.
149         * @param incremental true if the DTM should be built incrementally, if
150         *                    possible.
151         * @param doIndexing true if the caller considers it worth it to use
152         *                   indexing schemes.
153         * @param buildIdIndex true if the id index table should be built.
154         * 
155         * @return a non-null DTM reference.
156         */
157        public DTM getDTM(Source source, boolean unique,
158                          DTMWSFilter whiteSpaceFilter, boolean incremental,
159                          boolean doIndexing, boolean buildIdIndex)
160        {
161            return getDTM(source, unique, whiteSpaceFilter, incremental,
162                          doIndexing, false, 0, buildIdIndex, false);
163        }
164      
165        /**
166         * Get an instance of a DTM, loaded with the content from the
167         * specified source.  If the unique flag is true, a new instance will
168         * always be returned.  Otherwise it is up to the DTMManager to return a
169         * new instance or an instance that it already created and may be being used
170         * by someone else.
171         * (I think more parameters will need to be added for error handling, and
172         * entity resolution).
173         *
174         * @param source the specification of the source object.
175         * @param unique true if the returned DTM must be unique, probably because it
176         * is going to be mutated.
177         * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
178         *                         be null.
179         * @param incremental true if the DTM should be built incrementally, if
180         *                    possible.
181         * @param doIndexing true if the caller considers it worth it to use
182         *                   indexing schemes.
183         * @param buildIdIndex true if the id index table should be built.
184         * @param newNameTable true if we want to use a separate ExpandedNameTable
185         *                     for this DTM.
186         * 
187         * @return a non-null DTM reference.
188         */
189      public DTM getDTM(Source source, boolean unique,
190                        DTMWSFilter whiteSpaceFilter, boolean incremental,
191                        boolean doIndexing, boolean buildIdIndex,
192                        boolean newNameTable)
193      {
194        return getDTM(source, unique, whiteSpaceFilter, incremental,
195                      doIndexing, false, 0, buildIdIndex, newNameTable);
196      }
197      
198      /**
199         * Get an instance of a DTM, loaded with the content from the
200         * specified source.  If the unique flag is true, a new instance will
201         * always be returned.  Otherwise it is up to the DTMManager to return a
202         * new instance or an instance that it already created and may be being used
203         * by someone else.
204         * (I think more parameters will need to be added for error handling, and
205         * entity resolution).
206         *
207         * @param source the specification of the source object.
208         * @param unique true if the returned DTM must be unique, probably because it
209         * is going to be mutated.
210         * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
211         *                         be null.
212         * @param incremental true if the DTM should be built incrementally, if
213         *                    possible.
214         * @param doIndexing true if the caller considers it worth it to use
215         *                   indexing schemes.
216         * @param hasUserReader true if <code>source</code> is a
217         *                      <code>SAXSource</code> object that has an
218         *                      <code>XMLReader</code>, that was specified by the
219         *                      user.
220         * @param size  Specifies initial size of tables that represent the DTM
221         * @param buildIdIndex true if the id index table should be built.
222         *
223         * @return a non-null DTM reference.
224         */
225        public DTM getDTM(Source source, boolean unique,
226                          DTMWSFilter whiteSpaceFilter, boolean incremental,
227                          boolean doIndexing, boolean hasUserReader, int size,
228                          boolean buildIdIndex)
229        {
230          return getDTM(source, unique, whiteSpaceFilter, incremental,
231                        doIndexing, hasUserReader, size,
232                        buildIdIndex, false);
233      }
234      
235      /**
236         * Get an instance of a DTM, loaded with the content from the
237         * specified source.  If the unique flag is true, a new instance will
238         * always be returned.  Otherwise it is up to the DTMManager to return a
239         * new instance or an instance that it already created and may be being used
240         * by someone else.
241         * (I think more parameters will need to be added for error handling, and
242         * entity resolution).
243         *
244         * @param source the specification of the source object.
245         * @param unique true if the returned DTM must be unique, probably because it
246         * is going to be mutated.
247         * @param whiteSpaceFilter Enables filtering of whitespace nodes, and may
248         *                         be null.
249         * @param incremental true if the DTM should be built incrementally, if
250         *                    possible.
251         * @param doIndexing true if the caller considers it worth it to use
252         *                   indexing schemes.
253         * @param hasUserReader true if <code>source</code> is a
254         *                      <code>SAXSource</code> object that has an
255         *                      <code>XMLReader</code>, that was specified by the
256         *                      user.
257         * @param size  Specifies initial size of tables that represent the DTM
258         * @param buildIdIndex true if the id index table should be built.
259         * @param newNameTable true if we want to use a separate ExpandedNameTable
260         *                     for this DTM.
261         *
262         * @return a non-null DTM reference.
263         */
264      public DTM getDTM(Source source, boolean unique,
265                        DTMWSFilter whiteSpaceFilter, boolean incremental,
266                        boolean doIndexing, boolean hasUserReader, int size,
267                        boolean buildIdIndex, boolean newNameTable)
268      {
269            if(DEBUG && null != source) {
270                System.out.println("Starting "+
271                             (unique ? "UNIQUE" : "shared")+
272                             " source: "+source.getSystemId());
273            }
274    
275            int dtmPos = getFirstFreeDTMID();
276            int documentID = dtmPos << IDENT_DTM_NODE_BITS;
277    
278            if ((null != source) && source instanceof DOMSource)
279            {
280                final DOMSource domsrc = (DOMSource) source;
281                final org.w3c.dom.Node node = domsrc.getNode();
282                final DOM2SAX dom2sax = new DOM2SAX(node);
283          
284                SAXImpl dtm;
285    
286                if (size <= 0) {
287                    dtm = new SAXImpl(this, source, documentID,
288                                      whiteSpaceFilter, null, doIndexing, 
289                                      DTMDefaultBase.DEFAULT_BLOCKSIZE,
290                                      buildIdIndex, newNameTable);
291                } else {
292                    dtm = new SAXImpl(this, source, documentID,
293                                      whiteSpaceFilter, null, doIndexing, 
294                                      size, buildIdIndex, newNameTable);
295                }
296          
297                dtm.setDocumentURI(source.getSystemId());
298    
299                addDTM(dtm, dtmPos, 0);
300          
301                dom2sax.setContentHandler(dtm);
302          
303                try {
304                    dom2sax.parse();
305                }
306                catch (RuntimeException re) {
307                    throw re;
308                }
309                catch (Exception e) {
310                    throw new org.apache.xml.utils.WrappedRuntimeException(e);
311                }
312          
313                return dtm;
314            }
315            else
316            {
317                boolean isSAXSource = (null != source)
318                                      ? (source instanceof SAXSource) : true;
319                boolean isStreamSource = (null != source)
320                                      ? (source instanceof StreamSource) : false;
321    
322                if (isSAXSource || isStreamSource) {
323                    XMLReader reader;
324                    InputSource xmlSource;
325    
326                    if (null == source) {
327                        xmlSource = null;
328                        reader = null;
329                        hasUserReader = false;  // Make sure the user didn't lie
330                    }
331                    else {
332                        reader = getXMLReader(source);
333                        xmlSource = SAXSource.sourceToInputSource(source);
334    
335                        String urlOfSource = xmlSource.getSystemId();
336    
337                        if (null != urlOfSource) {
338                            try {
339                                urlOfSource = SystemIDResolver.getAbsoluteURI(urlOfSource);
340                            }
341                            catch (Exception e) {
342                                // %REVIEW% Is there a better way to send a warning?
343                                System.err.println("Can not absolutize URL: " + urlOfSource);
344                            }
345    
346                            xmlSource.setSystemId(urlOfSource);
347                        }
348                    }
349    
350                    // Create the basic SAX2DTM.
351                    SAXImpl dtm;
352                    if (size <= 0) {
353                        dtm = new SAXImpl(this, source, documentID, whiteSpaceFilter,
354                                          null, doIndexing, 
355                                          DTMDefaultBase.DEFAULT_BLOCKSIZE,
356                                          buildIdIndex, newNameTable);
357                    } else {
358                        dtm = new SAXImpl(this, source, documentID, whiteSpaceFilter,
359                                null, doIndexing, size, buildIdIndex, newNameTable);
360                    }
361    
362                    // Go ahead and add the DTM to the lookup table.  This needs to be
363                    // done before any parsing occurs. Note offset 0, since we've just
364                    // created a new DTM.
365                    addDTM(dtm, dtmPos, 0);
366    
367                    if (null == reader) {
368                        // Then the user will construct it themselves.
369                        return dtm;
370                    }
371    
372                    reader.setContentHandler(dtm.getBuilder());
373                    
374                    if (!hasUserReader || null == reader.getDTDHandler()) {
375                        reader.setDTDHandler(dtm);
376                    }
377                    
378                    if(!hasUserReader || null == reader.getErrorHandler()) {
379                        reader.setErrorHandler(dtm);
380                    }
381    
382                    try {
383                        reader.setProperty("http://xml.org/sax/properties/lexical-handler", dtm);
384                    }
385                    catch (SAXNotRecognizedException e){}
386                    catch (SAXNotSupportedException e){}
387    
388                    try {
389                        reader.parse(xmlSource);
390                    }
391                    catch (RuntimeException re) {
392                        throw re;
393                    }
394                    catch (Exception e) {
395                        throw new org.apache.xml.utils.WrappedRuntimeException(e);
396                    } finally {
397                        if (!hasUserReader) {
398                            releaseXMLReader(reader);
399                        }
400                    }
401    
402                    if (DUMPTREE) {
403                        System.out.println("Dumping SAX2DOM");
404                        dtm.dumpDTM(System.err);
405                    }
406    
407                    return dtm;
408                }
409                else {
410                    // It should have been handled by a derived class or the caller
411                    // made a mistake.
412                    throw new DTMException(XMLMessages.createXMLMessage(XMLErrorResources.ER_NOT_SUPPORTED, new Object[]{source}));
413                }
414            }
415        }
416    }