1 /* Woodstox XML processor
2  *
3  * Copyright (c) 2004- Tatu Saloranta, tatu.saloranta@iki.fi
4  *
5  * Licensed under the License specified in the file LICENSE which is
6  * included with the source code.
7  * You may not use this file except in compliance with the License.
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */

15
16 package com.ctc.wstx.stax;
17
18 import java.io.*;
19 import java.net.URL;
20
21 import javax.xml.stream.*;
22 import javax.xml.stream.util.XMLEventAllocator;
23 import javax.xml.transform.dom.DOMSource;
24 import javax.xml.transform.sax.SAXSource;
25 import javax.xml.transform.stream.StreamSource;
26
27 import org.xml.sax.InputSource;
28 import org.codehaus.stax2.XMLEventReader2;
29 import org.codehaus.stax2.XMLInputFactory2;
30 import org.codehaus.stax2.XMLStreamReader2;
31 import org.codehaus.stax2.io.Stax2Source;
32 import org.codehaus.stax2.io.Stax2ByteArraySource;
33 import org.codehaus.stax2.ri.Stax2FilteredStreamReader;
34 import org.codehaus.stax2.ri.Stax2ReaderAdapter;
35 import org.codehaus.stax2.ri.evt.Stax2EventReaderAdapter;
36 import org.codehaus.stax2.ri.evt.Stax2FilteredEventReader;
37
38 import com.ctc.wstx.api.ReaderConfig;
39 import com.ctc.wstx.api.WstxInputProperties;
40 import com.ctc.wstx.cfg.InputConfigFlags;
41 import com.ctc.wstx.cfg.XmlConsts;
42 import com.ctc.wstx.dtd.DTDId;
43 import com.ctc.wstx.dtd.DTDSubset;
44 import com.ctc.wstx.dom.WstxDOMWrappingReader;
45 import com.ctc.wstx.evt.DefaultEventAllocator;
46 import com.ctc.wstx.evt.WstxEventReader;
47 import com.ctc.wstx.exc.WstxIOException;
48 import com.ctc.wstx.io.*;
49 import com.ctc.wstx.sr.ValidatingStreamReader;
50 import com.ctc.wstx.sr.ReaderCreator;
51 import com.ctc.wstx.util.DefaultXmlSymbolTable;
52 import com.ctc.wstx.util.SimpleCache;
53 import com.ctc.wstx.util.SymbolTable;
54 import com.ctc.wstx.util.URLUtil;
55
56 /**
57  * Factory for creating various Stax objects (stream/event reader,
58  * writer).
59  *
60  *<p>
61  * Currently supported configuration options fall into two categories. First,
62  * all properties from {@link XMLInputFactory} (such as, say,
63  * {@link XMLInputFactory#IS_NAMESPACE_AWARE}) are at least recognized, and
64  * most are supported. Second, there are additional properties, defined in
65  * constant class {@link WstxInputProperties}, that are supported.
66  * See {@link WstxInputProperties} for further explanation of these 'custom'
67  * properties.
68  *
69  * @author Tatu Saloranta
70  */

