1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package net.sf.stax;
25
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.List;
29 import java.util.ArrayList;
30 import java.util.AbstractList;
31
32 import org.xml.sax.Locator;
33 import org.xml.sax.Attributes;
34 import org.xml.sax.ContentHandler;
35 import org.xml.sax.SAXException;
36
37 /***
38 * Lightweight adaptor which translates SAX content events into
39 * StAX form, and provides delegation and id mapping services.
40 *
41 * @author Thomas Down
42 * @author Michael Heuer
43 * @version $Revision: 1.4 $ $Date: 2006/01/02 20:37:34 $
44 */
45 public final class SAX2StAXAdaptor
46 implements ContentHandler
47 {
48 /*** Handler binding stack. */
49 private List handlerBindingStack;
50
51 /*** Current handler binding. */
52 private HandlerBinding currentHandlerBinding;
53
54 /*** Context binding stack. */
55 private List contextBindingStack;
56
57 /*** Current context binding. */
58 private ContextBinding currentContextBinding;
59
60 /*** View on the context binding stack. */
61 private List contextBindingStackView;
62
63 /*** True if <code>endDocument</code> should throw an exception if there are unresolved ids. */
64 private boolean checkIdsResolveFlag;
65
66 /*** Id mapper. */
67 private IdMapperImpl idMapper;
68
69 /*** Result. */
70 private Object result;
71
72 /*** Context. */
73 private StAXContext context;
74
75 {
76 handlerBindingStack = new ArrayList();
77 contextBindingStack = new ArrayList();
78 idMapper = new IdMapperImpl();
79
80 contextBindingStackView = new AbstractList()
81 {
82
83 /*** @see List */
84 public int size()
85 {
86 return contextBindingStack.size() - 1;
87 }
88
89 /*** @see List */
90 public Object get(final int index)
91 {
92 return transduce(contextBindingStack.get(index + 1));
93 }
94
95 /***
96 * Return the context name of the specified item.
97 *
98 * @param item item
99 */
100 private Object transduce(final Object item)
101 {
102 return ((ContextBinding) item).contextName;
103 }
104 };
105
106 context = new StAXContext()
107 {
108 /*** Document locator. */
109 private Locator locator;
110
111
112 /*** @see StAXContext */
113 public IdMapper getIdMapper()
114 {
115 return idMapper;
116 }
117
118 /*** @see StAXContext */
119 public List getContexts()
120 {
121 return contextBindingStackView;
122 }
123
124 /*** @see StAXContext */
125 public Locator getLocator()
126 {
127 return locator;
128 }
129
130 /*** @see StAXContext */
131 public void setLocator(final Locator locator)
132 {
133 this.locator = locator;
134 }
135 };
136 }
137
138
139 /***
140 * Construct a new SAX Content handler which wraps a StAX
141 * handler.
142 *
143 * @param rootHandler root handler
144 * @param checkIdsResolveFlag true if <code>endDocument</code> should throw an exception
145 * if there are unresolved ids
146 * @param existingIds map of existing ids to items
147 */
148 public SAX2StAXAdaptor(final StAXContentHandler rootHandler,
149 final boolean checkIdsResolveFlag,
150 final Map existingIds)
151 {
152 this(rootHandler, checkIdsResolveFlag);
153 idMapper = new IdMapperImpl(existingIds);
154 }
155
156 /***
157 * Construct a new SAX Content handler which wraps a StAX
158 * handler.
159 *
160 * @param rootHandler root handler
161 * @param checkIdsResolveFlag true if <code>endDocument</code> should throw an exception
162 * if there are unresolved ids
163 */
164 public SAX2StAXAdaptor(final StAXContentHandler rootHandler,
165 final boolean checkIdsResolveFlag)
166 {
167 currentHandlerBinding = new HandlerBinding(rootHandler);
168 handlerBindingStack.add(currentHandlerBinding);
169 currentContextBinding = new ContextBinding();
170 contextBindingStack.add(currentContextBinding);
171 this.checkIdsResolveFlag = checkIdsResolveFlag;
172 }
173
174 /***
175 * Construct a new SAX Content handler which wraps a StAX
176 * handler.
177 *
178 * @param rootHandler root handler
179 */
180 public SAX2StAXAdaptor(final StAXContentHandler rootHandler)
181 {
182 this(rootHandler, true);
183 }
184
185
186 /*** @see ContentHandler */
187 public void startDocument()
188 throws SAXException
189 {
190 currentHandlerBinding.getHandler().startTree(context);
191 }
192
193 /*** @see ContentHandler */
194 public void endDocument()
195 throws SAXException
196 {
197 result = currentHandlerBinding.getHandler().endTree(context);
198 if (checkIdsResolveFlag)
199 {
200 Set ids = idMapper.getUnresolvedIds();
201 if (ids.size() > 0)
202 {
203 throw new SAXException("Unresolved Ids: " + ids);
204 }
205 }
206 }
207
208 /*** @see ContentHandler */
209 public void characters(final char[] ch,
210 final int start,
211 final int end)
212 throws SAXException
213 {
214 currentHandlerBinding.getHandler().characters(ch, start, end, context);
215 }
216
217 /*** @see ContentHandler */
218 public void ignorableWhitespace(final char[] ch,
219 final int start,
220 final int end)
221 throws SAXException
222 {
223 currentHandlerBinding.getHandler().ignorableWhitespace(ch, start, end, context);
224 }
225
226 /*** @see ContentHandler */
227 public void startPrefixMapping(final String prefix, final String uri)
228 throws SAXException
229 {
230 currentHandlerBinding.getHandler().startPrefixMapping(prefix, uri, context);
231 }
232
233 /*** @see ContentHandler */
234 public void endPrefixMapping(final String prefix)
235 throws SAXException
236 {
237 currentHandlerBinding.getHandler().endPrefixMapping(prefix, context);
238 }
239
240 /*** @see ContentHandler */
241 public void processingInstruction(final String target, final String data)
242 throws SAXException
243 {
244 currentHandlerBinding.getHandler().processingInstruction(target, data, context);
245 }
246
247 /*** @see ContentHandler */
248 public void setDocumentLocator(final Locator locator)
249 {
250 currentHandlerBinding.getHandler().setDocumentLocator(locator, context);
251 }
252
253 /*** @see ContentHandler */
254 public void skippedEntity(final String name)
255 throws SAXException
256 {
257 currentHandlerBinding.getHandler().skippedEntity(name, context);
258 }
259
260 /*** @see ContentHandler */
261 public void startElement(final String nsURI,
262 final String localName,
263 final String qName,
264 final Attributes attrs)
265 throws SAXException
266 {
267 S2SStAXDelegationContext dctx = new S2SStAXDelegationContext(context);
268 currentHandlerBinding.getHandler().startElement(nsURI,
269 localName,
270 qName,
271 attrs,
272 dctx);
273
274
275 if (dctx.getContext() != null)
276 {
277 currentContextBinding = new ContextBinding(dctx.getContext());
278 contextBindingStack.add(currentContextBinding);
279 }
280 else
281 {
282 currentContextBinding.setCount(currentContextBinding.getCount() + 1);
283 }
284
285
286 if (dctx.getDelegate() != null)
287 {
288 currentHandlerBinding = new HandlerBinding(dctx.getDelegate());
289 handlerBindingStack.add(currentHandlerBinding);
290 currentHandlerBinding.getHandler().startTree(context);
291
292 startElement(nsURI, localName, qName, attrs);
293 }
294 else
295 {
296 currentHandlerBinding.setCount(currentHandlerBinding.getCount() + 1);
297 }
298 }
299
300 /*** @see ContentHandler */
301 public void endElement(final String nsURI,
302 final String localName,
303 final String qName)
304 throws SAXException
305 {
306 currentHandlerBinding.getHandler().endElement(nsURI, localName, qName, null, context);
307
308
309 currentContextBinding.setCount(currentContextBinding.getCount() - 1);
310 while (currentContextBinding.getCount() == 0 && contextBindingStack.size() > 1)
311 {
312 contextBindingStack.remove(contextBindingStack.size() - 1);
313 currentContextBinding = (ContextBinding) contextBindingStack.get(contextBindingStack.size() - 1);
314 }
315
316
317 currentHandlerBinding.setCount(currentHandlerBinding.getCount() - 1);
318 while (currentHandlerBinding.getCount() == 0 && handlerBindingStack.size() > 1)
319 {
320 StAXContentHandler oldHandler = currentHandlerBinding.getHandler();
321 Object res = currentHandlerBinding.getHandler().endTree(context);
322 handlerBindingStack.remove(handlerBindingStack.size() - 1);
323 currentHandlerBinding = (HandlerBinding) handlerBindingStack.get(handlerBindingStack.size() - 1);
324 currentHandlerBinding.getHandler().endElement(nsURI, localName, qName, res, context);
325
326
327
328 currentContextBinding.setCount(currentContextBinding.getCount() - 1);
329 while (currentContextBinding.getCount() == 0 && contextBindingStack.size() > 1)
330 {
331 contextBindingStack.remove(contextBindingStack.size() - 1);
332 currentContextBinding = (ContextBinding) contextBindingStack.get(contextBindingStack.size() - 1);
333 }
334 }
335 }
336
337 /***
338 * SAX2StAX implementation of <b>StAXDelegationContext</b>.
339 */
340 private class S2SStAXDelegationContext
341 implements StAXDelegationContext
342 {
343 /*** StAX context. */
344 private final StAXContext ctx;
345
346 /*** Delegate. */
347 private StAXContentHandler delegate;
348
349 /*** Context. */
350 private String context;
351
352
353 /***
354 * Create a new SAX2StAX implementation of StAXDelegationContext
355 * with the specified StAX context.
356 *
357 * @param ctx StAX context
358 */
359 public S2SStAXDelegationContext(final StAXContext ctx)
360 {
361 this.ctx = ctx;
362 }
363
364
365 /*** @see StAXDelegationContext */
366 public void delegate(final StAXContentHandler handler)
367 throws SAXException
368 {
369 if (handler == null)
370 {
371 throw new SAXException("Tried to delegate to null handler");
372 }
373
374 if (this.delegate != null)
375 {
376 throw new SAXException("Tried to multiply delegate a single StAX element");
377 }
378 this.delegate = handler;
379 }
380
381 /*** @see StAXDelegationContext */
382 public void pushContext(final String context)
383 throws SAXException
384 {
385 if (this.context != null)
386 {
387 throw new SAXException("Tried to push multiple contexts for a single StAX element");
388 }
389 this.context = context;
390 }
391
392 /***
393 * Return the delegate.
394 *
395 * @return the delegate
396 */
397 public StAXContentHandler getDelegate()
398 {
399 return delegate;
400 }
401
402 /***
403 * Return the context.
404 *
405 * @return the context
406 */
407 public String getContext()
408 {
409 return context;
410 }
411
412 /*** @see StAXDelegationContext */
413 public IdMapper getIdMapper()
414 {
415 return ctx.getIdMapper();
416 }
417
418 /*** @see StAXDelegationContext */
419 public List getContexts()
420 {
421 return ctx.getContexts();
422 }
423
424 /*** @see StAXDelegationContext */
425 public Locator getLocator()
426 {
427 return ctx.getLocator();
428 }
429
430 /*** @see StAXDelegationContext */
431 public void setLocator(final Locator locator)
432 {
433 ctx.setLocator(locator);
434 }
435 }
436
437 /***
438 * Memento class binding a handler.
439 */
440 private final class HandlerBinding
441 {
442 /*** Count. */
443 private int count;
444
445 /*** Handler. */
446 private final StAXContentHandler handler;
447
448
449 /***
450 * Create a new binding for the specified handler.
451 *
452 * @param handler handler
453 */
454 private HandlerBinding(final StAXContentHandler handler)
455 {
456 this.handler = handler;
457 this.count = 0;
458 }
459
460
461 /***
462 * Return the count for this handler binding.
463 *
464 * @return the count for this handler binding
465 */
466 public int getCount()
467 {
468 return count;
469 }
470
471 /***
472 * Set the count for this handler binding to <code>count</code>.
473 *
474 * @param count count
475 */
476 public void setCount(final int count)
477 {
478 this.count = count;
479 }
480
481 /***
482 * Return the handler for this binding.
483 *
484 * @return the handler for this binding
485 */
486 public StAXContentHandler getHandler()
487 {
488 return handler;
489 }
490 }
491
492 /***
493 * Memento class binding a context.
494 */
495 private final class ContextBinding
496 {
497 /*** Count. */
498 private int count;
499
500 /*** Context name. */
501 private String contextName;
502
503
504 /***
505 * Create a new context binding.
506 */
507 private ContextBinding()
508 {
509 contextName = "";
510 count = 0;
511 }
512
513 /***
514 * Create a new context binding with the specified context name.
515 *
516 * @param contextName context name
517 */
518 private ContextBinding(final String contextName)
519 {
520 this.contextName = contextName;
521 count = 0;
522 }
523
524
525 /***
526 * Return the count for this context binding.
527 *
528 * @return the count for this context binding
529 */
530 public int getCount()
531 {
532 return count;
533 }
534
535 /***
536 * Set the count for this context binding to <code>count</code>.
537 *
538 * @param count count
539 */
540 public void setCount(final int count)
541 {
542 this.count = count;
543 }
544
545 /***
546 * Return the context name for this binding.
547 *
548 * @return the context name for this binding
549 */
550 public String getContextName()
551 {
552 return contextName;
553 }
554
555 /***
556 * Set the context name for this binding to <code>contextName</code>.
557 *
558 * @param contextName context name
559 */
560 public void setContextName(final String contextName)
561 {
562 this.contextName = contextName;
563 }
564 }
565 }
566