//
// 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 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2004.05.05 at 11:23:00 PDT 
//

/*
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

/*
 * @(#)$Id: SAXUnmarshallerHandlerImpl.java,v 1.9 2003/05/21 01:18:16 kk122374 Exp $
 */
package CMSDDL.impl.runtime;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

import javax.xml.XMLConstants;
import javax.xml.bind.JAXBException;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;

import org.xml.sax.Attributes;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import com.sun.xml.bind.unmarshaller.Messages;
import com.sun.xml.bind.unmarshaller.Tracer;
import com.sun.xml.bind.util.AttributesImpl;

/**
 * Implementation of {@link UnmarshallerHandler}.
 * 
 * This object converts SAX events into unmarshaller events and
 * cooridnates the entire unmarshalling process.
 *
 * @author
 *  <a href="mailto:kohsuke.kawaguchi@sun.com>Kohsuke KAWAGUCHI</a>
 */
public class SAXUnmarshallerHandlerImpl
    implements SAXUnmarshallerHandler, UnmarshallingContext
{
    /**
     * This flag is set to true at the startDocument event
     * and false at the endDocument event.
     * 
     * Until the first document is unmarshalled, we don't
     * want to return an object. So this variable is initialized
     * to true.
     */
    private boolean isUnmarshalInProgress = true;
    
    /** stack of collectText flag. False means text can be ignored. */ 
    private boolean[] collectText = new boolean[16];
    private int collectTextTop = 0;
    
    
    public SAXUnmarshallerHandlerImpl( UnmarshallerImpl _parent, GrammarInfo _gi ) {
        this.parent = _parent;
        grammarInfo = _gi;
        startPrefixMapping("",""); // by default, the default ns is bound to "".
     }
    
    private final GrammarInfo grammarInfo;
    public GrammarInfo getGrammarInfo() { return grammarInfo; }
    
    /**
     * Returns true if we should be collecting characters in the current element.
     */
    private final boolean shouldCollectText() {
        return collectText[collectTextTop];
    }
    
    public void startDocument() throws SAXException {
        // reset the object
        result = null;
        handlerLen=0;
        patchers=null;
        patchersLen=0;
        aborted = false;
        isUnmarshalInProgress = true;
        collectTextTop = 0;
        
        attStack.clear();
    }
    
    public void endDocument() throws SAXException {
        runPatchers();
        isUnmarshalInProgress = false;
    }
    
    public void startElement( String uri, String local, String qname, Attributes atts )
            throws SAXException {
        
        // symbolize
        uri = uri.intern();
        local = local.intern();
        qname = qname.intern();
        
        if(result==null) {
            // this is the root element.
            // create a root object and start unmarshalling
            UnmarshallingEventHandler unmarshaller =
                grammarInfo.createUnmarshaller(uri,local,this);
            if(unmarshaller==null) {
                // the registry doesn't know about this element.
                //
                // the no.1 cause of this problem is that your application is configuring
                // an XML parser by your self and you forgot to call
                // the SAXParserFactory.setNamespaceAware(true). When this happens, you see
                // the namespace URI is reported as empty whereas you expect something else.
                throw new SAXParseException(
                    Messages.format( Messages.UNEXPECTED_ROOT_ELEMENT2,
                        uri, local, computeExpectedRootElements() ),
                    getLocator() );
            }
            result = unmarshaller.owner();

            pushContentHandler(unmarshaller,0);
        }
    
        processText(true);
    
        getCurrentHandler().enterElement(uri,local,qname,atts);
    }

    public final void endElement( String uri, String local, String qname )
            throws SAXException {
        
        // symbolize
        uri = uri.intern();
        local = local.intern();
        qname = qname.intern();
        
        processText(false);
        getCurrentHandler().leaveElement(uri,local,qname);
    }
    
    
    
    
    
    /** Root object that is being unmarshalled. */
    private Object result;
    public Object getResult() throws UnmarshalException {
        if(isUnmarshalInProgress)
            throw new IllegalStateException();
        
        if(!aborted)       return result;
        
        // there was an error.
        throw new UnmarshalException((String)null);
    }

    
    
//
//
// handler stack maintainance
//
//
    private UnmarshallingEventHandler[] handlers = new UnmarshallingEventHandler[16];
    private int[] mementos = new int[16];
    private int handlerLen=0;
    
    public void pushContentHandler( UnmarshallingEventHandler handler, int memento ) {
        if(handlerLen==handlers.length) {
            // expand buffer
            UnmarshallingEventHandler[] h = new UnmarshallingEventHandler[handlerLen*2];
            int[] m = new int[handlerLen*2];
            System.arraycopy(handlers,0,h,0,handlerLen);
            System.arraycopy(mementos,0,m,0,handlerLen);
            handlers = h;
            mementos = m;
        }
        handlers[handlerLen] = handler;
        mementos[handlerLen] = memento;
        handlerLen++;
    }
    
    public void popContentHandler() throws SAXException {
        handlerLen--;
        handlers[handlerLen]=null;  // this handler is removed
        getCurrentHandler().leaveChild(mementos[handlerLen]);
    }

    public UnmarshallingEventHandler getCurrentHandler() {
        return handlers[handlerLen-1];
    }


//
//
// text handling
//
//    
    private StringBuffer buffer = new StringBuffer();
    
    protected void consumeText( String str, boolean ignorable ) throws SAXException {
         if(ignorable && str.trim().length()==0)
            // if we are allowed to ignore text and
            // the text is ignorable, ignore.
            return;
        
        // otherwise perform a transition by this token.
        getCurrentHandler().text(str);
    }
    private void processText( boolean ignorable ) throws SAXException {
        if( shouldCollectText() )
            consumeText(buffer.toString(),ignorable);
        
        // avoid excessive object allocation, but also avoid
        // keeping a huge array inside StringBuffer.
        if(buffer.length()<1024)    buffer.setLength(0);
        else                        buffer = new StringBuffer();
    }
    
    public final void characters( char[] buf, int start, int len ) {
        if( shouldCollectText() )
            buffer.append(buf,start,len);
    }

    public final void ignorableWhitespace( char[] buf, int start, int len ) {
        characters(buf,start,len);
    }



    
//
//
// namespace binding maintainance
//
//
    private String[] nsBind = new String[16];
    private int nsLen=0;
    
    // in the current scope, nsBind[0] - nsBind[idxStack[idxStackTop]-1]
    // are active.
    private int[] idxStack = new int[16];
    private int idxStackTop=0;
    
    public void startPrefixMapping( String prefix, String uri ) {
        if(nsBind.length==nsLen) {
            // expand the buffer
            String[] n = new String[nsLen*2];
            System.arraycopy(nsBind,0,n,0,nsLen);
            nsBind=n;
        }
        nsBind[nsLen++] = prefix;
        nsBind[nsLen++] = uri;
    }
    public void endPrefixMapping( String prefix ) {
        nsLen-=2;
    }
    public String resolveNamespacePrefix( String prefix ) {
        if(prefix.equals("xml"))
            return "http://www.w3.org/XML/1998/namespace";
        
        for( int i=idxStack[idxStackTop]-2; i>=0; i-=2 ) {
            if(prefix.equals(nsBind[i]))
                return nsBind[i+1];
        }
        return null;
    }
    //
    //  NamespaceContext2 implementation 
    //
    public Iterator getPrefixes(String uri) {
        // TODO: could be implemented much faster
        // wrap it into unmodifiable list so that the remove method
        // will throw UnsupportedOperationException.
        return Collections.unmodifiableList(
            getAllPrefixesInList(uri)).iterator();
    }
    
    private List getAllPrefixesInList(String uri) {
        List a = new ArrayList();
        
        if( uri.equals(XMLConstants.XML_NS_URI) ) {
            a.add(XMLConstants.XML_NS_PREFIX);
            return a;
        }
        if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) ) {
            a.add(XMLConstants.XMLNS_ATTRIBUTE);
            return a;
        }
        if( uri==null )
            throw new IllegalArgumentException();
          
        for( int i=nsLen-2; i>=0; i-=2 )
            if(uri.equals(nsBind[i+1]))
                if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
                    // make sure that this prefix is still effective.
                    a.add(nsBind[i]);
         
        return a;
    }

    public String getPrefix(String uri) {
        if( uri.equals(XMLConstants.XML_NS_URI) )
            return XMLConstants.XML_NS_PREFIX;
        if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) )
            return XMLConstants.XMLNS_ATTRIBUTE;
        if( uri==null )
            throw new IllegalArgumentException();
          
        for( int i=idxStack[idxStackTop]-2; i>=0; i-=2 )
            if(uri.equals(nsBind[i+1]))
                if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
                    // make sure that this prefix is still effective.
                    return nsBind[i];
         
        return null;
    }

     public String getNamespaceURI(String prefix) {
         if( prefix.equals(XMLConstants.XMLNS_ATTRIBUTE) )
             return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
         if( prefix==null )
             throw new IllegalArgumentException();
        
         return resolveNamespacePrefix(prefix);
     }

