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 }