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