1 /* Woodstox XML processor
2 *
3 * Copyright (c) 2004 Tatu Saloranta, tatu.saloranta@iki.fi
4 *
5 * Licensed under the License specified in file LICENSE, included with
6 * 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.evt;
17
18 import java.util.*;
19
20 import javax.xml.namespace.QName;
21 import javax.xml.namespace.NamespaceContext;
22 import javax.xml.stream.*;
23 import javax.xml.stream.events.Attribute;
24 import javax.xml.stream.events.Namespace;
25 import javax.xml.stream.events.XMLEvent;
26 import javax.xml.stream.util.XMLEventAllocator;
27 import javax.xml.stream.util.XMLEventConsumer;
28
29 import org.codehaus.stax2.*;
30 import org.codehaus.stax2.ri.evt.*;
31
32 import com.ctc.wstx.cfg.ErrorConsts;
33 import com.ctc.wstx.dtd.DTDSubset;
34 import com.ctc.wstx.ent.EntityDecl;
35 import com.ctc.wstx.exc.WstxException;
36 import com.ctc.wstx.sr.ElemAttrs;
37 import com.ctc.wstx.sr.ElemCallback;
38 import com.ctc.wstx.sr.StreamReaderImpl;
39 import com.ctc.wstx.util.BaseNsContext;
40
41 /**
42 * Straight-forward implementation of {@link XMLEventAllocator}, to be
43 * used with Woodstox' event reader.
44 *<p>
45 * One of few complications here is the way start elements are constructed.
46 * The pattern used is double-indirection, needed to get a callback from
47 * the stream reader, with data we need for constructing even Object...
48 * but without stream reader having any understanding of event Objects
49 * per se.
50 *<p>
51 * 03-Dec-2004, TSa: One additional twist is that it's now possible to
52 * create slightly faster event handling, by indicating that the
53 * fully accurate Location information is not necessary. If so,
54 * allocator will just use one shared Location object passed to
55 * all event objects constructed.
56 */
57 public class DefaultEventAllocator
58 extends ElemCallback
59 implements XMLEventAllocator, XMLStreamConstants
60 {
61 final static DefaultEventAllocator sStdInstance = new DefaultEventAllocator(true);
62
63 /*
64 ///////////////////////////////////////////////////////////
65 // Configuration
66 ///////////////////////////////////////////////////////////
67 */
68
69 protected final boolean mAccurateLocation;
70
71 /*
72 ///////////////////////////////////////////////////////////
73 // Recycled objects
74 ///////////////////////////////////////////////////////////
75 */
76
77 /**
78 * Last used location info; only relevant to non-accurate-location
79 * allocators.
80 */
81 protected Location mLastLocation = null;
82
83 /**
84 * @param accurateLocation If true, allocator will construct instances
85 * that have accurate location information; if false, instances
86 * will only have some generic shared Location info. Latter option
87 * will reduce memory usage/thrashing a bit, and may improve speed.
88 */
89 protected DefaultEventAllocator(boolean accurateLocation) {
90 mAccurateLocation = accurateLocation;
91 }
92
93 public static DefaultEventAllocator getDefaultInstance() {
94 /* Default (accurate location) instance can be shared as it
95 * has no state
96 */
97 return sStdInstance;
98 }
99
100 public static DefaultEventAllocator getFastInstance() {
101 /* Can not share instances, due to QName caching, as well as because
102 * of Location object related state
103 */
104 return new DefaultEventAllocator(false);
105 }
106
107 /*
108 ///////////////////////////////////////////////////////////
109 // XMLEventAllocator implementation
110 ///////////////////////////////////////////////////////////
111 */
112
113 @Override
114 public XMLEvent allocate(XMLStreamReader r) throws XMLStreamException
115 {
116 Location loc;
117
118 // Need to keep track of accurate location info?
119 if (mAccurateLocation) {
120 loc = r.getLocation();
121 } else {
122 loc = mLastLocation;
123 /* And even if we can just share one instance, we need that
124 * first instance...
125 */
126 if (loc == null) {
127 loc = mLastLocation = r.getLocation();
128 }
129 }
130
131 switch (r.getEventType()) {
132 case CDATA:
133 return new CharactersEventImpl(loc, r.getText(), true);
134 case CHARACTERS:
135 return new CharactersEventImpl(loc, r.getText(), false);
136 case COMMENT:
137 return new CommentEventImpl(loc, r.getText());
138 case DTD:
139 // Not sure if we really need this defensive coding but...
140 if (r instanceof XMLStreamReader2) {
141 XMLStreamReader2 sr2 = (XMLStreamReader2) r;
142 DTDInfo dtd = sr2.getDTDInfo();
143 return new WDTD(loc,
144 dtd.getDTDRootName(),
145 dtd.getDTDSystemId(), dtd.getDTDPublicId(),
146 dtd.getDTDInternalSubset(),
147 (DTDSubset) dtd.getProcessedDTD());
148 }
149 /* No way to get all information... the real big problem is
150 * that of how to access root name: it's obligatory for
151 * DOCTYPE construct. :-/
152 */
153 return new WDTD(loc, null, r.getText());
154
155 case END_DOCUMENT:
156 return new EndDocumentEventImpl(loc);
157
158 case END_ELEMENT:
159 return new EndElementEventImpl(loc, r);
160
161 case PROCESSING_INSTRUCTION:
162 return new ProcInstrEventImpl(loc, r.getPITarget(), r.getPIData());
163 case SPACE:
164 {
165 CharactersEventImpl ch = new CharactersEventImpl(loc, r.getText(), false);
166 ch.setWhitespaceStatus(true);
167 return ch;
168 }
169 case START_DOCUMENT:
170 return new StartDocumentEventImpl(loc, r);
171
172 case START_ELEMENT:
173 {
174 /* Creating the event is bit complicated, as the stream
175 * reader is not to know anything about event objects.
176 * To do this, we do double-indirection, which means that
177 * this object actually gets a callback:
178 */
179 /* 19-Jul-2006, TSa: WSTX-61 points out that the code was
180 * assuming it's always Woodstox reader we had... not
181 * necessarily so.
182 */
183 if (r instanceof StreamReaderImpl) {
184 StreamReaderImpl sr = (StreamReaderImpl) r;
185 BaseStartElement be = (BaseStartElement) sr.withStartElement(this, loc);
186 if (be == null) { // incorrect state
187 throw new WstxException("Trying to create START_ELEMENT when current event is "
188 +ErrorConsts.tokenTypeDesc(sr.getEventType()),
189 loc);
190 }
191 return be;
192 }
193 /* Ok, not woodstox impl, will be bit more work (plus less
194 * efficient, and may miss some info)... but can be done.
195 */
196 NamespaceContext nsCtxt = null;
197 if (r instanceof XMLStreamReader2) {
198 nsCtxt = ((XMLStreamReader2) r).getNonTransientNamespaceContext();
199 }
200 Map<QName,Attribute> attrs;
201 {
202 int attrCount = r.getAttributeCount();
203 if (attrCount < 1) {
204 attrs = null;
205 } else {
206 attrs = new LinkedHashMap<QName, Attribute>();
207 for (int i = 0; i < attrCount; ++i) {
208 QName aname = r.getAttributeName(i);
209 attrs.put(aname, new AttributeEventImpl(loc, aname, r.getAttributeValue(i), r.isAttributeSpecified(i)));
210 }
211 }
212 }
213 List<Namespace> ns;
214 {
215 int nsCount = r.getNamespaceCount();
216 if (nsCount < 1) {
217 ns = null;
218 } else {
219 ns = new ArrayList<Namespace>(nsCount);
220 for (int i = 0; i < nsCount; ++i) {
221 ns.add(NamespaceEventImpl.constructNamespace(loc, r.getNamespacePrefix(i), r.getNamespaceURI(i)));
222 }
223 }
224 }
225
226 return SimpleStartElement.construct(loc, r.getName(), attrs, ns, nsCtxt);
227 }
228
229 case ENTITY_REFERENCE:
230 {
231 /* 19-Jul-2006, TSa: Let's also allow other impls, although
232 * we can't get actual declaration if so...
233 */
234 if (r instanceof StreamReaderImpl) {
235 EntityDecl ed = ((StreamReaderImpl) r).getCurrentEntityDecl();
236 if (ed == null) { // undefined?
237 // We'll still know the name though...
238 return new WEntityReference(loc, r.getLocalName());
239 }
240 return new WEntityReference(loc, ed);
241 }
242 return new WEntityReference(loc, r.getLocalName());
243 }
244
245
246 /* Following 2 types should never get in here; they are directly
247 * handled by DTDReader, and can only be accessed via DTD event
248 * element.
249 */
250 case ENTITY_DECLARATION:
251 case NOTATION_DECLARATION:
252 /* Following 2 types should never get in here; they are directly
253 * handled by the reader, and can only be accessed via start
254 * element.
255 */
256 case NAMESPACE:
257 case ATTRIBUTE:
258 throw new WstxException("Internal error: should not get "
259 +ErrorConsts.tokenTypeDesc(r.getEventType()));
260 default:
261 throw new IllegalStateException("Unrecognized event type "+r.getEventType()+".");
262 }
263 }
264
265 @Override
266 public void allocate(XMLStreamReader r, XMLEventConsumer consumer)
267 throws XMLStreamException
268 {
269 consumer.add(allocate(r));
270 }
271
272 @Override
273 public XMLEventAllocator newInstance() {
274 return new DefaultEventAllocator(mAccurateLocation);
275 }
276
277 /*
278 ///////////////////////////////////////////////////////////
279 // ElemCallback implementation
280 ///////////////////////////////////////////////////////////
281 */
282
283 @Override
284 public Object withStartElement(Location loc, QName name,
285 BaseNsContext nsCtxt, ElemAttrs attrs, boolean wasEmpty)
286 {
287 return new CompactStartElement(loc, name, nsCtxt, attrs);
288 }
289 }
290