KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > tools > xjc > runtime > SAXUnmarshallerHandlerImpl


1 package com.sun.tools.xjc.runtime;
2
3 import java.util.ArrayList JavaDoc;
4 import java.util.Collections JavaDoc;
5 import java.util.Hashtable JavaDoc;
6 import java.util.Iterator JavaDoc;
7 import java.util.List JavaDoc;
8
9 import javax.xml.XMLConstants JavaDoc;
10 import javax.xml.bind.JAXBException;
11 import javax.xml.bind.UnmarshalException;
12 import javax.xml.bind.ValidationEvent;
13 import javax.xml.bind.ValidationEventHandler;
14
15 import org.xml.sax.Attributes JavaDoc;
16 import org.xml.sax.Locator JavaDoc;
17 import org.xml.sax.SAXException JavaDoc;
18 import org.xml.sax.SAXParseException JavaDoc;
19 import org.xml.sax.helpers.LocatorImpl JavaDoc;
20
21 import com.sun.xml.bind.JAXBAssertionError;
22 import com.sun.xml.bind.unmarshaller.Messages;
23 import com.sun.xml.bind.unmarshaller.Tracer;
24 import com.sun.xml.bind.util.AttributesImpl;
25
26 /**
27  * Implementation of {@link UnmarshallerHandler}.
28  *
29  * This object converts SAX events into unmarshaller events and
30  * cooridnates the entire unmarshalling process.
31  *
32  * @author
33  * <a HREF="mailto:kohsuke.kawaguchi@sun.com">Kohsuke KAWAGUCHI</a>
34  */

