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 }