KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > tools > xjc > runtime > SAXMarshaller


1 package com.sun.tools.xjc.runtime;
2
3 import java.util.HashSet JavaDoc;
4 import java.util.Iterator JavaDoc;
5 import java.util.Set JavaDoc;
6
7 import javax.xml.bind.JAXBException;
8 import javax.xml.bind.ValidationEvent;
9 import javax.xml.bind.ValidationEventHandler;
10 import javax.xml.bind.helpers.NotIdentifiableEventImpl;
11 import javax.xml.bind.helpers.ValidationEventLocatorImpl;
12
13 import org.xml.sax.ContentHandler JavaDoc;
14 import org.xml.sax.SAXException JavaDoc;
15 import org.xml.sax.helpers.AttributesImpl JavaDoc;
16
17 import com.sun.xml.bind.JAXBAssertionError;
18 import com.sun.xml.bind.JAXBObject;
19 import com.sun.xml.bind.marshaller.IdentifiableObject;
20 import com.sun.xml.bind.marshaller.Messages;
21 import com.sun.xml.bind.marshaller.NamespacePrefixMapper;
22 import com.sun.xml.bind.serializer.AbortSerializationException;
23 import com.sun.xml.bind.serializer.Util;
24
25 /**
26  * XMLSerializer that produces SAX2 events.
27  *
28  * To marshal an object, create an instance of SAXMarshaller
29  * and call the serializeElements method of the XMLSerializable
30  * object that you want to marshal.
31  *
32  * @author Kohsuke Kawaguchi
33  */