//
//
// Attribute handling
//
//
    // TODO: implementation can be more efficient
    private final Stack attStack = new Stack();
    
    public void pushAttributes( Attributes atts, boolean collectTextFlag ) {
        // since Attributes object is mutable, it is criticall important
        // to make a copy.
        // also symbolize attribute names
        AttributesImpl a = new AttributesImpl();
        for( int i=0; i<atts.getLength(); i++ )
            a.addAttribute(
                atts.getURI(i).intern(),
                atts.getLocalName(i).intern(),
                atts.getQName(i).intern(),
                atts.getType(i),
                atts.getValue(i) );
        
        // <foo xsi:nil="false">some value</foo> is a valid fragment, however
        // we need a look ahead to correctly handle this case.
        // (because when we process @xsi:nil, we don't know what the value is,
        // and by the time we read "false", we can't cancel this attribute anymore.)
        //
        // as a quick workaround, we remove @xsi:nil if the value is false.
        int idx = a.getIndex("http://www.w3.org/2001/XMLSchema-instance","nil");
        if(idx!=-1) {
            String v = a.getValue(idx).trim();
            if(v.equals("false") || v.equals("0"))
                a.removeAttribute(idx);
        }
        
        attStack.push(a);
        
        // start a new namespace scope
        if( ++idxStackTop==idxStack.length ) {
            // reallocation
            int[] newBuf = new int[idxStack.length*2];
            System.arraycopy(idxStack,0,newBuf,0,idxStack.length);
            idxStack = newBuf;
        }
        idxStack[idxStackTop] = nsLen;
        
        if( ++collectTextTop==collectText.length ) {
            // reallocation
            boolean[] newBuf = new boolean[collectText.length*2];
            System.arraycopy(collectText,0,newBuf,0,collectText.length);
            collectText = newBuf;
        }
        collectText[collectTextTop] = collectTextFlag;
    }
    public void popAttributes() {
        attStack.pop();
        
        idxStackTop--;
        collectTextTop--;
    }
    public Attributes getUnconsumedAttributes() {
        return (Attributes)attStack.peek();
    }
    /**
     * @param uri,local
     *      has to be interned.
     */
    public int getAttribute( String uri, String local ) {
        // Consider a class that corresponds to the root element.
        // if this class has a transition from final state by an attribute,
        // then this attribute transitions are checked when the final
        // leaveElement event is consumed.
        //
        // to handle this case, return "not found" if there is no active
        // attribute scope
        if(attStack.isEmpty())  return -1;
        
        AttributesImpl a = (AttributesImpl)attStack.peek();
        
        return a.getIndexFast(uri,local);
    }
    public void consumeAttribute( int idx ) throws SAXException {
        AttributesImpl a = (AttributesImpl)attStack.peek();
        
        String uri = a.getURI(idx);
        String local = a.getLocalName(idx);
        String qname = a.getQName(idx);
        String value = a.getValue(idx);

        // mark the attribute as consumed
        // we need to remove the attribute before we process it
        // because the event handler might access attributes.
        a.removeAttribute(idx);
        
        
        getCurrentHandler().enterAttribute(uri,local,qname);
        consumeText(value,false);
        getCurrentHandler().leaveAttribute(uri,local,qname);
    }
    public String eatAttribute( int idx ) throws SAXException {
        AttributesImpl a = (AttributesImpl)attStack.peek();
        
        String value = a.getValue(idx);

        // mark the attribute as consumed
        a.removeAttribute(idx);
        
        return value;
    }