35 public class SAXUnmarshallerHandlerImpl
36     implements SAXUnmarshallerHandler, UnmarshallingContext
37 {
38     /**
39      * This flag is set to true at the startDocument event
40      * and false at the endDocument event.
41      *
42      * Until the first document is unmarshalled, we don't
43      * want to return an object. So this variable is initialized
44      * to true.
45      */

46     private boolean isUnmarshalInProgress = true;
47     
48     
49     
50     public SAXUnmarshallerHandlerImpl( UnmarshallerImpl _parent, GrammarInfo _gi ) {
51         this.parent = _parent;
52         grammarInfo = _gi;
53         startPrefixMapping("",""); // by default, the default ns is bound to "".
54
}
55     
56     private final GrammarInfo grammarInfo;
57     public GrammarInfo getGrammarInfo() { return grammarInfo; }
58     
59     /**
60      * Returns true if we should be collecting characters in the current element.
61      */

62     private final boolean shouldCollectText() {
63         return collectText[stackTop];
64     }
65     
66     public void startDocument() throws SAXException JavaDoc {
67         // reset the object
68
result = null;
69         handlerLen=0;
70         patchers=null;
71         patchersLen=0;
72         aborted = false;
73         isUnmarshalInProgress = true;
74         
75         stackTop=0;
76         elementDepth=1;
77     }
78     
79     public void endDocument() throws SAXException JavaDoc {
80         runPatchers();
81         isUnmarshalInProgress = false;
82     }
83     
84     public void startElement( String JavaDoc uri, String JavaDoc local, String JavaDoc qname, Attributes JavaDoc atts )
85             throws SAXException JavaDoc {
86         
87         // work gracefully with misconfigured parsers that don't support namespaces
88
if( uri==null )
89             uri="";
90         if( local==null || local.length()==0 )
91             local=qname;
92         if( qname==null || qname.length()==0 )
93             qname=local;
94         
95         if(result==null) {
96             // this is the root element.
97
// create a root object and start unmarshalling
98
UnmarshallingEventHandler unmarshaller =
99                 grammarInfo.createUnmarshaller(uri,local,this);
100             if(unmarshaller==null) {
101                 // the registry doesn't know about this element.
102
//
103
// the no.1 cause of this problem is that your application is configuring
104
// an XML parser by your self and you forgot to call
105
// the SAXParserFactory.setNamespaceAware(true). When this happens, you see
106
// the namespace URI is reported as empty whereas you expect something else.
107
throw new SAXParseException JavaDoc(
108                     Messages.format( Messages.UNEXPECTED_ROOT_ELEMENT2,
109                         uri, local, computeExpectedRootElements() ),
110                     getLocator() );
111             }
112             result = unmarshaller.owner();
113
114             pushContentHandler(unmarshaller,0);
115         }
116     
117         processText(true);
118     
119         getCurrentHandler().enterElement(uri,local,qname,atts);
120     }
121
122     public final void endElement( String JavaDoc uri, String JavaDoc local, String JavaDoc qname )
123             throws SAXException JavaDoc {
124         
125         // work gracefully with misconfigured parsers that don't support namespaces
126
if( uri==null )
127             uri="";
128         if( local==null || local.length()==0 )
129             local=qname;
130         if( qname==null || qname.length()==0 )
131             qname=local;
132         
133         processText(false);
134         getCurrentHandler().leaveElement(uri,local,qname);
135     }
136     
137     
138     
139     
140     
141     /** Root object that is being unmarshalled. */
142     private Object JavaDoc result;
143     public Object JavaDoc getResult() throws UnmarshalException {
144         if(isUnmarshalInProgress)
145             throw new IllegalStateException JavaDoc();
146         
147         if(!aborted) return result;
148         
149         // there was an error.
150
throw new UnmarshalException((String JavaDoc)null);
151     }
152
153     
154     
155 //
156
//
157
// handler stack maintainance
158
//
159
//
160
private UnmarshallingEventHandler[] handlers = new UnmarshallingEventHandler[16];
161     private int[] mementos = new int[16];
162     private int handlerLen=0;
163     
164     public void pushContentHandler( UnmarshallingEventHandler handler, int memento ) {
165         if(handlerLen==handlers.length) {
166             // expand buffer
167
UnmarshallingEventHandler[] h = new UnmarshallingEventHandler[handlerLen*2];
168             int[] m = new int[handlerLen*2];
169             System.arraycopy(handlers,0,h,0,handlerLen);
170             System.arraycopy(mementos,0,m,0,handlerLen);
171             handlers = h;
172             mementos = m;
173         }
174         handlers[handlerLen] = handler;
175         mementos[handlerLen] = memento;
176         handlerLen++;
177     }
178     
179     public void popContentHandler() throws SAXException JavaDoc {
180         handlerLen--;
181         handlers[handlerLen]=null; // this handler is removed
182
getCurrentHandler().leaveChild(mementos[handlerLen]);
183     }
184
185     public UnmarshallingEventHandler getCurrentHandler() {
186         return handlers[handlerLen-1];
187     }
188
189
190 //
191
//
192
// text handling
193
//
194
//
195
private StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
196     
197     protected void consumeText( String JavaDoc str, boolean ignorable ) throws SAXException JavaDoc {
198          if(ignorable && str.trim().length()==0)
199             // if we are allowed to ignore text and
200
// the text is ignorable, ignore.
201
return;
202         
203         // otherwise perform a transition by this token.
204
getCurrentHandler().text(str);
205     }
206     private void processText( boolean ignorable ) throws SAXException JavaDoc {
207         if( shouldCollectText() )
208             consumeText(buffer.toString(),ignorable);
209         
210         // avoid excessive object allocation, but also avoid
211
// keeping a huge array inside StringBuffer.
212
if(buffer.length()<1024) buffer.setLength(0);
213         else buffer = new StringBuffer JavaDoc();
214     }
215     
216     public final void characters( char[] buf, int start, int len ) {
217         if( shouldCollectText() )
218             buffer.append(buf,start,len);
219     }
220
221     public final void ignorableWhitespace( char[] buf, int start, int len ) {
222         characters(buf,start,len);
223     }
224
225
226
227     
228 //
229
//
230
// namespace binding maintainance
231
//
232
//
233
private String JavaDoc[] nsBind = new String JavaDoc[16];
234     private int nsLen=0;
235     
236     // in the current scope, nsBind[0] - nsBind[idxStack[idxStackTop]-1]
237
// are active.
238
// use {@link #elementDepth} and {@link stackTop} to access.
239
private int[] idxStack = new int[16];
240     
241     public void startPrefixMapping( String JavaDoc prefix, String JavaDoc uri ) {
242         if(nsBind.length==nsLen) {
243             // expand the buffer
244
String JavaDoc[] n = new String JavaDoc[nsLen*2];
245             System.arraycopy(nsBind,0,n,0,nsLen);
246             nsBind=n;
247         }
248         nsBind[nsLen++] = prefix;
249         nsBind[nsLen++] = uri;
250     }
251     public void endPrefixMapping( String JavaDoc prefix ) {
252         nsLen-=2;
253     }
254     public String JavaDoc resolveNamespacePrefix( String JavaDoc prefix ) {
255         if(prefix.equals("xml"))
256             return "http://www.w3.org/XML/1998/namespace";
257         
258         for( int i=idxStack[stackTop]-2; i>=0; i-=2 ) {
259             if(prefix.equals(nsBind[i]))
260                 return nsBind[i+1];
261         }
262         return null;
263     }
264     public String JavaDoc[] getNewlyDeclaredPrefixes() {
265         return getPrefixList( idxStack[stackTop-1] );
266     }
267
268     public String JavaDoc[] getAllDeclaredPrefixes() {
269         return getPrefixList( 2 ); // skip the default ""->"" mapping
270
}
271     
272     private String JavaDoc[] getPrefixList( int startIndex ) {
273         int size = (idxStack[stackTop]-startIndex)/2;
274         String JavaDoc[] r = new String JavaDoc[size];
275         for( int i=0; i<r.length; i++ )
276             r[i] = nsBind[startIndex+i*2];
277         return r;
278     }
279
280     
281     //
282
// NamespaceContext2 implementation
283
//
284
public Iterator JavaDoc getPrefixes(String JavaDoc uri) {
285         // wrap it into unmodifiable list so that the remove method
286
// will throw UnsupportedOperationException.
287
return Collections.unmodifiableList(
288             getAllPrefixesInList(uri)).iterator();
289     }
290     
291     private List JavaDoc getAllPrefixesInList(String JavaDoc uri) {
292         List JavaDoc a = new ArrayList JavaDoc();
293         
294         if( uri.equals(XMLConstants.XML_NS_URI) ) {
295             a.add(XMLConstants.XML_NS_PREFIX);
296             return a;
297         }
298         if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) ) {
299             a.add(XMLConstants.XMLNS_ATTRIBUTE);
300             return a;
301         }
302         if( uri==null )
303             throw new IllegalArgumentException JavaDoc();
304           
305         for( int i=nsLen-2; i>=0; i-=2 )
306             if(uri.equals(nsBind[i+1]))
307                 if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
308                     // make sure that this prefix is still effective.
309
a.add(nsBind[i]);
310          
311         return a;
312     }
313
314     public String JavaDoc getPrefix(String JavaDoc uri) {
315         if( uri.equals(XMLConstants.XML_NS_URI) )
316             return XMLConstants.XML_NS_PREFIX;
317         if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) )
318             return XMLConstants.XMLNS_ATTRIBUTE;
319         if( uri==null )
320             throw new IllegalArgumentException JavaDoc();
321           
322         for( int i=idxStack[stackTop]-2; i>=0; i-=2 )
323             if(uri.equals(nsBind[i+1]))
324                 if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
325                     // make sure that this prefix is still effective.
326
return nsBind[i];
327          
328         return null;
329     }
330
331      public String JavaDoc getNamespaceURI(String JavaDoc prefix) {
332          if( prefix==null )
333              throw new IllegalArgumentException JavaDoc();
334          if( prefix.equals(XMLConstants.XMLNS_ATTRIBUTE) )
335              return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
336         
337          return resolveNamespacePrefix(prefix);
338      }
339
340 //
341
//
342
// Attribute handling
343
//
344
//
345
/**
346      * Attributes stack.
347      */

