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