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.HashSet;
011import java.util.Iterator;
012import java.util.Set;
013
014import javax.xml.bind.JAXBException;
015import javax.xml.bind.ValidationEvent;
016import javax.xml.bind.ValidationEventHandler;
017import javax.xml.bind.helpers.NotIdentifiableEventImpl;
018import javax.xml.bind.helpers.ValidationEventLocatorImpl;
019
020import org.xml.sax.ContentHandler;
021import org.xml.sax.SAXException;
022import org.xml.sax.helpers.AttributesImpl;
023
024import com.sun.xml.bind.JAXBAssertionError;
025import com.sun.xml.bind.JAXBObject;
026import com.sun.xml.bind.marshaller.IdentifiableObject;
027import com.sun.xml.bind.marshaller.Messages;
028import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
029import com.sun.xml.bind.serializer.AbortSerializationException;
030import com.sun.xml.bind.serializer.Util;
031
032/**
033 * XMLSerializer that produces SAX2 events.
034 * 
035 * To marshal an object, create an instance of SAXMarshaller
036 * and call the serializeElements method of the XMLSerializable
037 * object that you want to marshal.
038 * 
039 * @author  Kohsuke Kawaguchi
040 */
041public class SAXMarshaller implements XMLSerializer
042{
043    /**
044     * "Attributes" object that is passed to the startElement event.
045     * One object is reused throughout the marshalling.
046     */
047    private final AttributesImpl attributes = new AttributesImpl();
048 
049    /** This object receives SAX2 events generated from the marshaller. */
050    private final ContentHandler writer;
051    
052    /** Marshaller object to which this object belongs. */
053    private final MarshallerImpl owner;
054    
055    /** Objects referenced through IDREF. */
056    private final Set idReferencedObjects = new HashSet();
057    
058    /** Objects with ID. */
059    private final Set objectsWithId = new HashSet();
060    
061    /** Object currently marshalling itself. */
062    private JAXBObject currentTarget;
063    
064    /**
065     * Creates a marshalling context by designating the ContentHandler
066     * that receives generated SAX2 events.
067     */
068    public SAXMarshaller( ContentHandler _writer, NamespacePrefixMapper prefixMapper, MarshallerImpl _owner ) {
069        this.writer = _writer;
070        this.owner = _owner;
071        this.nsContext = new NamespaceContextImpl(
072            prefixMapper!=null?prefixMapper:defaultNamespacePrefixMapper);
073    }
074    
075    /** namespace context. */
076    private final NamespaceContextImpl nsContext;
077    
078    public NamespaceContext2 getNamespaceContext() { return nsContext; }
079    
080    //
081    //
082    // name stack
083    //
084    //
085    
086    /** Element name stack implemented as an array of (uri,local) pairs. */
087    private String[] elementStack = new String[16];;
088    private int elementLen=0;
089    
090    
091    
092    private void pushElement( String uri, String local ) {
093        if(elementStack.length==elementLen) {
094            // reallocate buffer
095            String[] buf = new String[elementStack.length*2];
096            System.arraycopy( elementStack, 0, buf, 0, elementStack.length );
097            elementStack = buf;
098        }
099        elementStack[elementLen++] = uri;
100        elementStack[elementLen++] = local;
101    }
102    
103    private void popElement() { elementLen-=2;  }
104    
105    private String getCurrentElementUri()   { return elementStack[elementLen-2]; }
106    private String getCurrentElementLocal() { return elementStack[elementLen-1]; }
107    
108    
109    
110    
111    
112    /**
113     * Starts marshalling of an element.
114     * Calling this method will push the internal state into the
115     * internal stack.
116     */
117    public void startElement( String uri, String local ) throws SAXException {
118        boolean isRoot = false;
119        String suggestion = null;
120        if( elementLen==0 ) {
121            isRoot = true;
122            // this is the root element. suggest this as the default namespace
123            suggestion = "";
124        }
125        
126        writePendingText();
127        nsContext.startElement();
128        pushElement(uri,local); // memorize element name
129                
130        // declare this uri
131        nsContext.declareNamespace(uri,suggestion,false);
132        
133        // if this is the root element, declare user-specified namespace URIs.
134        if( isRoot ) {
135            // work defensively. we are calling an user-defined method.
136            String[] uris = nsContext.getNamespacePrefixMapper().getPreDeclaredNamespaceUris();
137            if( uris!=null ) {
138                for( int i=0; i<uris.length; i++ ) {
139                    if( uris[i]!=null )
140                        nsContext.declareNamespace(uris[i],null,false);
141                }
142            }
143        }
144    }
145    
146    
147    private final PrefixCallback startPrefixCallback = new PrefixCallback() {
148        public void onPrefixMapping( String prefix, String nsUri ) throws SAXException {
149            writer.startPrefixMapping(prefix,nsUri);
150        }
151    };
152    private final PrefixCallback endPrefixCallback = new PrefixCallback() {
153        public void onPrefixMapping( String prefix, String nsUri ) throws SAXException {
154            writer.endPrefixMapping(prefix);
155        }
156    };
157    
158    public void endNamespaceDecls() throws SAXException {
159        nsContext.endNamespaceDecls();
160    }
161    
162    /**
163     * Switches to the "marshal child texts/elements" mode.
164     * This method has to be called after the 1st pass is completed.
165     */
166    public void endAttributes() throws SAXException {
167        // calculate QName of the element
168        String uri = getCurrentElementUri();
169        String local = getCurrentElementLocal();
170        
171        String prefix = nsContext.getPrefix(uri);
172        _assert(prefix!=null);  // since we've declared it, it should be available
173        
174        String qname;
175        if(prefix.length()!=0 ) 
176            qname = prefix+':'+local;
177        else
178            qname = local;
179
180        // fire startPrefixMapping events
181        nsContext.iterateDeclaredPrefixes(startPrefixCallback);
182        
183        // fire the startElement event
184        writer.startElement( uri, local, qname, attributes );
185        
186        
187        // reset attributes
188        attributes.clear();
189        
190        // prepare to collect texts
191        textBuf.setLength(0);
192    }
193    
194    /**
195     * Ends marshalling of an element.
196     * Pops the internal stack.
197     */
198    public void endElement() throws SAXException {
199        writePendingText();
200        
201        String uri = getCurrentElementUri();
202        String local = getCurrentElementLocal();
203        
204        String prefix = nsContext.getPrefix(uri);
205        _assert(prefix!=null);      // we've declared it earlier.
206        
207        String qname;
208        if(prefix.length()!=0)
209            qname = prefix+':'+local;
210        else
211            qname = local;
212        
213        writer.endElement( uri, local, qname );
214
215        // pop namespace bindings and
216        // fire endPrefixMapping events
217        nsContext.iterateDeclaredPrefixes(endPrefixCallback);
218        
219        popElement();
220        
221        // prepare to collect texts
222        textBuf.setLength(0);
223        
224        nsContext.endElement();
225    }
226    
227    
228    /** Buffer for collecting characters. */
229    private final StringBuffer textBuf = new StringBuffer();
230    
231    /**
232     * Marshalls text.
233     * 
234     * <p>
235     * This method can be called (i) after the startAttribute method
236     * and (ii) before the endAttribute method, to marshal attribute values.
237     * If the method is called more than once, those texts are considered
238     * as separated by whitespaces. For example,
239     * 
240     * <pre>
241     * c.startAttribute();
242     * c.text("abc");
243     * c.text("def");
244     * c.endAttribute("","foo");
245     * </pre>
246     * 
247     * will generate foo="abc def".
248     * 
249     * <p>
250     * Similarly, this method can be called after the endAttributes
251     * method to marshal texts inside elements. The same rule about
252     * multiple invokations apply to this case, too. For example,
253     * 
254     * <pre>
255     * c.startElement("","foo");
256     * c.endAttributes();
257     * c.text("abc");
258     * c.text("def");
259     *   c.startElement("","bar");
260     *   c.endAttributes();
261     *   c.endElement();
262     * c.text("ghi");
263     * c.endElement();
264     * </pre>
265     * 
266     * will generate <code>&lt;foo>abc def&lt;bar/>ghi&lt;/foo></code>.
267     */
268    public void text( String text, String fieldName ) throws SAXException {
269        // If the assertion fails, it must be a bug of xjc.
270        // right now, we are not expecting the text method to be called.
271        if(text==null) {
272            reportError(Util.createMissingObjectError(currentTarget,fieldName));
273            return;
274        }
275    
276        if(textBuf.length()!=0)
277            textBuf.append(' ');
278        textBuf.append(text);
279    }
280    
281    /**
282     * Writes pending text (characters inside elements) to the writer.
283     * This method is called from startElement and endElement.
284     */
285    private void writePendingText() throws SAXException {
286        // assert(textBuf!=null);
287        int len = textBuf.length();
288        
289        if(len!=0)
290            writer.characters( textBuf.toString().toCharArray(), 0, len );
291    }
292    
293    /**
294     * Starts marshalling of an attribute.
295     * 
296     * The marshalling of an attribute will be done by
297     * <ol>
298     *  <li>call the startAttribute method
299     *  <li>call the text method (several times if necessary)
300     *  <li>call the endAttribute method
301     * </ol>
302     * 
303     * No two attributes can be marshalled at the same time.
304     * Note that the whole attribute marshalling must be happened
305     * after the startElement method and before the endAttributes method.
306     */
307    public void startAttribute( String uri, String local ) {
308        // initialize the buffer to collect attribute value
309        textBuf.setLength(0);
310        
311        // remember the attribute name. We'll use this value later.
312        this.attNamespaceUri = uri;
313        this.attLocalName = local;
314    }
315    
316    // used to keep attribute names until the endAttribute method is called.
317    private String attNamespaceUri;
318    private String attLocalName;
319
320    public void endAttribute() {
321        // use CDATA as the attribute type. This preserves
322        // successive processors to collapse whitespaces.
323        // (we cannot prevent characters like #xD to be replaced to
324        // #x20, though).
325        //
326        // strictly speaking, attribute value normalization should be
327        // provessed by XML parser, so it's unclear whether XML writer
328        // uses this type value.
329        //
330        // in any way, CDATA type is the safest choice here.
331        
332        String qname;
333        if(attNamespaceUri.length()==0) {
334            // default namespace. don't need prefix
335            qname = attLocalName;
336        } else {
337            qname = nsContext.declareNamespace(attNamespaceUri,null,true)+':'+attLocalName;
338        }
339
340        attributes.addAttribute(attNamespaceUri,attLocalName,qname,"CDATA",textBuf.toString());
341    }
342    
343    public String onID( IdentifiableObject owner, String value ) throws SAXException {
344        objectsWithId.add(owner);
345        return value;
346    }
347    public String onIDREF( IdentifiableObject obj ) throws SAXException {
348        idReferencedObjects.add(obj);
349        String id = obj.____jaxb____getId();
350        if(id==null) {
351            reportError( new NotIdentifiableEventImpl(
352                ValidationEvent.ERROR,
353                Messages.format(Messages.ERR_NOT_IDENTIFIABLE),
354                new ValidationEventLocatorImpl(obj) ) );
355        }
356        return id;
357    }
358    
359    void reconcileID() throws AbortSerializationException {
360        // find objects that were not a part of the object graph 
361        idReferencedObjects.removeAll(objectsWithId);
362        
363        for( Iterator itr=idReferencedObjects.iterator(); itr.hasNext(); ) {
364            IdentifiableObject o = (IdentifiableObject)itr.next();
365            reportError( new NotIdentifiableEventImpl(
366                ValidationEvent.ERROR,
367                Messages.format(Messages.ERR_DANGLING_IDREF,o.____jaxb____getId()),
368                new ValidationEventLocatorImpl(o) ) );
369        }
370        
371        // clear the garbage
372        idReferencedObjects.clear();
373        objectsWithId.clear();
374    }
375
376
377
378    public void childAsBody( JAXBObject o, String fieldName ) throws SAXException {
379        if(o==null) {
380            // if null is passed, it usually means that the content tree object
381            // doesn't have some of its required property.
382            reportMissingObjectError(fieldName);
383            // as a marshaller, we should be generous, so we'll continue to marshal
384            // this document by skipping this missing object.
385            return;
386        }
387        
388        JAXBObject oldTarget = currentTarget;
389        currentTarget = o;
390        
391        owner.context.getGrammarInfo().castToXMLSerializable(o).serializeBody(this);
392        
393        currentTarget = oldTarget;
394    }
395    
396    public void childAsAttributes( JAXBObject o, String fieldName ) throws SAXException {
397        if(o==null) {
398            reportMissingObjectError(fieldName);
399            return;
400        }
401
402        JAXBObject oldTarget = currentTarget;
403        currentTarget = o;
404        
405        owner.context.getGrammarInfo().castToXMLSerializable(o).serializeAttributes(this);
406        
407        currentTarget = oldTarget;
408    }
409    
410    public void childAsURIs( JAXBObject o, String fieldName ) throws SAXException {
411        if(o==null) {
412            reportMissingObjectError(fieldName);
413            return;
414        }
415        
416        JAXBObject oldTarget = currentTarget;
417        currentTarget = o;
418        
419        owner.context.getGrammarInfo().castToXMLSerializable(o).serializeURIs(this);
420        
421        currentTarget = oldTarget;
422    }
423    
424
425    public void reportError( ValidationEvent ve ) throws AbortSerializationException {
426        ValidationEventHandler handler;
427        
428        try {
429            handler = owner.getEventHandler();
430        } catch( JAXBException e ) {
431            throw new AbortSerializationException(e);
432        }
433        
434        if(!handler.handleEvent(ve)) {
435            if(ve.getLinkedException() instanceof Exception)
436                throw new AbortSerializationException((Exception)ve.getLinkedException());
437            else
438                throw new AbortSerializationException(ve.getMessage());
439        }
440    }
441    
442    
443    public void reportMissingObjectError(String fieldName) throws SAXException {
444        reportError(Util.createMissingObjectError(currentTarget,fieldName));
445    }
446    
447    
448    private static void _assert( boolean b ) {
449        if(!b)
450            throw new JAXBAssertionError();
451    }
452    
453    /**
454     * Default {@link NamespacePrefixMapper} implementation used when
455     * it is not specified by the user.
456     */
457    private static NamespacePrefixMapper defaultNamespacePrefixMapper = new NamespacePrefixMapper() {
458        public String getPreferredPrefix(String namespaceUri, String suggestion, boolean requirePrefix) {
459            if( namespaceUri.equals("http://www.w3.org/2001/XMLSchema-instance") )
460                return "xsi";
461            return suggestion;
462        }
463    };
464}