001    //
002    // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v1.0.1-05/30/2003 05:06 AM(java_re)-fcs 
003    // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
004    // Any modifications to this file will be lost upon recompilation of the source schema. 
005    // Generated on: 2004.10.11 at 12:13:34 EDT 
006    //
007    
008    /*
009     * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
010     * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
011     */
012    
013    /*
014     * @(#)$Id: SAXUnmarshallerHandlerImpl.java,v 1.9 2003/05/21 01:18:16 kk122374 Exp $
015     */
016    package astronomy.data.spectra.impl.runtime;
017    
018    import java.util.ArrayList;
019    import java.util.Collections;
020    import java.util.Hashtable;
021    import java.util.Iterator;
022    import java.util.List;
023    import java.util.Stack;
024    
025    import javax.xml.XMLConstants;
026    import javax.xml.bind.JAXBException;
027    import javax.xml.bind.UnmarshalException;
028    import javax.xml.bind.ValidationEvent;
029    import javax.xml.bind.ValidationEventHandler;
030    
031    import org.xml.sax.Attributes;
032    import org.xml.sax.Locator;
033    import org.xml.sax.SAXException;
034    import org.xml.sax.SAXParseException;
035    
036    import com.sun.xml.bind.unmarshaller.Messages;
037    import com.sun.xml.bind.unmarshaller.Tracer;
038    import com.sun.xml.bind.util.AttributesImpl;
039    
040    /**
041     * Implementation of {@link UnmarshallerHandler}.
042     * 
043     * This object converts SAX events into unmarshaller events and
044     * cooridnates the entire unmarshalling process.
045     *
046     * @author
047     *  <a href="mailto:kohsuke.kawaguchi@sun.com>Kohsuke KAWAGUCHI</a>
048     */
049    public class SAXUnmarshallerHandlerImpl
050        implements SAXUnmarshallerHandler, UnmarshallingContext
051    {
052        /**
053         * This flag is set to true at the startDocument event
054         * and false at the endDocument event.
055         * 
056         * Until the first document is unmarshalled, we don't
057         * want to return an object. So this variable is initialized
058         * to true.
059         */
060        private boolean isUnmarshalInProgress = true;
061        
062        /** stack of collectText flag. False means text can be ignored. */ 
063        private boolean[] collectText = new boolean[16];
064        private int collectTextTop = 0;
065        
066        
067        public SAXUnmarshallerHandlerImpl( UnmarshallerImpl _parent, GrammarInfo _gi ) {
068            this.parent = _parent;
069            grammarInfo = _gi;
070            startPrefixMapping("",""); // by default, the default ns is bound to "".
071         }
072        
073        private final GrammarInfo grammarInfo;
074        public GrammarInfo getGrammarInfo() { return grammarInfo; }
075        
076        /**
077         * Returns true if we should be collecting characters in the current element.
078         */
079        private final boolean shouldCollectText() {
080            return collectText[collectTextTop];
081        }
082        
083        public void startDocument() throws SAXException {
084            // reset the object
085            result = null;
086            handlerLen=0;
087            patchers=null;
088            patchersLen=0;
089            aborted = false;
090            isUnmarshalInProgress = true;
091            collectTextTop = 0;
092            
093            attStack.clear();
094        }
095        
096        public void endDocument() throws SAXException {
097            runPatchers();
098            isUnmarshalInProgress = false;
099        }
100        
101        public void startElement( String uri, String local, String qname, Attributes atts )
102                throws SAXException {
103            
104            // symbolize
105            uri = uri.intern();
106            local = local.intern();
107            qname = qname.intern();
108            
109            if(result==null) {
110                // this is the root element.
111                // create a root object and start unmarshalling
112                UnmarshallingEventHandler unmarshaller =
113                    grammarInfo.createUnmarshaller(uri,local,this);
114                if(unmarshaller==null) {
115                    // the registry doesn't know about this element.
116                    //
117                    // the no.1 cause of this problem is that your application is configuring
118                    // an XML parser by your self and you forgot to call
119                    // the SAXParserFactory.setNamespaceAware(true). When this happens, you see
120                    // the namespace URI is reported as empty whereas you expect something else.
121                    throw new SAXParseException(
122                        Messages.format( Messages.UNEXPECTED_ROOT_ELEMENT2,
123                            uri, local, computeExpectedRootElements() ),
124                        getLocator() );
125                }
126                result = unmarshaller.owner();
127    
128                pushContentHandler(unmarshaller,0);
129            }
130        
131            processText(true);
132        
133            getCurrentHandler().enterElement(uri,local,qname,atts);
134        }
135    
136        public final void endElement( String uri, String local, String qname )
137                throws SAXException {
138            
139            // symbolize
140            uri = uri.intern();
141            local = local.intern();
142            qname = qname.intern();
143            
144            processText(false);
145            getCurrentHandler().leaveElement(uri,local,qname);
146        }
147        
148        
149        
150        
151        
152        /** Root object that is being unmarshalled. */
153        private Object result;
154        public Object getResult() throws UnmarshalException {
155            if(isUnmarshalInProgress)
156                throw new IllegalStateException();
157            
158            if(!aborted)       return result;
159            
160            // there was an error.
161            throw new UnmarshalException((String)null);
162        }
163    
164        
165        
166    //
167    //
168    // handler stack maintainance
169    //
170    //
171        private UnmarshallingEventHandler[] handlers = new UnmarshallingEventHandler[16];
172        private int[] mementos = new int[16];
173        private int handlerLen=0;
174        
175        public void pushContentHandler( UnmarshallingEventHandler handler, int memento ) {
176            if(handlerLen==handlers.length) {
177                // expand buffer
178                UnmarshallingEventHandler[] h = new UnmarshallingEventHandler[handlerLen*2];
179                int[] m = new int[handlerLen*2];
180                System.arraycopy(handlers,0,h,0,handlerLen);
181                System.arraycopy(mementos,0,m,0,handlerLen);
182                handlers = h;
183                mementos = m;
184            }
185            handlers[handlerLen] = handler;
186            mementos[handlerLen] = memento;
187            handlerLen++;
188        }
189        
190        public void popContentHandler() throws SAXException {
191            handlerLen--;
192            handlers[handlerLen]=null;  // this handler is removed
193            getCurrentHandler().leaveChild(mementos[handlerLen]);
194        }
195    
196        public UnmarshallingEventHandler getCurrentHandler() {
197            return handlers[handlerLen-1];
198        }
199    
200    
201    //
202    //
203    // text handling
204    //
205    //    
206        private StringBuffer buffer = new StringBuffer();
207        
208        protected void consumeText( String str, boolean ignorable ) throws SAXException {
209             if(ignorable && str.trim().length()==0)
210                // if we are allowed to ignore text and
211                // the text is ignorable, ignore.
212                return;
213            
214            // otherwise perform a transition by this token.
215            getCurrentHandler().text(str);
216        }
217        private void processText( boolean ignorable ) throws SAXException {
218            if( shouldCollectText() )
219                consumeText(buffer.toString(),ignorable);
220            
221            // avoid excessive object allocation, but also avoid
222            // keeping a huge array inside StringBuffer.
223            if(buffer.length()<1024)    buffer.setLength(0);
224            else                        buffer = new StringBuffer();
225        }
226        
227        public final void characters( char[] buf, int start, int len ) {
228            if( shouldCollectText() )
229                buffer.append(buf,start,len);
230        }
231    
232        public final void ignorableWhitespace( char[] buf, int start, int len ) {
233            characters(buf,start,len);
234        }
235    
236    
237    
238        
239    //
240    //
241    // namespace binding maintainance
242    //
243    //
244        private String[] nsBind = new String[16];
245        private int nsLen=0;
246        
247        // in the current scope, nsBind[0] - nsBind[idxStack[idxStackTop]-1]
248        // are active.
249        private int[] idxStack = new int[16];
250        private int idxStackTop=0;
251        
252        public void startPrefixMapping( String prefix, String uri ) {
253            if(nsBind.length==nsLen) {
254                // expand the buffer
255                String[] n = new String[nsLen*2];
256                System.arraycopy(nsBind,0,n,0,nsLen);
257                nsBind=n;
258            }
259            nsBind[nsLen++] = prefix;
260            nsBind[nsLen++] = uri;
261        }
262        public void endPrefixMapping( String prefix ) {
263            nsLen-=2;
264        }
265        public String resolveNamespacePrefix( String prefix ) {
266            if(prefix.equals("xml"))
267                return "http://www.w3.org/XML/1998/namespace";
268            
269            for( int i=idxStack[idxStackTop]-2; i>=0; i-=2 ) {
270                if(prefix.equals(nsBind[i]))
271                    return nsBind[i+1];
272            }
273            return null;
274        }
275        //
276        //  NamespaceContext2 implementation 
277        //
278        public Iterator getPrefixes(String uri) {
279            // TODO: could be implemented much faster
280            // wrap it into unmodifiable list so that the remove method
281            // will throw UnsupportedOperationException.
282            return Collections.unmodifiableList(
283                getAllPrefixesInList(uri)).iterator();
284        }
285        
286        private List getAllPrefixesInList(String uri) {
287            List a = new ArrayList();
288            
289            if( uri.equals(XMLConstants.XML_NS_URI) ) {
290                a.add(XMLConstants.XML_NS_PREFIX);
291                return a;
292            }
293            if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) ) {
294                a.add(XMLConstants.XMLNS_ATTRIBUTE);
295                return a;
296            }
297            if( uri==null )
298                throw new IllegalArgumentException();
299              
300            for( int i=nsLen-2; i>=0; i-=2 )
301                if(uri.equals(nsBind[i+1]))
302                    if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
303                        // make sure that this prefix is still effective.
304                        a.add(nsBind[i]);
305             
306            return a;
307        }
308    
309        public String getPrefix(String uri) {
310            if( uri.equals(XMLConstants.XML_NS_URI) )
311                return XMLConstants.XML_NS_PREFIX;
312            if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) )
313                return XMLConstants.XMLNS_ATTRIBUTE;
314            if( uri==null )
315                throw new IllegalArgumentException();
316              
317            for( int i=idxStack[idxStackTop]-2; i>=0; i-=2 )
318                if(uri.equals(nsBind[i+1]))
319                    if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
320                        // make sure that this prefix is still effective.
321                        return nsBind[i];
322             
323            return null;
324        }
325    
326         public String getNamespaceURI(String prefix) {
327             if( prefix.equals(XMLConstants.XMLNS_ATTRIBUTE) )
328                 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
329             if( prefix==null )
330                 throw new IllegalArgumentException();
331            
332             return resolveNamespacePrefix(prefix);
333         }
334    
335    //
336    //
337    // Attribute handling
338    //
339    //
340        // TODO: implementation can be more efficient
341        private final Stack attStack = new Stack();
342        
343        public void pushAttributes( Attributes atts, boolean collectTextFlag ) {
344            // since Attributes object is mutable, it is criticall important
345            // to make a copy.
346            // also symbolize attribute names
347            AttributesImpl a = new AttributesImpl();
348            for( int i=0; i<atts.getLength(); i++ )
349                a.addAttribute(
350                    atts.getURI(i).intern(),
351                    atts.getLocalName(i).intern(),
352                    atts.getQName(i).intern(),
353                    atts.getType(i),
354                    atts.getValue(i) );
355            
356            // <foo xsi:nil="false">some value</foo> is a valid fragment, however
357            // we need a look ahead to correctly handle this case.
358            // (because when we process @xsi:nil, we don't know what the value is,
359            // and by the time we read "false", we can't cancel this attribute anymore.)
360            //
361            // as a quick workaround, we remove @xsi:nil if the value is false.
362            int idx = a.getIndex("http://www.w3.org/2001/XMLSchema-instance","nil");
363            if(idx!=-1) {
364                String v = a.getValue(idx).trim();
365                if(v.equals("false") || v.equals("0"))
366                    a.removeAttribute(idx);
367            }
368            
369            attStack.push(a);
370            
371            // start a new namespace scope
372            if( ++idxStackTop==idxStack.length ) {
373                // reallocation
374                int[] newBuf = new int[idxStack.length*2];
375                System.arraycopy(idxStack,0,newBuf,0,idxStack.length);
376                idxStack = newBuf;
377            }
378            idxStack[idxStackTop] = nsLen;
379            
380            if( ++collectTextTop==collectText.length ) {
381                // reallocation
382                boolean[] newBuf = new boolean[collectText.length*2];
383                System.arraycopy(collectText,0,newBuf,0,collectText.length);
384                collectText = newBuf;
385            }
386            collectText[collectTextTop] = collectTextFlag;
387        }
388        public void popAttributes() {
389            attStack.pop();
390            
391            idxStackTop--;
392            collectTextTop--;
393        }
394        public Attributes getUnconsumedAttributes() {
395            return (Attributes)attStack.peek();
396        }
397        /**
398         * @param uri,local
399         *      has to be interned.
400         */
401        public int getAttribute( String uri, String local ) {
402            // Consider a class that corresponds to the root element.
403            // if this class has a transition from final state by an attribute,
404            // then this attribute transitions are checked when the final
405            // leaveElement event is consumed.
406            //
407            // to handle this case, return "not found" if there is no active
408            // attribute scope
409            if(attStack.isEmpty())  return -1;
410            
411            AttributesImpl a = (AttributesImpl)attStack.peek();
412            
413            return a.getIndexFast(uri,local);
414        }
415        public void consumeAttribute( int idx ) throws SAXException {
416            AttributesImpl a = (AttributesImpl)attStack.peek();
417            
418            String uri = a.getURI(idx);
419            String local = a.getLocalName(idx);
420            String qname = a.getQName(idx);
421            String value = a.getValue(idx);
422    
423            // mark the attribute as consumed
424            // we need to remove the attribute before we process it
425            // because the event handler might access attributes.
426            a.removeAttribute(idx);
427            
428            
429            getCurrentHandler().enterAttribute(uri,local,qname);
430            consumeText(value,false);
431            getCurrentHandler().leaveAttribute(uri,local,qname);
432        }
433        public String eatAttribute( int idx ) throws SAXException {
434            AttributesImpl a = (AttributesImpl)attStack.peek();
435            
436            String value = a.getValue(idx);
437    
438            // mark the attribute as consumed
439            a.removeAttribute(idx);
440            
441            return value;
442        }
443    
444    //
445    //
446    // ID/IDREF related code
447    //
448    //
449        /**
450         * Submitted patchers in the order they've submitted.
451         * Many XML vocabulary doesn't use ID/IDREF at all, so we
452         * initialize it with null.
453         */
454        private Runnable[] patchers = null;
455        private int patchersLen = 0;
456        
457        public void addPatcher( Runnable job ) {
458            // re-allocate buffer if necessary
459            if( patchers==null )
460                patchers = new Runnable[32];
461            if( patchers.length == patchersLen ) {
462                Runnable[] buf = new Runnable[patchersLen*2];
463                System.arraycopy(patchers,0,buf,0,patchersLen);
464                patchers = buf;
465            }
466            patchers[patchersLen++] = job;
467        }
468        
469        /** Executes all the patchers. */
470        private void runPatchers() {
471            if( patchers!=null ) {
472                for( int i=0; i<patchersLen; i++ )
473                    patchers[i].run();
474            }
475        }
476    
477        /** Records ID->Object map. */
478        private Hashtable idmap = null;
479    
480        public String addToIdTable( String id ) {
481            if(idmap==null)     idmap = new Hashtable();
482            idmap.put( id, getCurrentHandler().owner() );
483            return id;
484        }
485        
486        public Object getObjectFromId( String id ) {
487            if(idmap==null)     return null;
488            return idmap.get(id);
489        }
490        
491    
492    
493    //
494    //
495    // Other SAX callbacks
496    //
497    //
498        public void skippedEntity( String name ) {
499            // TODO: throw an exception, perhaps?
500        }
501        public void processingInstruction( String target, String data ) {
502            // just ignore
503        }
504        public void setDocumentLocator( Locator loc ) {
505            locator = loc;
506        }
507        public Locator getLocator() { return locator; }
508        
509        private Locator locator;
510    
511    
512    //
513    //
514    // error handling
515    //
516    //
517        private final UnmarshallerImpl parent;
518        private boolean aborted = false;
519        
520        public void handleEvent(ValidationEvent event, boolean canRecover ) throws SAXException {
521            ValidationEventHandler eventHandler;
522            try {
523                eventHandler = parent.getEventHandler();
524            } catch( JAXBException e ) {
525                // impossible.
526                throw new InternalError();
527            }
528    
529            boolean recover = eventHandler.handleEvent(event);
530            
531            // if the handler says "abort", we will not return the object
532            // from the unmarshaller.getResult()
533            if(!recover)    aborted = true;
534            
535            if( !canRecover || !recover )
536                throw new SAXException( new UnmarshalException(
537                    event.getMessage(),
538                    event.getLinkedException() ) );
539        }
540      
541    //
542    //
543    // ValidationContext implementation
544    //
545    //
546        public String getBaseUri() { return null; }
547        public boolean isUnparsedEntity(String s) { return true; }
548        public boolean isNotation(String s) { return true; }
549    
550    
551    //
552    //
553    // debug trace methods
554    //
555    //
556        private Tracer tracer;
557        public void setTracer( Tracer t ) {
558            this.tracer = t;
559        }
560        public Tracer getTracer() {
561            if(tracer==null)
562                tracer = new Tracer.Standard();
563            return tracer;
564        }
565        
566        /**
567         * Computes the names of possible root elements for a better error diagnosis.
568         */
569        private String computeExpectedRootElements() {
570            String r = "";
571            
572            String[] probePoints = grammarInfo.getProbePoints();
573            for( int i=0; i<probePoints.length; i+=2 ) {
574                if( grammarInfo.recognize(probePoints[i],probePoints[i+1]) ) {
575                    if(r.length()!=0)   r+=',';
576                    r += "<{"+probePoints[i]+"}"+probePoints[i+1]+">";
577                }
578            }
579            
580            return r;
581        }
582    
583    }