1
2
3
4
5
6
7
8 package de.nierbeck.timeTrack.model.impl.runtime;
9
10 import java.util.ArrayList;
11 import java.util.Collections;
12 import java.util.Hashtable;
13 import java.util.Iterator;
14 import java.util.List;
15
16 import javax.xml.XMLConstants;
17 import javax.xml.bind.JAXBException;
18 import javax.xml.bind.UnmarshalException;
19 import javax.xml.bind.ValidationEvent;
20 import javax.xml.bind.ValidationEventHandler;
21
22 import org.xml.sax.Attributes;
23 import org.xml.sax.Locator;
24 import org.xml.sax.SAXException;
25 import org.xml.sax.SAXParseException;
26
27 import com.sun.xml.bind.JAXBAssertionError;
28 import com.sun.xml.bind.unmarshaller.Messages;
29 import com.sun.xml.bind.unmarshaller.Tracer;
30 import com.sun.xml.bind.util.AttributesImpl;
31
32 /***
33 * Implementation of {@link UnmarshallerHandler}.
34 *
35 * This object converts SAX events into unmarshaller events and cooridnates the
36 * entire unmarshalling process.
37 *
38 * @author <a href="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
39 */
40 public class SAXUnmarshallerHandlerImpl implements SAXUnmarshallerHandler,
41 UnmarshallingContext {
42 /***
43 * This flag is set to true at the startDocument event and false at the
44 * endDocument event.
45 *
46 * Until the first document is unmarshalled, we don't want to return an
47 * object. So this variable is initialized to true.
48 */
49 private boolean isUnmarshalInProgress = true;
50
51 public SAXUnmarshallerHandlerImpl(UnmarshallerImpl _parent, GrammarInfo _gi) {
52 this.parent = _parent;
53 grammarInfo = _gi;
54 startPrefixMapping("", "");
55
56 }
57
58 private final GrammarInfo grammarInfo;
59
60 public GrammarInfo getGrammarInfo() {
61 return grammarInfo;
62 }
63
64 /***
65 * Returns true if we should be collecting characters in the current
66 * element.
67 */
68 private final boolean shouldCollectText() {
69 return collectText[stackTop];
70 }
71
72 public void startDocument() throws SAXException {
73
74 result = null;
75 handlerLen = 0;
76 patchers = null;
77 patchersLen = 0;
78 aborted = false;
79 isUnmarshalInProgress = true;
80
81 stackTop = 0;
82 elementDepth = 1;
83 }
84
85 public void endDocument() throws SAXException {
86 runPatchers();
87 isUnmarshalInProgress = false;
88 }
89
90 public void startElement(String uri, String local, String qname,
91 Attributes atts) throws SAXException {
92
93
94
95 if (uri == null)
96 uri = "";
97 if (local == null || local.length() == 0)
98 local = qname;
99 if (qname == null || qname.length() == 0)
100 qname = local;
101
102 if (result == null) {
103
104
105 UnmarshallingEventHandler unmarshaller = grammarInfo
106 .createUnmarshaller(uri, local, this);
107 if (unmarshaller == null) {
108
109
110
111
112
113
114
115
116
117 throw new SAXParseException(Messages.format(
118 Messages.UNEXPECTED_ROOT_ELEMENT2, uri, local,
119 computeExpectedRootElements()), getLocator());
120 }
121 result = unmarshaller.owner();
122
123 pushContentHandler(unmarshaller, 0);
124 }
125
126 processText(true);
127
128 getCurrentHandler().enterElement(uri, local, qname, atts);
129 }
130
131 public final void endElement(String uri, String local, String qname)
132 throws SAXException {
133
134
135
136 if (uri == null)
137 uri = "";
138 if (local == null || local.length() == 0)
139 local = qname;
140 if (qname == null || qname.length() == 0)
141 qname = local;
142
143 processText(false);
144 getCurrentHandler().leaveElement(uri, local, qname);
145 }
146
147 /*** Root object that is being unmarshalled. */
148 private Object result;
149
150 public Object getResult() throws UnmarshalException {
151 if (isUnmarshalInProgress)
152 throw new IllegalStateException();
153
154 if (!aborted)
155 return result;
156
157
158 throw new UnmarshalException((String) null);
159 }
160
161
162
163
164
165
166 private UnmarshallingEventHandler[] handlers = new UnmarshallingEventHandler[16];
167
168 private int[] mementos = new int[16];
169
170 private int handlerLen = 0;
171
172 public void pushContentHandler(UnmarshallingEventHandler handler,
173 int memento) {
174 if (handlerLen == handlers.length) {
175
176 UnmarshallingEventHandler[] h = new UnmarshallingEventHandler[handlerLen * 2];
177 int[] m = new int[handlerLen * 2];
178 System.arraycopy(handlers, 0, h, 0, handlerLen);
179 System.arraycopy(mementos, 0, m, 0, handlerLen);
180 handlers = h;
181 mementos = m;
182 }
183 handlers[handlerLen] = handler;
184 mementos[handlerLen] = memento;
185 handlerLen++;
186 }
187
188 public void popContentHandler() throws SAXException {
189 handlerLen--;
190 handlers[handlerLen] = null;
191 getCurrentHandler().leaveChild(mementos[handlerLen]);
192 }
193
194 public UnmarshallingEventHandler getCurrentHandler() {
195 return handlers[handlerLen - 1];
196 }
197
198
199
200
201
202
203 private StringBuffer buffer = new StringBuffer();
204
205 protected void consumeText(String str, boolean ignorable)
206 throws SAXException {
207 if (ignorable && str.trim().length() == 0)
208
209
210 return;
211
212
213 getCurrentHandler().text(str);
214 }
215
216 private void processText(boolean ignorable) throws SAXException {
217 if (shouldCollectText())
218 consumeText(buffer.toString(), ignorable);
219
220
221
222 if (buffer.length() < 1024)
223 buffer.setLength(0);
224 else
225 buffer = new StringBuffer();
226 }
227
228 public final void characters(char[] buf, int start, int len) {
229 if (shouldCollectText())
230 buffer.append(buf, start, len);
231 }
232
233 public final void ignorableWhitespace(char[] buf, int start, int len) {
234 characters(buf, start, len);
235 }
236
237
238
239
240
241
242 private String[] nsBind = new String[16];
243
244 private int nsLen = 0;
245
246
247
248
249 private int[] idxStack = new int[16];
250
251 public void startPrefixMapping(String prefix, String uri) {
252 if (nsBind.length == nsLen) {
253
254 String[] n = new String[nsLen * 2];
255 System.arraycopy(nsBind, 0, n, 0, nsLen);
256 nsBind = n;
257 }
258 nsBind[nsLen++] = prefix;
259 nsBind[nsLen++] = uri;
260 }
261
262 public void endPrefixMapping(String prefix) {
263 nsLen -= 2;
264 }
265
266 public String resolveNamespacePrefix(String prefix) {
267 if (prefix.equals("xml"))
268 return "http://www.w3.org/XML/1998/namespace";
269
270 for (int i = idxStack[stackTop] - 2; i >= 0; i -= 2) {
271 if (prefix.equals(nsBind[i]))
272 return nsBind[i + 1];
273 }
274 return null;
275 }
276
277 public String[] getNewlyDeclaredPrefixes() {
278 return getPrefixList(idxStack[stackTop - 1]);
279 }
280
281 public String[] getAllDeclaredPrefixes() {
282 return getPrefixList(2);
283 }
284
285 private String[] getPrefixList(int startIndex) {
286 int size = (idxStack[stackTop] - startIndex) / 2;
287 String[] r = new String[size];
288 for (int i = 0; i < r.length; i++)
289 r[i] = nsBind[startIndex + i * 2];
290 return r;
291 }
292
293
294
295
296 public Iterator getPrefixes(String uri) {
297
298
299 return Collections.unmodifiableList(getAllPrefixesInList(uri))
300 .iterator();
301 }
302
303 private List getAllPrefixesInList(String uri) {
304 List a = new ArrayList();
305
306 if (uri.equals(XMLConstants.XML_NS_URI)) {
307 a.add(XMLConstants.XML_NS_PREFIX);
308 return a;
309 }
310 if (uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
311 a.add(XMLConstants.XMLNS_ATTRIBUTE);
312 return a;
313 }
314 if (uri == null)
315 throw new IllegalArgumentException();
316
317 for (int i = nsLen - 2; i >= 0; i -= 2)
318 if (uri.equals(nsBind[i + 1]))
319 if (getNamespaceURI(nsBind[i]).equals(nsBind[i + 1]))
320
321 a.add(nsBind[i]);
322
323 return a;
324 }
325
326 public String getPrefix(String uri) {
327 if (uri.equals(XMLConstants.XML_NS_URI))
328 return XMLConstants.XML_NS_PREFIX;
329 if (uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI))
330 return XMLConstants.XMLNS_ATTRIBUTE;
331 if (uri == null)
332 throw new IllegalArgumentException();
333
334 for (int i = idxStack[stackTop] - 2; i >= 0; i -= 2)
335 if (uri.equals(nsBind[i + 1]))
336 if (getNamespaceURI(nsBind[i]).equals(nsBind[i + 1]))
337
338 return nsBind[i];
339
340 return null;
341 }
342
343 public String getNamespaceURI(String prefix) {
344 if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE))
345 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
346 if (prefix == null)
347 throw new IllegalArgumentException();
348
349 return resolveNamespacePrefix(prefix);
350 }
351
352
353
354
355
356
357 /***
358 * Attributes stack.
359 */
360 private AttributesImpl[] attStack = new AttributesImpl[16];
361
362 /***
363 * Element nesting level.
364 */
365 private int elementDepth;
366
367 /***
368 * Always {@link #elementDepth}-1.
369 */
370 private int stackTop;
371
372 /***
373 * Stack of collectText flag. False means text can be ignored for this
374 * element.
375 *
376 * Use {@link #elementDepth} and {@link #stackTop} to access the array.
377 */
378 private boolean[] collectText = new boolean[16];
379
380 public void pushAttributes(Attributes atts, boolean collectTextFlag) {
381
382 if (attStack.length == elementDepth) {
383
384 AttributesImpl[] buf1 = new AttributesImpl[attStack.length * 2];
385 System.arraycopy(attStack, 0, buf1, 0, attStack.length);
386 attStack = buf1;
387
388 int[] buf2 = new int[idxStack.length * 2];
389 System.arraycopy(idxStack, 0, buf2, 0, idxStack.length);
390 idxStack = buf2;
391
392 boolean[] buf3 = new boolean[collectText.length * 2];
393 System.arraycopy(collectText, 0, buf3, 0, collectText.length);
394 collectText = buf3;
395 }
396
397 elementDepth++;
398 stackTop++;
399
400
401 AttributesImpl a = attStack[stackTop];
402 if (a == null)
403 attStack[stackTop] = a = new AttributesImpl();
404 else
405 a.clear();
406
407
408
409
410 for (int i = 0; i < atts.getLength(); i++) {
411 String auri = atts.getURI(i);
412 String alocal = atts.getLocalName(i);
413 String avalue = atts.getValue(i);
414 String aqname = atts.getQName(i);
415
416
417
418 if (auri == null)
419 auri = "";
420 if (alocal == null || alocal.length() == 0)
421 alocal = aqname;
422 if (aqname == null || aqname.length() == 0)
423 aqname = alocal;
424
425
426
427
428
429
430
431
432
433
434 if (auri == "http://www.w3.org/2001/XMLSchema-instance"
435 && alocal == "nil") {
436 String v = avalue.trim();
437 if (v.equals("false") || v.equals("0"))
438 continue;
439 }
440
441
442 a.addAttribute(auri, alocal, aqname, atts.getType(i), avalue);
443 }
444
445
446 idxStack[stackTop] = nsLen;
447
448 collectText[stackTop] = collectTextFlag;
449 }
450
451 public void popAttributes() {
452 stackTop--;
453 elementDepth--;
454 }
455
456 public Attributes getUnconsumedAttributes() {
457 return attStack[stackTop];
458 }
459
460 /***
461 * @param uri,local
462 * has to be interned.
463 */
464 public int getAttribute(String uri, String local) {
465 return attStack[stackTop].getIndexFast(uri, local);
466 }
467
468 public void consumeAttribute(int idx) throws SAXException {
469 AttributesImpl a = attStack[stackTop];
470
471 String uri = a.getURI(idx);
472 String local = a.getLocalName(idx);
473 String qname = a.getQName(idx);
474 String value = a.getValue(idx);
475
476
477
478
479 a.removeAttribute(idx);
480
481 getCurrentHandler().enterAttribute(uri, local, qname);
482 consumeText(value, false);
483 getCurrentHandler().leaveAttribute(uri, local, qname);
484 }
485
486 public String eatAttribute(int idx) throws SAXException {
487 AttributesImpl a = attStack[stackTop];
488
489 String value = a.getValue(idx);
490
491
492 a.removeAttribute(idx);
493
494 return value;
495 }
496
497
498
499
500
501
502 /***
503 * Submitted patchers in the order they've submitted. Many XML vocabulary
504 * doesn't use ID/IDREF at all, so we initialize it with null.
505 */
506 private Runnable[] patchers = null;
507
508 private int patchersLen = 0;
509
510 public void addPatcher(Runnable job) {
511
512 if (patchers == null)
513 patchers = new Runnable[32];
514 if (patchers.length == patchersLen) {
515 Runnable[] buf = new Runnable[patchersLen * 2];
516 System.arraycopy(patchers, 0, buf, 0, patchersLen);
517 patchers = buf;
518 }
519 patchers[patchersLen++] = job;
520 }
521
522 /*** Executes all the patchers. */
523 private void runPatchers() {
524 if (patchers != null) {
525 for (int i = 0; i < patchersLen; i++)
526 patchers[i].run();
527 }
528 }
529
530 /*** Records ID->Object map. */
531 private Hashtable idmap = null;
532
533 public String addToIdTable(String id) {
534 if (idmap == null)
535 idmap = new Hashtable();
536 idmap.put(id, getCurrentHandler().owner());
537 return id;
538 }
539
540 public Object getObjectFromId(String id) {
541 if (idmap == null)
542 return null;
543 return idmap.get(id);
544 }
545
546
547
548
549
550
551 public void skippedEntity(String name) {
552 }
553
554 public void processingInstruction(String target, String data) {
555
556 }
557
558 public void setDocumentLocator(Locator loc) {
559 locator = loc;
560 }
561
562 public Locator getLocator() {
563 return locator;
564 }
565
566 private Locator locator;
567
568
569
570
571
572
573 private final UnmarshallerImpl parent;
574
575 private boolean aborted = false;
576
577 public void handleEvent(ValidationEvent event, boolean canRecover)
578 throws SAXException {
579 ValidationEventHandler eventHandler;
580 try {
581 eventHandler = parent.getEventHandler();
582 } catch (JAXBException e) {
583
584 throw new JAXBAssertionError();
585 }
586
587 boolean recover = eventHandler.handleEvent(event);
588
589
590
591 if (!recover)
592 aborted = true;
593
594 if (!canRecover || !recover)
595 throw new SAXException(new UnmarshalException(event.getMessage(),
596 event.getLinkedException()));
597 }
598
599
600
601
602
603
604 public String getBaseUri() {
605 return null;
606 }
607
608 public boolean isUnparsedEntity(String s) {
609 return true;
610 }
611
612 public boolean isNotation(String s) {
613 return true;
614 }
615
616
617
618
619
620
621 private Tracer tracer;
622
623 public void setTracer(Tracer t) {
624 this.tracer = t;
625 }
626
627 public Tracer getTracer() {
628 if (tracer == null)
629 tracer = new Tracer.Standard();
630 return tracer;
631 }
632
633 /***
634 * Computes the names of possible root elements for a better error
635 * diagnosis.
636 */
637 private String computeExpectedRootElements() {
638 String r = "";
639
640 String[] probePoints = grammarInfo.getProbePoints();
641 for (int i = 0; i < probePoints.length; i += 2) {
642 if (grammarInfo.recognize(probePoints[i], probePoints[i + 1])) {
643 if (r.length() != 0)
644 r += ',';
645 r += "<{" + probePoints[i] + "}" + probePoints[i + 1] + ">";
646 }
647 }
648
649 return r;
650 }
651 }