//
//
// ID/IDREF related code
//
//
    /**
     * Submitted patchers in the order they've submitted.
     * Many XML vocabulary doesn't use ID/IDREF at all, so we
     * initialize it with null.
     */
    private Runnable[] patchers = null;
    private int patchersLen = 0;
    
    public void addPatcher( Runnable job ) {
        // re-allocate buffer if necessary
        if( patchers==null )
            patchers = new Runnable[32];
        if( patchers.length == patchersLen ) {
            Runnable[] buf = new Runnable[patchersLen*2];
            System.arraycopy(patchers,0,buf,0,patchersLen);
            patchers = buf;
        }
        patchers[patchersLen++] = job;
    }
    
    /** Executes all the patchers. */
    private void runPatchers() {
        if( patchers!=null ) {
            for( int i=0; i<patchersLen; i++ )
                patchers[i].run();
        }
    }

    /** Records ID->Object map. */
    private Hashtable idmap = null;

    public String addToIdTable( String id ) {
        if(idmap==null)     idmap = new Hashtable();
        idmap.put( id, getCurrentHandler().owner() );
        return id;
    }
    
    public Object getObjectFromId( String id ) {
        if(idmap==null)     return null;
        return idmap.get(id);
    }
    


//
//
// Other SAX callbacks
//
//
    public void skippedEntity( String name ) {
        // TODO: throw an exception, perhaps?
    }
    public void processingInstruction( String target, String data ) {
        // just ignore
    }
    public void setDocumentLocator( Locator loc ) {
        locator = loc;
    }
    public Locator getLocator() { return locator; }
    
    private Locator locator;