348     private AttributesImpl[] attStack = new AttributesImpl[16];
349     /**
350      * Element nesting level.
351      */

352     private int elementDepth;
353     /**
354      * Always {@link #elementDepth}-1.
355      */

356     private int stackTop;
357     
358     /**
359      * Stack of collectText flag.
360      * False means text can be ignored for this element.
361      *
362      * Use {@link #elementDepth} and {@link #stackTop} to access the array.
363      */

364     private boolean[] collectText = new boolean[16];
365     
366     public void pushAttributes( Attributes JavaDoc atts, boolean collectTextFlag ) {
367         
368         if( attStack.length==elementDepth ) {
369             // reallocate the buffer
370
AttributesImpl[] buf1 = new AttributesImpl[attStack.length*2];
371             System.arraycopy(attStack,0,buf1,0,attStack.length);
372             attStack = buf1;
373             
374             int[] buf2 = new int[idxStack.length*2];
375             System.arraycopy(idxStack,0,buf2,0,idxStack.length);
376             idxStack = buf2;
377             
378             boolean[] buf3 = new boolean[collectText.length*2];
379             System.arraycopy(collectText,0,buf3,0,collectText.length);
380             collectText = buf3;
381         }
382         
383         elementDepth++;
384         stackTop++;
385         
386         // push the stack
387
AttributesImpl a = attStack[stackTop];
388         if( a==null )
389             attStack[stackTop] = a = new AttributesImpl();
390         else
391             a.clear();
392         
393         // since Attributes object is mutable, it is criticall important
394
// to make a copy.
395
// also symbolize attribute names
396
for( int i=0; i<atts.getLength(); i++ ) {
397             String JavaDoc auri = atts.getURI(i);
398             String JavaDoc alocal = atts.getLocalName(i);
399             String JavaDoc avalue = atts.getValue(i);
400             String JavaDoc aqname = atts.getQName(i);
401             
402             // work gracefully with misconfigured parsers that don't support namespaces
403
if( auri==null )
404                 auri="";
405             if( alocal==null || alocal.length()==0 )
406                 alocal=aqname;
407             if( aqname==null || aqname.length()==0 )
408                 aqname=alocal;
409
410             // <foo xsi:nil="false">some value</foo> is a valid fragment, however
411
// we need a look ahead to correctly handle this case.
412
// (because when we process @xsi:nil, we don't know what the value is,
413
// and by the time we read "false", we can't cancel this attribute anymore.)
414
//
415
// as a quick workaround, we remove @xsi:nil if the value is false.
416
if( auri=="http://www.w3.org/2001/XMLSchema-instance" && alocal=="nil" ) {
417                 String JavaDoc v = avalue.trim();
418                 if(v.equals("false") || v.equals("0"))
419                     continue; // skip this attribute
420
}
421             
422             // otherwise just add it.
423
a.addAttribute(
424                     auri,
425                     alocal,
426                     aqname,
427                     atts.getType(i),
428                     avalue );
429         }
430         
431         
432         // start a new namespace scope
433
idxStack[stackTop] = nsLen;
434         
435         collectText[stackTop] = collectTextFlag;
436     }
437     public void popAttributes() {
438         stackTop--;
439         elementDepth--;
440     }
441     public Attributes JavaDoc getUnconsumedAttributes() {
442         return attStack[stackTop];
443     }
444     /**
445      * @param uri,local
446      * has to be interned.
447      */