71 public class WstxInputFactory
72     extends XMLInputFactory2
73     implements ReaderCreator,
74                InputConfigFlags
75 {
76     /**
77      * Let's limit max size to 3/4 of 16k, since this corresponds
78      * to 64k main hash index. This should not be too low, but could
79      * perhaps be further lowered?
80      */

81     final static int MAX_SYMBOL_TABLE_SIZE = 12000;
82
83     /**
84      * Number of generations should not matter as much as raw
85      * size... but let's still cap it at some number. 500 generations
86      * seems reasonable for flushing (note: does not count uses
87      * where no new symbols were added).
88      */

89     final static int MAX_SYMBOL_TABLE_GENERATIONS = 500;
90
91     /*
92     ///////////////////////////////////////////////////////////////////////
93     // Actual storage of configuration settings
94     ///////////////////////////////////////////////////////////////////////
95      */

96
97     /**
98      * Current configurations for this factory
99      */

100     protected final ReaderConfig mConfig;
101
102     // // // Stax - mandated objects:
103
104     protected XMLEventAllocator mAllocator = null;
105
106     // // // Other configuration objects:
107
108     protected SimpleCache<DTDId,DTDSubset> mDTDCache = null;
109
110     /*
111     ///////////////////////////////////////////////////////////////////////
112     // Objects shared by actual parsers
113     ///////////////////////////////////////////////////////////////////////
114      */

115
116     /**
117      * 'Root' symbol table, used for creating actual symbol table instances,
118      * but never as is.
119      */

120     final static SymbolTable mRootSymbols = DefaultXmlSymbolTable.getInstance();
121     static {
122         /* By default, let's enable intern()ing of names (element, attribute,
123          * prefixes) added to symbol table. This is likely to make some
124          * access (attr by QName) and comparison of element/attr names
125          * more efficient. Although it will add some overhead on adding
126          * new symbols to symbol table that should be rather negligible.
127          *
128          * Also note that always doing intern()ing allows for more efficient
129          * access during DTD validation.
130          */

131         mRootSymbols.setInternStrings(true);
132     }
133
134     /**
135      * Actual current 'parent' symbol table; concrete instances will be
136      * created from this instance using <code>makeChild</code> method
137      */

138     private SymbolTable mSymbols = mRootSymbols;
139
140     /*
141     ///////////////////////////////////////////////////////////////////////
142     // Life-cycle:
143     ///////////////////////////////////////////////////////////////////////
144      */

145
146     public WstxInputFactory() {
147         mConfig = ReaderConfig.createFullDefaults();
148     }
149
150     /**
151      * Method that can be used to ensure that specified symbol is
152      * contained in the shared symbol table. This may occasionally
153      * be useful in pre-populating symbols; although it is unlikely
154      * to be commonly useful.
155      * 
156      * @since 4.2.1
157      */

158     public void addSymbol(String symbol)
159     {
160         synchronized (mSymbols) {
161             mSymbols.findSymbol(symbol);
162         }
163     }
164     
165     /*
166     ///////////////////////////////////////////////////////////////////////
167     // ReaderCreator implementation
168     ///////////////////////////////////////////////////////////////////////
169      */

170
171     // // // Configuration access methods:
172
173     /**
174      * Method readers created by this factory call, if DTD caching is
175      * enabled, to see if an external DTD (subset) has been parsed
176      * and cached earlier.
177      */

178     @Override
179     public synchronized DTDSubset findCachedDTD(DTDId id)
180     {
181         return (mDTDCache == null) ? null : mDTDCache.find(id);
182     }
183
184     // // // Callbacks for updating shared information
185
186     /**
187      * Method individual parsers call to pass back symbol table that
188      * they updated, which may be useful for other parser to reuse, instead
189      * of previous base symbol table.
190      *<p>
191      * Note: parser is only to call this method, if passed-in symbol
192      * table was modified, ie new entry/ies were added in addition to
193      * whatever was in root table.
194      */

195     @Override
196     public synchronized void updateSymbolTable(SymbolTable t)
197     {
198         SymbolTable curr = mSymbols;
199         /* Let's only add if table was direct descendant; this prevents
200          * siblings from keeping overwriting settings (multiple direct
201          * children have additional symbols added)
202          */

203         if (t.isDirectChildOf(curr)) {
204             /* 07-Apr-2006, TSa: Actually, since huge symbol tables
205              *    might become hindrance more than benefit (either in
206              *    pathological cases with random names; or with very
207              *    long running processes), let's actually limit both
208              *    number of generations, and, more imporantly, maximum
209              *    size of the symbol table
210              */

211             if (t.size() > MAX_SYMBOL_TABLE_SIZE || 
212                 t.version() > MAX_SYMBOL_TABLE_GENERATIONS) {
213                 // If so, we'll reset from bare defaults
214                 mSymbols = mRootSymbols;
215 //System.err.println("DEBUG: !!!! XXXXX Symbol Table Flush: size: "+t.size()+"; version: "+t.version());
216             } else {
217                 mSymbols.mergeChild(t);
218 //System.err.println("Debug: new symbol table: size: "+t.size()+"; version: "+t.version());
219             }
220         }
221 //else System.err.println("Debug: skipping symbol table update");
222     }
223
224     @Override
225     public synchronized void addCachedDTD(DTDId id, DTDSubset extSubset)
226     {
227         if (mDTDCache == null) {
228             mDTDCache = new SimpleCache<DTDId,DTDSubset>(mConfig.getDtdCacheSize());
229         }
230         mDTDCache.add(id, extSubset);
231     }
232
233     /*
234     ///////////////////////////////////////////////////////////////////////
235     // Stax, XMLInputFactory; factory methods
236     ///////////////////////////////////////////////////////////////////////
237      */

238
239     // // // Filtered reader factory methods
240
241     @Override
242     public XMLEventReader createFilteredReader(XMLEventReader reader, EventFilter filter) {
243         return new Stax2FilteredEventReader(Stax2EventReaderAdapter.wrapIfNecessary(reader), filter);
244     }
245
246     @Override
247     public XMLStreamReader createFilteredReader(XMLStreamReader reader, StreamFilter filter)
248         throws XMLStreamException
249     {
250         Stax2FilteredStreamReader fr = new Stax2FilteredStreamReader(reader, filter);
251         /* [WSTX-111] As per Stax 1.0 TCK, apparently the filtered
252          *   reader is expected to be automatically forwarded to the first
253          *   acceptable event. This is different from the way RI works, but
254          *   since specs don't say anything about filtered readers, let's
255          *   consider TCK to be "more formal" for now, and implement that
256          *   behavior.
257          */

258         if (!filter.accept(fr)) { // START_DOCUMENT ok?
259             // Ok, nope, this should do the trick:
260             fr.next();
261         }
262         return fr;
263     }
264
265     // // // Event reader factory methods
266
267     @Override
268     public XMLEventReader createXMLEventReader(InputStream in)
269         throws XMLStreamException
270     {
271         // false for auto-close, since caller has access to the input stream
272         return new WstxEventReader(createEventAllocator(),
273                 createSR(null, in, nulltruefalse));
274     }
275
276     @Override
277     public XMLEventReader createXMLEventReader(InputStream in, String enc)
278         throws XMLStreamException
279     {
280         // false for auto-close, since caller has access to the input stream
281         return new WstxEventReader(createEventAllocator(),
282                                    createSR(null, in, enc, truefalse));
283     }
284
285     @Override
286     public XMLEventReader createXMLEventReader(Reader r)
287         throws XMLStreamException
288     {
289         // false for auto-close, since caller has access to the input stream
290         return new WstxEventReader(createEventAllocator(),
291                                    createSR(null, r, truefalse));
292     }
293
294     @Override
295     public XMLEventReader createXMLEventReader(javax.xml.transform.Source source)
296         throws XMLStreamException
297     {
298         return new WstxEventReader(createEventAllocator(),
299                                    createSR(source, true));
300     }
301
302     @Override
303     public XMLEventReader createXMLEventReader(String systemId, InputStream in)
304         throws XMLStreamException
305     {
306         // false for auto-close, since caller has access to the input stream
307         return new WstxEventReader(createEventAllocator(),
308                 createSR(SystemId.construct(systemId), in, nulltruefalse));
309     }
310
311     @Override
312     public XMLEventReader createXMLEventReader(String systemId, Reader r)
313         throws XMLStreamException
314     {
315         // false for auto-close, since caller has access to the reader
316         return new WstxEventReader(createEventAllocator(),
317                 createSR(SystemId.construct(systemId), r, truefalse));
318     }
319
320     @Override
321     public XMLEventReader createXMLEventReader(XMLStreamReader sr)
322         throws XMLStreamException
323     {
324         return new WstxEventReader(createEventAllocator(), Stax2ReaderAdapter.wrapIfNecessary(sr));
325     }
326
327     // // // Stream reader factory methods
328
329     @Override
330     public XMLStreamReader createXMLStreamReader(InputStream in)
331         throws XMLStreamException
332     {
333         // false for auto-close, since caller has access to the input stream
334         return createSR(null, in, nullfalsefalse);
335     }
336     
337     @Override
338     public XMLStreamReader createXMLStreamReader(InputStream in, String enc)
339         throws XMLStreamException
340     {
341         // false for auto-close, since caller has access to the input stream
342         return createSR(null, in, enc, falsefalse);
343     }
344
345     @Override
346     public XMLStreamReader createXMLStreamReader(Reader r)
347         throws XMLStreamException
348     {
349         // false for auto-close, since caller has access to the reader
350         return createSR(null, r, falsefalse);
351     }
352
353     @Override
354     public XMLStreamReader createXMLStreamReader(javax.xml.transform.Source src)
355         throws XMLStreamException
356     {
357         // false -> not for event. No definition for auto-close; called method will decide
358         return createSR(src, false);
359     }
360
361     @Override
362     public XMLStreamReader createXMLStreamReader(String systemId, InputStream in)
363         throws XMLStreamException
364     {
365         // false for auto-close, since caller has access to the input stream
366         return createSR(SystemId.construct(systemId), in, nullfalsefalse);
367     }
368
369     @Override
370     public XMLStreamReader createXMLStreamReader(String systemId, Reader r)
371         throws XMLStreamException
372     {
373         // false for auto-close, since caller has access to the Reader
374         return createSR(SystemId.construct(systemId), r, falsefalse);
375     }
376
377     /*
378     ///////////////////////////////////////////////////////////////////////
379     // Stax, XMLInputFactory; generic accessors/mutators
380     ///////////////////////////////////////////////////////////////////////
381      */

382
383     @Override
384     public Object getProperty(String name)
385     {
386         Object ob = mConfig.getProperty(name);
387
388         if (ob == null) {
389             if (name.equals(XMLInputFactory.ALLOCATOR)) {
390                 // Event allocator not available via J2ME subset...
391                 return getEventAllocator();
392             }
393         }
394         return ob;
395     }
396
397     @Override
398     public void setProperty(String propName, Object value)
399     {
400         if (!mConfig.setProperty(propName, value)) {
401             if (XMLInputFactory.ALLOCATOR.equals(propName)) {
402                 setEventAllocator((XMLEventAllocator) value);
403             }
404         }
405     } 
406
407     @Override
408     public XMLEventAllocator getEventAllocator() {
409         return mAllocator;
410     }
411     
412     @Override
413     public XMLReporter getXMLReporter() {
414         return mConfig.getXMLReporter();
415     }
416
417     @Override
418     public XMLResolver getXMLResolver() {
419         return mConfig.getXMLResolver();
420     }
421
422     @Override
423     public boolean isPropertySupported(String name) {
424         return mConfig.isPropertySupported(name);
425     }
426
427     @Override
428     public void setEventAllocator(XMLEventAllocator allocator) {
429         mAllocator = allocator;
430     }
431
432     @Override
433     public void setXMLReporter(XMLReporter r) {
434         mConfig.setXMLReporter(r);
435     }
436
437     /**
438      * Note: it's preferable to use Wstx-specific
439      * {@link ReaderConfig#setEntityResolver}
440      * instead, if possible, since this just wraps passed in resolver.
441      */

442     @Override
443     public void setXMLResolver(XMLResolver r) {
444         mConfig.setXMLResolver(r);
445     }
446
447     /*
448     ///////////////////////////////////////////////////////////////////////
449     // Stax2 implementation
450     ///////////////////////////////////////////////////////////////////////
451      */

452
453     // // // Stax2, additional factory methods:
454
455     @Override
456     public XMLEventReader2 createXMLEventReader(URL src)
457         throws XMLStreamException
458     {
459         /* true for auto-close, since caller has no access to the underlying
460          * input stream created from the URL
461          */

462         return new WstxEventReader(createEventAllocator(),
463                                    createSR(createPrivateConfig(), src, truetrue));
464     }
465
466     @Override
467     public XMLEventReader2 createXMLEventReader(File f)
468         throws XMLStreamException
469     {
470         /* true for auto-close, since caller has no access to the underlying
471          * input stream created from the File
472          */

473         return new WstxEventReader(createEventAllocator(),
474                                    createSR(f, truetrue));
475     }
476
477     @Override
478     public XMLStreamReader2 createXMLStreamReader(URL src)
479         throws XMLStreamException
480     {
481         /* true for auto-close, since caller has no access to the underlying
482          * input stream created from the URL
483          */

484         return createSR(createPrivateConfig(), src, falsetrue);
485     }
486
487     /**
488      * Convenience factory method that allows for parsing a document
489      * stored in the specified file.
490      */

491     @Override
492     public XMLStreamReader2 createXMLStreamReader(File f)
493         throws XMLStreamException
494     {
495         /* true for auto-close, since caller has no access to the underlying
496          * input stream created from the File
497          */

498         return createSR(f, falsetrue);
499     }
500
501     // // // Stax2 "Profile" mutators
502
503     @Override
504     public void configureForXmlConformance() {
505         mConfig.configureForXmlConformance();
506     }
507
508     @Override
509     public void configureForConvenience() {
510         mConfig.configureForConvenience();
511     }
512
513     @Override
514     public void configureForSpeed() {
515         mConfig.configureForSpeed();
516     }
517
518     @Override
519     public void configureForLowMemUsage() {
520         mConfig.configureForLowMemUsage();
521     }
522
523     @Override
524     public void configureForRoundTripping() {
525         mConfig.configureForRoundTripping();
526     }
527
528     /*
529     ///////////////////////////////////////////////////////////////////////
530     // Woodstox-specific configuration access
531     ///////////////////////////////////////////////////////////////////////
532      */

533
534     public ReaderConfig getConfig() {
535         return mConfig;
536     }
537
538     /*
539     ///////////////////////////////////////////////////////////////////////
540     // Internal methods
541     ///////////////////////////////////////////////////////////////////////
542      */

543
544     /**
545      * Bottleneck method used for creating ALL full stream reader instances
546      * (via other createSR() methods and directly)
547      *
548      * @param forER True, if the reader is being constructed to be used
549      *   by an event reader; false if it is not (or the purpose is not known)
550      * @param autoCloseInput Whether the underlying input source should be
551      *   actually closed when encountering EOF, or when <code>close()</code>
552      *   is called. Will be true for input sources that are automatically
553      *   managed by stream reader (input streams created for
554      *   {@link java.net.URL} and {@link java.io.File} arguments, or when
555      *   configuration settings indicate auto-closing is to be enabled
556      *   (the default value is false as per Stax 1.0 specs).
557      */

558     @SuppressWarnings("resource")
559     private XMLStreamReader2 doCreateSR(ReaderConfig cfg, SystemId systemId,
560             InputBootstrapper bs,  boolean forER, boolean autoCloseInput)
561         throws XMLStreamException
562     {
563         /* Automatic closing of input: will happen always for some input
564          * types (ones application has no direct access to; but can also
565          * be explicitly enabled.
566          */

567         if (!autoCloseInput) {
568             autoCloseInput = cfg.willAutoCloseInput();
569         }
570
571         Reader r;
572         try {
573             r = bs.bootstrapInput(cfg, true, XmlConsts.XML_V_UNKNOWN);
574             if (bs.declaredXml11()) {
575                 cfg.enableXml11(true);
576             }
577         } catch (IOException ie) {
578             throw new WstxIOException(ie);
579         }
580
581         /* null -> no public id available
582          * false -> don't close the reader when scope is closed.
583          */

584         BranchingReaderSource input = InputSourceFactory.constructDocumentSource
585             (cfg, bs, null, systemId, r, autoCloseInput);
586
587         return ValidatingStreamReader.createValidatingStreamReader(input, this, cfg, bs, forER);
588     }
589
590     /**
591      * Method that is eventually called to create a (full) stream read
592      * instance.
593      *<p>
594      * Note: defined as public method because it needs to be called by
595      * SAX implementation.
596      *
597      * @param systemId System id used for this reader (if any)
598      * @param bs Bootstrapper to use for creating actual underlying
599      *    physical reader
600      * @param forER Flag to indicate whether it will be used via
601      *    Event API (will affect some configuration settings), true if it
602      *    will be, false if not (or not known)
603      * @param autoCloseInput Whether the underlying input source should be
604      *   actually closed when encountering EOF, or when <code>close()</code>
605      *   is called. Will be true for input sources that are automatically
606      *   managed by stream reader (input streams created for
607      *   {@link java.net.URL} and {@link java.io.File} arguments, or when
608      *   configuration settings indicate auto-closing is to be enabled
609      *   (the default value is false as per Stax 1.0 specs).
610      */

611     public XMLStreamReader2 createSR(ReaderConfig cfg, String systemId, InputBootstrapper bs,
612             boolean forER, boolean autoCloseInput)
613         throws XMLStreamException
614     {
615         // 16-Aug-2004, TSa: Maybe we have a context?
616         URL src = cfg.getBaseURL();
617
618         // If not, maybe we can derive it from system id?
619         if ((src == null) && (systemId != null && systemId.length() > 0)) {
620             try {
621                 src = URLUtil.urlFromSystemId(systemId);
622             } catch (IOException ie) {
623                 throw new WstxIOException(ie);
624             }
625         }
626         return doCreateSR(cfg, SystemId.construct(systemId, src), bs, forER, autoCloseInput);
627     }
628
629     public XMLStreamReader2 createSR(ReaderConfig cfg, SystemId systemId, InputBootstrapper bs,
630             boolean forER, boolean autoCloseInput)
631         throws XMLStreamException
632     {
633         return doCreateSR(cfg, systemId, bs, forER, autoCloseInput);
634     }
635
636     @SuppressWarnings("resource")
637     protected XMLStreamReader2 createSR(SystemId systemId, InputStream in, String enc,
638             boolean forER, boolean autoCloseInput)
639         throws XMLStreamException
640     {
641         // sanity check:
642         if (in == null) {
643             throw new IllegalArgumentException("Null InputStream is not a valid argument");
644         }
645         ReaderConfig cfg = createPrivateConfig();
646         if (enc == null || enc.length() == 0) {
647             return createSR(cfg, systemId, StreamBootstrapper.getInstance
648                             (null, systemId, in), forER, autoCloseInput);
649         }
650
651         /* !!! 17-Feb-2006, TSa: We don't yet know if it's xml 1.0 or 1.1;
652          *   so have to specify 1.0 (which is less restrictive WRT input
653          *   streams). Would be better to let bootstrapper deal with it
654          *   though:
655          */

656         Reader r = DefaultInputResolver.constructOptimizedReader(cfg, in, false, enc);
657         return createSR(cfg, systemId, ReaderBootstrapper.getInstance
658                         (null, systemId, r, enc), forER, autoCloseInput);
659     }
660
661     protected XMLStreamReader2 createSR(ReaderConfig cfg, URL src,
662             boolean forER, boolean autoCloseInput)
663         throws XMLStreamException
664     {
665         final SystemId systemId = SystemId.construct(src);
666         try {
667             return createSR(cfg, systemId, URLUtil.inputStreamFromURL(src),
668                     forER, autoCloseInput);
669         } catch (IOException ioe) {
670             throw new WstxIOException(ioe);
671         }
672     }
673     
674     private XMLStreamReader2 createSR(ReaderConfig cfg, SystemId systemId,
675             InputStream in, boolean forER, boolean autoCloseInput)
676         throws XMLStreamException
677     {
678         return doCreateSR(cfg, systemId,
679               StreamBootstrapper.getInstance(null, systemId, in),
680               forER, autoCloseInput);
681     }
682
683     protected XMLStreamReader2 createSR(SystemId systemId, Reader r,
684             boolean forER, boolean autoCloseInput)
685         throws XMLStreamException
686     {
687         return createSR(createPrivateConfig(), systemId,
688                 ReaderBootstrapper.getInstance
689                 (null, systemId, r, null), forER, autoCloseInput);
690     }
691
692     @SuppressWarnings("resource")
693     protected XMLStreamReader2 createSR(File f, boolean forER, boolean autoCloseInput)
694         throws XMLStreamException
695     {
696         ReaderConfig cfg = createPrivateConfig();
697         try {
698             /* 18-Nov-2008, TSa: If P_BASE_URL is set, and File reference is
699              *   relative, let's resolve against base...
700              */

701             if (!f.isAbsolute()) {
702                 URL base = cfg.getBaseURL();
703                 if (base != null) {
704                     URL src = new URL(base, f.getPath());
705                     return createSR(cfg, SystemId.construct(src), URLUtil.inputStreamFromURL(src),
706                             forER, autoCloseInput);
707                 }
708             }
709             final SystemId systemId = SystemId.construct(URLUtil.toURL(f));
710             return createSR(cfg, systemId, new FileInputStream(f), forER, autoCloseInput);
711
712         } catch (IOException ie) {
713             throw new WstxIOException(ie);
714         }
715     }
716
717     /**
718      * Another internal factory method, used when dealing with a generic
719      * Source base type. One thing worth noting is that 'auto-closing'
720      * will be enabled if the input source or Reader is constructed (and
721      * thus owned) by Woodstox.
722      *
723      * @param forER True, if the reader is being constructed to be used
724      *   by an event reader; false if it is not (or the purpose is not known)
725      */

726     @SuppressWarnings("resource")
727     protected XMLStreamReader2 createSR(javax.xml.transform.Source src,
728             boolean forER)
729         throws XMLStreamException
730     {
731         ReaderConfig cfg = createPrivateConfig();
732         Reader r = null;
733         InputStream in = null;
734         String pubId = null;
735         String sysId = null;
736         String encoding = null;
737         boolean autoCloseInput;
738
739         InputBootstrapper bs = null;
740
741         if (src instanceof Stax2Source) {
742             Stax2Source ss = (Stax2Source) src;
743             sysId = ss.getSystemId();
744             pubId = ss.getPublicId();
745             encoding = ss.getEncoding();
746
747             try {
748                 /* 11-Nov-2008, TSa: Let's add optimized handling for byte-block
749                  *   source
750                  */

751                 if (src instanceof Stax2ByteArraySource) {
752                     Stax2ByteArraySource bas = (Stax2ByteArraySource) src;
753                     bs = StreamBootstrapper.getInstance(pubId, SystemId.construct(sysId), bas.getBuffer(), bas.getBufferStart(), bas.getBufferEnd());
754                 } else {
755                     in = ss.constructInputStream();
756                     if (in == null) {
757                         r = ss.constructReader();
758                     }
759                 }
760             } catch (IOException ioe) {
761                 throw new WstxIOException(ioe);
762             }
763             /* Caller has no direct access to stream/reader, Woodstox
764              * owns it and thus has to close too
765              */

766             autoCloseInput = true;
767         } else  if (src instanceof StreamSource) {
768             StreamSource ss = (StreamSource) src;
769             sysId = ss.getSystemId();
770             pubId = ss.getPublicId();
771             in = ss.getInputStream();
772             if (in == null) {
773                 r = ss.getReader();
774             }
775             /* Caller still has access to stream/reader; no need to
776              * force auto-close-input
777              */

778             autoCloseInput = cfg.willAutoCloseInput();
779         } else if (src instanceof SAXSource) {
780             SAXSource ss = (SAXSource) src;
781             /* 28-Jan-2006, TSa: Not a complete implementation, but maybe
782              *   even this might help...
783              */

784             sysId = ss.getSystemId();
785             InputSource isrc = ss.getInputSource();
786             if (isrc != null) {
787                 encoding = isrc.getEncoding();
788                 in = isrc.getByteStream();
789                 if (in == null) {
790                     r = isrc.getCharacterStream();
791                 }
792             }
793             /* Caller still has access to stream/reader; no need to
794              * force auto-close-input
795              */

796             autoCloseInput = cfg.willAutoCloseInput();
797         } else if (src instanceof DOMSource) {
798             DOMSource domSrc = (DOMSource) src;
799             // SymbolTable not used by the DOM-based 'reader':
800             return WstxDOMWrappingReader.createFrom(domSrc, cfg);
801         } else {
802             throw new IllegalArgumentException("Can not instantiate Stax reader for XML source type "+src.getClass()+" (unrecognized type)");
803         }
804         if (bs == null) { // may have already created boostrapper...
805             if (r != null) { 
806                 bs = ReaderBootstrapper.getInstance(pubId, SystemId.construct(sysId), r, encoding);
807             } else if (in != null) {
808                 bs = StreamBootstrapper.getInstance(pubId, SystemId.construct(sysId), in);
809             } else if (sysId != null && sysId.length() > 0) {
810                 /* 26-Dec-2008, TSa: If we must construct URL from system id,
811                  *   it means caller will not have access to resulting
812                  *   stream, thus we will force auto-closing.
813                  */

814                 autoCloseInput = true;
815                 try {
816                     return createSR(cfg, URLUtil.urlFromSystemId(sysId),
817                             forER, autoCloseInput);
818                 } catch (IOException ioe) {
819                     throw new WstxIOException(ioe);
820                 }
821             } else {
822                 throw new XMLStreamException("Can not create Stax reader for the Source passed -- neither reader, input stream nor system id was accessible; can not use other types of sources (like embedded SAX streams)");
823             }
824         }
825         return createSR(cfg, sysId, bs, forER, autoCloseInput);
826     }
827
828     protected XMLEventAllocator createEventAllocator() 
829     {
830         // Explicitly set allocate?
831         if (mAllocator != null) {
832             return mAllocator.newInstance();
833         }
834
835         /* Complete or fast one? Note: standard allocator is designed
836          * in such a way that newInstance() need not be called (calling
837          * it wouldn't do anything, anyway)
838          */

839         return mConfig.willPreserveLocation() ?
840             DefaultEventAllocator.getDefaultInstance()
841             : DefaultEventAllocator.getFastInstance();
842     }
843
844     /**
845      * Method called to construct a copy of the factory's configuration
846      * object, such that two will be unlinked (changes to one are not
847      * reflect in the other).
848      *<p>
849      * Note: only public so that other woodstox components outside of
850      * this package can access it.
851      */

852     public ReaderConfig createPrivateConfig()
853     {
854         return mConfig.createNonShared(mSymbols.makeChild());
855     }
856 }
857