//
//
// error handling
//
//
    private final UnmarshallerImpl parent;
    private boolean aborted = false;
    
    public void handleEvent(ValidationEvent event, boolean canRecover ) throws SAXException {
        ValidationEventHandler eventHandler;
        try {
            eventHandler = parent.getEventHandler();
        } catch( JAXBException e ) {
            // impossible.
            throw new InternalError();
        }

        boolean recover = eventHandler.handleEvent(event);
        
        // if the handler says "abort", we will not return the object
        // from the unmarshaller.getResult()
        if(!recover)    aborted = true;
        
        if( !canRecover || !recover )
            throw new SAXException( new UnmarshalException(
                event.getMessage(),
                event.getLinkedException() ) );
    }
  
//
//
// ValidationContext implementation
//
//
    public String getBaseUri() { return null; }
    public boolean isUnparsedEntity(String s) { return true; }
    public boolean isNotation(String s) { return true; }


//
//
// debug trace methods
//
//
    private Tracer tracer;
    public void setTracer( Tracer t ) {
        this.tracer = t;
    }
    public Tracer getTracer() {
        if(tracer==null)
            tracer = new Tracer.Standard();
        return tracer;
    }
    
    /**
     * Computes the names of possible root elements for a better error diagnosis.
     */
    private String computeExpectedRootElements() {
        String r = "";
        
        String[] probePoints = grammarInfo.getProbePoints();
        for( int i=0; i<probePoints.length; i+=2 ) {
            if( grammarInfo.recognize(probePoints[i],probePoints[i+1]) ) {
                if(r.length()!=0)   r+=',';
                r += "<{"+probePoints[i]+"}"+probePoints[i+1]+">";
            }
        }
        
        return r;
    }

}