448     public int getAttribute( String JavaDoc uri, String JavaDoc local ) {
449         return attStack[stackTop].getIndexFast(uri,local);
450     }
451     public void consumeAttribute( int idx ) throws SAXException JavaDoc {
452         AttributesImpl a = attStack[stackTop];
453         
454         String JavaDoc uri = a.getURI(idx);
455         String JavaDoc local = a.getLocalName(idx);
456         String JavaDoc qname = a.getQName(idx);
457         String JavaDoc value = a.getValue(idx);
458
459         // mark the attribute as consumed
460
// we need to remove the attribute before we process it
461
// because the event handler might access attributes.
462
a.removeAttribute(idx);
463         
464         
465         getCurrentHandler().enterAttribute(uri,local,qname);
466         consumeText(value,false);
467         getCurrentHandler().leaveAttribute(uri,local,qname);
468     }
469     public String JavaDoc eatAttribute( int idx ) throws SAXException JavaDoc {
470         AttributesImpl a = attStack[stackTop];
471         
472         String JavaDoc value = a.getValue(idx);
473
474         // mark the attribute as consumed
475
a.removeAttribute(idx);
476         
477         return value;
478     }
479
480 //
481
//
482
// ID/IDREF related code
483
//
484
//
485
/**
486      * Submitted patchers in the order they've submitted.
487      * Many XML vocabulary doesn't use ID/IDREF at all, so we
488      * initialize it with null.
489      */

