View Javadoc

1   //
2   // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v1.0.3-b18-fcs 
3   // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
4   // Any modifications to this file will be lost upon recompilation of the source schema. 
5   // Generated on: 2004.12.16 at 07:09:42 EST 
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("",""); // by default, the default ns is bound to "".
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          // reset the object
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          // work gracefully with misconfigured parsers that don't support namespaces
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             // this is the root element.
103             // create a root object and start unmarshalling
104             UnmarshallingEventHandler unmarshaller =
105                 grammarInfo.createUnmarshaller(uri,local,this);
106             if(unmarshaller==null) {
107                 // the registry doesn't know about this element.
108                 //
109                 // the no.1 cause of this problem is that your application is configuring
110                 // an XML parser by your self and you forgot to call
111                 // the SAXParserFactory.setNamespaceAware(true). When this happens, you see
112                 // the namespace URI is reported as empty whereas you expect something else.
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         // work gracefully with misconfigured parsers that don't support namespaces
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         // there was an error.
156         throw new UnmarshalException((String)null);
157     }
158 
159     
160     
161 //
162 //
163 // handler stack maintainance
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             // expand buffer
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;  // this handler is removed
188         getCurrentHandler().leaveChild(mementos[handlerLen]);
189     }
190 
191     public UnmarshallingEventHandler getCurrentHandler() {
192         return handlers[handlerLen-1];
193     }
194 
195 
196 //
197 //
198 // text handling
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             // if we are allowed to ignore text and
206             // the text is ignorable, ignore.
207             return;
208         
209         // otherwise perform a transition by this token.
210         getCurrentHandler().text(str);
211     }
212     private void processText( boolean ignorable ) throws SAXException {
213         if( shouldCollectText() )
214             consumeText(buffer.toString(),ignorable);
215         
216         // avoid excessive object allocation, but also avoid
217         // keeping a huge array inside StringBuffer.
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 // namespace binding maintainance
237 //
238 //
239     private String[] nsBind = new String[16];
240     private int nsLen=0;
241     
242     // in the current scope, nsBind[0] - nsBind[idxStack[idxStackTop]-1]
243     // are active.
244     // use {@link #elementDepth} and {@link stackTop} to access.
245     private int[] idxStack = new int[16];
246     
247     public void startPrefixMapping( String prefix, String uri ) {
248         if(nsBind.length==nsLen) {
249             // expand the buffer
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 );  // skip the default ""->"" mapping
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     //  NamespaceContext2 implementation 
289     //
290     public Iterator getPrefixes(String uri) {
291         // wrap it into unmodifiable list so that the remove method
292         // will throw UnsupportedOperationException.
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                     // make sure that this prefix is still effective.
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                     // make sure that this prefix is still effective.
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 // Attribute handling
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             // reallocate the buffer
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         // push the stack
393         AttributesImpl a = attStack[stackTop];
394         if( a==null )
395             attStack[stackTop] = a = new AttributesImpl();
396         else
397             a.clear();
398         
399         // since Attributes object is mutable, it is criticall important
400         // to make a copy.
401         // also symbolize attribute names
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             // <foo xsi:nil="false">some value</foo> is a valid fragment, however
408             // we need a look ahead to correctly handle this case.
409             // (because when we process @xsi:nil, we don't know what the value is,
410             // and by the time we read "false", we can't cancel this attribute anymore.)
411             //
412             // as a quick workaround, we remove @xsi:nil if the value is false.
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;   // skip this attribute
417             }
418             
419             // otherwise just add it.
420             a.addAttribute(
421                     auri,
422                     alocal,
423                     atts.getQName(i),
424                     atts.getType(i),
425                     avalue );
426         }
427         
428         
429         // start a new namespace scope
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         // mark the attribute as consumed
457         // we need to remove the attribute before we process it
458         // because the event handler might access attributes.
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         // mark the attribute as consumed
472         a.removeAttribute(idx);
473         
474         return value;
475     }
476 
477 //
478 //
479 // ID/IDREF related code
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         // re-allocate buffer if necessary
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 // Other SAX callbacks
529 //
530 //
531     public void skippedEntity( String name ) {
532     }
533     public void processingInstruction( String target, String data ) {
534         // just ignore
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 // error handling
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             // impossible.
558             throw new JAXBAssertionError();
559         }
560 
561         boolean recover = eventHandler.handleEvent(event);
562         
563         // if the handler says "abort", we will not return the object
564         // from the unmarshaller.getResult()
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 // ValidationContext implementation
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 // debug trace methods
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 }