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><foo>abc def<bar/>ghi</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 }