34 public class SAXMarshaller implements XMLSerializer
35 {
36     /**
37      * "Attributes" object that is passed to the startElement event.
38      * One object is reused throughout the marshalling.
39      */

40     private final AttributesImpl JavaDoc attributes = new AttributesImpl JavaDoc();
41  
42     /** This object receives SAX2 events generated from the marshaller. */
43     private final ContentHandler JavaDoc writer;
44     
45     /** Marshaller object to which this object belongs. */
46     private final MarshallerImpl owner;
47     
48     /** Objects referenced through IDREF. */
49     private final Set JavaDoc idReferencedObjects = new HashSet JavaDoc();
50     
51     /** Objects with ID. */
52     private final Set JavaDoc objectsWithId = new HashSet JavaDoc();
53     
54     /** Object currently marshalling itself. */
55     private JAXBObject currentTarget;
56     
57     /**
58      * Creates a marshalling context by designating the ContentHandler
59      * that receives generated SAX2 events.
60      */

61     public SAXMarshaller( ContentHandler JavaDoc _writer, NamespacePrefixMapper prefixMapper, MarshallerImpl _owner ) {
62         this.writer = _writer;
63         this.owner = _owner;
64         this.nsContext = new NamespaceContextImpl(
65             prefixMapper!=null?prefixMapper:defaultNamespacePrefixMapper);
66     }
67     
68     /** namespace context. */
69     private final NamespaceContextImpl nsContext;
70     
71     public NamespaceContext2 getNamespaceContext() { return nsContext; }
72     
73     //
74
//
75
// name stack
76
//
77
//
78

79     /** Element name stack implemented as an array of (uri,local) pairs. */
80     private String JavaDoc[] elementStack = new String JavaDoc[16];;
81     private int elementLen=0;
82     
83     
84     
85     private void pushElement( String JavaDoc uri, String JavaDoc local ) {
86         if(elementStack.length==elementLen) {
87             // reallocate buffer
88
String JavaDoc[] buf = new String JavaDoc[elementStack.length*2];
89             System.arraycopy( elementStack, 0, buf, 0, elementStack.length );
90             elementStack = buf;
91         }
92         elementStack[elementLen++] = uri;
93         elementStack[elementLen++] = local;
94     }
95     
96     private void popElement() { elementLen-=2; }
97     
98     private String JavaDoc getCurrentElementUri() { return elementStack[elementLen-2]; }
99     private String JavaDoc getCurrentElementLocal() { return elementStack[elementLen-1]; }
100     
101     
102     
103     
104     
105     /**
106      * Starts marshalling of an element.
107      * Calling this method will push the internal state into the
108      * internal stack.
109      */

110     public void startElement( String JavaDoc uri, String JavaDoc local ) throws SAXException JavaDoc {
111         boolean isRoot = false;
112         String JavaDoc suggestion = null;
113         if( elementLen==0 ) {
114             isRoot = true;
115             // this is the root element. suggest this as the default namespace
116
suggestion = "";
117         }
118         
119         writePendingText();
120         nsContext.startElement();
121         pushElement(uri,local); // memorize element name
122

123         // declare this uri
124
nsContext.declareNamespace(uri,suggestion,false);
125         
126         // if this is the root element, declare user-specified namespace URIs.
127
if( isRoot ) {
128             // work defensively. we are calling an user-defined method.
129
String JavaDoc[] uris = nsContext.getNamespacePrefixMapper().getPreDeclaredNamespaceUris();
130             if( uris!=null ) {
131                 for( int i=0; i<uris.length; i++ ) {
132                     if( uris[i]!=null )
133                         nsContext.declareNamespace(uris[i],null,false);
134                 }
135             }
136         }
137     }
138     
139     
140     private final PrefixCallback startPrefixCallback = new PrefixCallback() {
141         public void onPrefixMapping( String JavaDoc prefix, String JavaDoc nsUri ) throws SAXException JavaDoc {
142             writer.startPrefixMapping(prefix,nsUri);
143         }
144     };
145     private final PrefixCallback endPrefixCallback = new PrefixCallback() {
146         public void onPrefixMapping( String JavaDoc prefix, String JavaDoc nsUri ) throws SAXException JavaDoc {
147             writer.endPrefixMapping(prefix);
148         }
149     };
150     
151     public void endNamespaceDecls() throws SAXException JavaDoc {
152         nsContext.endNamespaceDecls();
153     }
154     
155     /**
156      * Switches to the "marshal child texts/elements" mode.
157      * This method has to be called after the 1st pass is completed.
158      */

159     public void endAttributes() throws SAXException JavaDoc {
160         // calculate QName of the element
161
String JavaDoc uri = getCurrentElementUri();
162         String JavaDoc local = getCurrentElementLocal();
163         
164         String JavaDoc prefix = nsContext.getPrefix(uri);
165         _assert(prefix!=null); // since we've declared it, it should be available
166

167         String JavaDoc qname;
168         if(prefix.length()!=0 )
169             qname = prefix+':'+local;
170         else
171             qname = local;
172
173         // fire startPrefixMapping events
174
nsContext.iterateDeclaredPrefixes(startPrefixCallback);
175         
176         // fire the startElement event
177
writer.startElement( uri, local, qname, attributes );
178         
179         
180         // reset attributes
181
attributes.clear();
182         
183         // prepare to collect texts
184
textBuf.setLength(0);
185     }
186     
187     /**
188      * Ends marshalling of an element.
189      * Pops the internal stack.
190      */

191     public void endElement() throws SAXException JavaDoc {
192         writePendingText();
193         
194         String JavaDoc uri = getCurrentElementUri();
195         String JavaDoc local = getCurrentElementLocal();
196         
197         String JavaDoc prefix = nsContext.getPrefix(uri);
198         _assert(prefix!=null); // we've declared it earlier.
199

200         String JavaDoc qname;
201         if(prefix.length()!=0)
202             qname = prefix+':'+local;
203         else
204             qname = local;
205         
206         writer.endElement( uri, local, qname );
207
208         // pop namespace bindings and
209
// fire endPrefixMapping events
210
nsContext.iterateDeclaredPrefixes(endPrefixCallback);
211         
212         popElement();
213         
214         // prepare to collect texts
215
textBuf.setLength(0);
216         
217         nsContext.endElement();
218     }
219     
220     
221     /** Buffer for collecting characters. */
222     private final StringBuffer JavaDoc textBuf = new StringBuffer JavaDoc();
223     
224     /**
225      * Marshalls text.
226      *
227      * <p>
228      * This method can be called (i) after the startAttribute method
229      * and (ii) before the endAttribute method, to marshal attribute values.
230      * If the method is called more than once, those texts are considered
231      * as separated by whitespaces. For example,
232      *
233      * <pre>
234      * c.startAttribute();
235      * c.text("abc");
236      * c.text("def");
237      * c.endAttribute("","foo");
238      * </pre>
239      *
240      * will generate foo="abc def".
241      *
242      * <p>
243      * Similarly, this method can be called after the endAttributes
244      * method to marshal texts inside elements. The same rule about
245      * multiple invokations apply to this case, too. For example,
246      *
247      * <pre>
248      * c.startElement("","foo");
249      * c.endAttributes();
250      * c.text("abc");
251      * c.text("def");
252      * c.startElement("","bar");
253      * c.endAttributes();
254      * c.endElement();
255      * c.text("ghi");
256      * c.endElement();
257      * </pre>
258      *
259      * will generate <code>&lt;foo>abc def&lt;bar/>ghi&lt;/foo></code>.
260      */

261     public void text( String JavaDoc text, String JavaDoc fieldName ) throws SAXException JavaDoc {
262         // If the assertion fails, it must be a bug of xjc.
263
// right now, we are not expecting the text method to be called.
264
if(text==null) {
265             reportError(Util.createMissingObjectError(currentTarget,fieldName));
266             return;
267         }
268     
269         if(textBuf.length()!=0)
270             textBuf.append(' ');
271         textBuf.append(text);
272     }
273     
274     /**
275      * Writes pending text (characters inside elements) to the writer.
276      * This method is called from startElement and endElement.
277      */

278     private void writePendingText() throws SAXException JavaDoc {
279         // assert(textBuf!=null);
280
int len = textBuf.length();
281         
282         if(len!=0)
283             writer.characters( textBuf.toString().toCharArray(), 0, len );
284     }
285     
286     /**
287      * Starts marshalling of an attribute.
288      *
289      * The marshalling of an attribute will be done by
290      * <ol>
291      * <li>call the startAttribute method
292      * <li>call the text method (several times if necessary)
293      * <li>call the endAttribute method
294      * </ol>
295      *
296      * No two attributes can be marshalled at the same time.
297      * Note that the whole attribute marshalling must be happened
298      * after the startElement method and before the endAttributes method.
299      */

300     public void startAttribute( String JavaDoc uri, String JavaDoc local ) {
301         // initialize the buffer to collect attribute value
302
textBuf.setLength(0);
303         
304         // remember the attribute name. We'll use this value later.
305
this.attNamespaceUri = uri;
306         this.attLocalName = local;
307     }
308     
309     // used to keep attribute names until the endAttribute method is called.
310
private String JavaDoc attNamespaceUri;
311     private String JavaDoc attLocalName;
312
313     public void endAttribute() {
314         // use CDATA as the attribute type. This preserves
315
// successive processors to collapse whitespaces.
316
// (we cannot prevent characters like #xD to be replaced to
317
// #x20, though).
318
//
319
// strictly speaking, attribute value normalization should be
320
// provessed by XML parser, so it's unclear whether XML writer
321
// uses this type value.
322
//
323
// in any way, CDATA type is the safest choice here.
324

325         String JavaDoc qname;
326         if(attNamespaceUri.length()==0) {
327             // default namespace. don't need prefix
328
qname = attLocalName;
329         } else {
330             qname = nsContext.declareNamespace(attNamespaceUri,null,true)+':'+attLocalName;
331         }
332
333         attributes.addAttribute(attNamespaceUri,attLocalName,qname,"CDATA",textBuf.toString());
334     }
335     
336     public String JavaDoc onID( IdentifiableObject owner, String JavaDoc value ) throws SAXException JavaDoc {
337         objectsWithId.add(owner);
338         return value;
339     }
340     public String JavaDoc onIDREF( IdentifiableObject obj ) throws SAXException JavaDoc {
341         idReferencedObjects.add(obj);
342         String JavaDoc id = obj.____jaxb____getId();
343         if(id==null) {
344             reportError( new NotIdentifiableEventImpl(
345                 ValidationEvent.ERROR,
346                 Messages.format(Messages.ERR_NOT_IDENTIFIABLE),
347                 new ValidationEventLocatorImpl(obj) ) );
348         }
349         return id;
350     }
351     
352     void reconcileID() throws AbortSerializationException {
353         // find objects that were not a part of the object graph
354
idReferencedObjects.removeAll(objectsWithId);
355         
356         for( Iterator JavaDoc itr=idReferencedObjects.iterator(); itr.hasNext(); ) {
357             IdentifiableObject o = (IdentifiableObject)itr.next();
358             reportError( new NotIdentifiableEventImpl(
359                 ValidationEvent.ERROR,
360                 Messages.format(Messages.ERR_DANGLING_IDREF,o.____jaxb____getId()),
361                 new ValidationEventLocatorImpl(o) ) );
362         }
363         
364         // clear the garbage
365
idReferencedObjects.clear();
366         objectsWithId.clear();
367     }
368
369
370
371     public void childAsBody( JAXBObject o, String JavaDoc fieldName ) throws SAXException JavaDoc {
372         if(o==null) {
373             // if null is passed, it usually means that the content tree object
374
// doesn't have some of its required property.
375
reportMissingObjectError(fieldName);
376             // as a marshaller, we should be generous, so we'll continue to marshal
377
// this document by skipping this missing object.
378
return;
379         }
380         
381         JAXBObject oldTarget = currentTarget;
382         currentTarget = o;
383         
384         owner.context.getGrammarInfo().castToXMLSerializable(o).serializeBody(this);
385         
386         currentTarget = oldTarget;
387     }
388     
389     public void childAsAttributes( JAXBObject o, String JavaDoc fieldName ) throws SAXException JavaDoc {
390         if(o==null) {
391             reportMissingObjectError(fieldName);
392             return;
393         }
394
395         JAXBObject oldTarget = currentTarget;
396         currentTarget = o;
397         
398         owner.context.getGrammarInfo().castToXMLSerializable(o).serializeAttributes(this);
399         
400         currentTarget = oldTarget;
401     }
402     
403     public void childAsURIs( JAXBObject o, String JavaDoc fieldName ) throws SAXException JavaDoc {
404         if(o==null) {
405             reportMissingObjectError(fieldName);
406             return;
407         }
408         
409         JAXBObject oldTarget = currentTarget;
410         currentTarget = o;
411         
412         owner.context.getGrammarInfo().castToXMLSerializable(o).serializeURIs(this);
413         
414         currentTarget = oldTarget;
415     }
416     
417
418     public void reportError( ValidationEvent ve ) throws AbortSerializationException {
419         ValidationEventHandler handler;
420         
421         try {
422             handler = owner.getEventHandler();
423         } catch( JAXBException e ) {
424             throw new AbortSerializationException(e);
425         }
426         
427         if(!handler.handleEvent(ve)) {
428             if(ve.getLinkedException() instanceof Exception JavaDoc)
429                 throw new AbortSerializationException((Exception JavaDoc)ve.getLinkedException());
430             else
431                 throw new AbortSerializationException(ve.getMessage());
432         }
433     }
434     
435     
436     public void reportMissingObjectError(String JavaDoc fieldName) throws SAXException JavaDoc {
437         reportError(Util.createMissingObjectError(currentTarget,fieldName));
438     }
439     
440     
441     private static void _assert( boolean b ) {
442         if(!b)
443             throw new JAXBAssertionError();
444     }
445     
446     /**
447      * Default {@link NamespacePrefixMapper} implementation used when
448      * it is not specified by the user.
449      */

450     private static NamespacePrefixMapper defaultNamespacePrefixMapper = new NamespacePrefixMapper() {
451         public String JavaDoc getPreferredPrefix(String JavaDoc namespaceUri, String JavaDoc suggestion, boolean requirePrefix) {
452             if( namespaceUri.equals("http://www.w3.org/2001/XMLSchema-instance") )
453                 return "xsi";
454             return suggestion;
455         }
456     };
457 }
458
Popular Tags