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