490     private Runnable JavaDoc[] patchers = null;
491     private int patchersLen = 0;
492     
493     public void addPatcher( Runnable JavaDoc job ) {
494         // re-allocate buffer if necessary
495
if( patchers==null )
496             patchers = new Runnable JavaDoc[32];
497         if( patchers.length == patchersLen ) {
498             Runnable JavaDoc[] buf = new Runnable JavaDoc[patchersLen*2];
499             System.arraycopy(patchers,0,buf,0,patchersLen);
500             patchers = buf;
501         }
502         patchers[patchersLen++] = job;
503     }
504     
505     /** Executes all the patchers. */
506     private void runPatchers() {
507         if( patchers!=null ) {
508             for( int i=0; i<patchersLen; i++ )
509                 patchers[i].run();
510         }
511     }
512
513     /** Records ID->Object map. */
514     private Hashtable JavaDoc idmap = null;
515
516     public String JavaDoc addToIdTable( String JavaDoc id ) {
517         if(idmap==null) idmap = new Hashtable JavaDoc();
518         idmap.put( id, getCurrentHandler().owner() );
519         return id;
520     }
521     
522     public Object JavaDoc getObjectFromId( String JavaDoc id ) {
523         if(idmap==null) return null;
524         return idmap.get(id);
525     }
526     
527
528
529 //
530
//
531
// Other SAX callbacks
532
//
533
//
534
public void skippedEntity( String JavaDoc name ) {
535     }
536     public void processingInstruction( String JavaDoc target, String JavaDoc data ) {
537         // just ignore
538
}
539     public void setDocumentLocator( Locator JavaDoc loc ) {
540         locator = loc;
541     }
542     public Locator JavaDoc getLocator() { return locator; }
543     
544     private Locator JavaDoc locator = DUMMY_LOCATOR;
545
546     private static final Locator JavaDoc DUMMY_LOCATOR = new LocatorImpl JavaDoc();
547
548
549 //
550
//
551
// error handling
552
//
553
//
554
private final UnmarshallerImpl parent;
555     private boolean aborted = false;
556     
557     public void handleEvent(ValidationEvent event, boolean canRecover ) throws SAXException JavaDoc {
558         ValidationEventHandler eventHandler;
559         try {
560             eventHandler = parent.getEventHandler();
561         } catch( JAXBException e ) {
562             // impossible.
563
throw new JAXBAssertionError();
564         }
565
566         boolean recover = eventHandler.handleEvent(event);
567         
568         // if the handler says "abort", we will not return the object
569
// from the unmarshaller.getResult()
570
if(!recover) aborted = true;
571         
572         if( !canRecover || !recover )
573             throw new SAXException JavaDoc( new UnmarshalException(
574                 event.getMessage(),
575                 event.getLinkedException() ) );
576     }
577   
578 //
579
//
580
// ValidationContext implementation
581
//
582
//
583
public String JavaDoc getBaseUri() { return null; }
584     public boolean isUnparsedEntity(String JavaDoc s) { return true; }
585     public boolean isNotation(String JavaDoc s) { return true; }
586
587
588 //
589
//
590
// debug trace methods
591
//
592
//
593
private Tracer tracer;
594     public void setTracer( Tracer t ) {
595         this.tracer = t;
596     }
597     public Tracer getTracer() {
598         if(tracer==null)
599             tracer = new Tracer.Standard();
600         return tracer;
601     }
602     
603     /**
604      * Computes the names of possible root elements for a better error diagnosis.
605      */

606     private String JavaDoc computeExpectedRootElements() {
607         String JavaDoc r = "";
608         
609         String JavaDoc[] probePoints = grammarInfo.getProbePoints();
610         for( int i=0; i<probePoints.length; i+=2 ) {
611             if( grammarInfo.recognize(probePoints[i],probePoints[i+1]) ) {
612                 if(r.length()!=0) r+=',';
613                 r += "<{"+probePoints[i]+"}"+probePoints[i+1]+">";
614             }
615         }
616         
617         return r;
618     }
619 }
620
Popular Tags