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 javax.xml.bind.ValidationEvent;
015 import javax.xml.bind.helpers.ValidationEventImpl;
016 import javax.xml.bind.helpers.ValidationEventLocatorImpl;
017
018 import org.relaxng.datatype.Datatype;
019 import org.xml.sax.SAXException;
020 import org.xml.sax.helpers.AttributesImpl;
021
022 import com.sun.msv.grammar.IDContextProvider;
023 import com.sun.msv.util.LightStack;
024 import com.sun.msv.util.StartTagInfo;
025 import com.sun.msv.util.StringRef;
026 import com.sun.msv.verifier.Acceptor;
027 import com.sun.xml.bind.JAXBObject;
028 import com.sun.xml.bind.RIElement;
029 import com.sun.xml.bind.marshaller.IdentifiableObject;
030 import com.sun.xml.bind.serializer.AbortSerializationException;
031 import com.sun.xml.bind.validator.Messages;
032
033 /**
034 * XMLSerializer that calls the native interface of MSV and performs validation.
035 * Used in a pair with a ValidationContext.
036 *
037 * @author Kohsuke Kawaguchi
038 */
039 public class MSVValidator implements XMLSerializer, IDContextProvider
040 {
041 /** Current acceptor in use. */
042 private Acceptor acceptor;
043
044 /** Context object that coordinates the entire validation effort. */
045 private final ValidationContext context;
046
047 /** The object which we are validating. */
048 private final ValidatableObject target;
049
050 /**
051 * Acceptor stack. Whenever an element is found, the current acceptor is
052 * pushed to the stack and new one is created.
053 *
054 * LightStack is a light-weight stack implementation
055 */
056 private final LightStack stack = new LightStack();
057
058 public NamespaceContext2 getNamespaceContext() {
059 return context.getNamespaceContext();
060 }
061
062 /**
063 * To use this class, call the static validate method.
064 */
065 private MSVValidator( ValidationContext _ctxt, ValidatableObject vo ) {
066 acceptor = vo.createRawValidator().createAcceptor();
067 context = _ctxt;
068 target = vo;
069 }
070
071 /**
072 * Validates the specified object and reports any error to the context.
073 */
074 public static void validate( ValidationContext context, ValidatableObject vo )
075 throws SAXException {
076 try {
077 new MSVValidator(context,vo)._validate();
078 } catch( RuntimeException e ) {
079 // sometimes when a conversion between Java object and
080 // lexical value fails, it may throw an exception like
081 // NullPointerException or NumberFormatException.
082 //
083 // catch them and report them as an error.
084 context.reportEvent(vo,e);
085 }
086 }
087
088 /** performs the validation to the object specified in the constructor. */
089 private void _validate() throws SAXException {
090 // validate attributes
091 target.serializeAttributes(this);
092
093 endAttribute();
094
095 // validate content model
096 target.serializeElementBody(this);
097 writePendingText();
098
099 if(!acceptor.isAcceptState(null)) {
100 // some elements are missing
101 // report error
102 StringRef ref = new StringRef();
103 acceptor.isAcceptState(ref);
104 context.reportEvent(target,ref.str);
105 }
106 }
107
108 public void endNamespaceDecls() throws SAXException {
109 context.getNamespaceContext().endNamespaceDecls();
110 }
111
112 public void endAttributes() throws SAXException {
113 // TODO: supply StartTagInfo by remembering all reported attributes
114 if(!acceptor.onEndAttributes( null, null )) {
115 // some required attributes are missing.
116 // report a validation error
117 // Note that we don't know which property of this object
118 // causes this error.
119 StringRef ref = new StringRef();
120 StartTagInfo sti = new StartTagInfo(
121 currentElementUri,currentElementLocalName,currentElementLocalName,
122 emptyAttributes,this);
123 acceptor.onEndAttributes( sti, ref );
124 context.reportEvent(target,ref.str);
125 }
126 }
127
128 /** stores text reported by the text method. */
129 private StringBuffer buf = new StringBuffer();
130
131 public final void text( String text ) throws SAXException {
132 if(text==null) {
133 reportMissingObjectError();
134 return;
135 }
136
137 if(buf.length()!=0)
138 buf.append(' ');
139 buf.append(text);
140 }
141
142 private void reportMissingObjectError() throws SAXException {
143 ValidationEvent ev = new ValidationEventImpl(
144 ValidationEvent.ERROR,
145 Messages.format(Messages.MISSING_OBJECT),
146 new ValidationEventLocatorImpl(target),
147 new NullPointerException() );
148
149 reportError(ev);
150 }
151
152
153 // used to keep attribute names until the endAttribute method is called.
154 private String attNamespaceUri;
155 private String attLocalName;
156
157 public void startAttribute( String uri, String local ) {
158 // we will do the processing at the end element
159 this.attNamespaceUri = uri;
160 this.attLocalName = local;
161 }
162
163 public void endAttribute() throws SAXException {
164 if(!acceptor.onAttribute( attNamespaceUri, attLocalName,
165 attLocalName /* we don't have QName, so just use the local name */,
166 buf.toString(),
167 this, null, null )) {
168
169 // either the name was incorrect (which is quite unlikely),
170 // or the value was wrong.
171 // report an error
172 StringRef ref = new StringRef();
173 acceptor.onAttribute( attNamespaceUri, attLocalName, attLocalName,
174 buf.toString(), this, ref, null );
175
176 context.reportEvent(target,ref.str);
177 }
178
179 buf = new StringBuffer();
180 }
181
182 private void writePendingText() throws SAXException {
183 // assert(textBuf!=null);
184 if(!acceptor.onText( buf.toString(), this, null, null )) {
185 // this text is invalid.
186 // report an error
187 StringRef ref = new StringRef();
188 acceptor.onText( buf.toString(), this, ref, null );
189 context.reportEvent(target,ref.str);
190 }
191
192 if(buf.length()>1024)
193 buf = new StringBuffer();
194 else
195 buf.setLength(0);
196 }
197
198 private String currentElementUri;
199 private String currentElementLocalName;
200
201 public void startElement( String uri, String local ) throws SAXException {
202 writePendingText();
203
204 context.getNamespaceContext().startElement();
205
206 stack.push(acceptor);
207
208 StartTagInfo sti = new StartTagInfo(uri,local,local,emptyAttributes,this);
209
210 // we pass in an empty attributes, as there is just no way for us to
211 // properly re-construct attributes. Fortunately, I know MSV is not using
212 // attribute values, so this would work, but nevertheless this code is
213 // ugly. This is one of the problems of the "middle" approach.
214 Acceptor child = acceptor.createChildAcceptor( sti, null );
215 if( child==null ) {
216 // this element is invalid. probably, so this object is invalid
217 // report an error
218 StringRef ref = new StringRef();
219 child = acceptor.createChildAcceptor( sti, ref );
220 context.reportEvent(target,ref.str);
221 }
222
223 this.currentElementUri = uri;
224 this.currentElementLocalName = local;
225
226 acceptor = child;
227 }
228
229 public void endElement() throws SAXException {
230 writePendingText();
231
232 if(!acceptor.isAcceptState(null)) {
233 // some required elements are missing
234 // report error
235 StringRef ref = new StringRef();
236 acceptor.isAcceptState(ref);
237 context.reportEvent(target,ref.str);
238 }
239
240 // pop the acceptor
241 Acceptor child = acceptor;
242 acceptor = (Acceptor)stack.pop();
243 if(!acceptor.stepForward( child, null )) {
244 // some required elements are missing.
245 // report an error
246 StringRef ref = new StringRef();
247 acceptor.stepForward( child, ref ); // force recovery and obtain an error message.
248
249 context.reportEvent(target,ref.str);
250 }
251
252 context.getNamespaceContext().endElement();
253 }
254
255
256 public void childAsAttributes( JAXBObject o ) throws SAXException {
257 // do nothing
258
259 // either the onMarshallableObjectInElement method
260 // or the onMarshallableObjectInAttributeBody method will be
261 // called for every content tree objects.
262 //
263 // so we don't need to validate an object within this method.
264 }
265
266 public void childAsURIs( JAXBObject o ) throws SAXException {
267 // ditto.
268 }
269
270
271 /** An empty <code>Attributes</code> object. */
272 private static final AttributesImpl emptyAttributes = new AttributesImpl();
273
274 /** namespace URI of dummy elements. TODO: allocate one namespace URI for this. */
275 public static final String DUMMY_ELEMENT_NS =
276 "http://java.sun.com/jaxb/xjc/dummy-elements";
277
278 public void childAsElementBody( JAXBObject o ) throws SAXException {
279 final ValidatableObject vo = Util.toValidatableObject(o);
280
281 if(vo==null) {
282 reportMissingObjectError();
283 return;
284 }
285
286 String intfName = vo.getPrimaryInterface().getName();
287 intfName = intfName.replace('$','.');
288
289 // if the object implements the RIElement interface,
290 // add a marker attribute to the dummy element.
291 //
292 // For example, if the object is org.acme.impl.FooImpl,
293 // the dummy element will look like
294 // <{DUMMY_ELEMENT_NS}org.acme.Foo
295 // {<URI of this element>}:<local name of this element>="" />
296 //
297 // This extra attribute is used to validate wildcards.
298 // AttributesImpl atts;
299 // if(o instanceof RIElement) {
300 // RIElement rie = (RIElement)o;
301 // atts = new AttributesImpl();
302 // atts.addAttribute(
303 // rie.____jaxb_ri____getNamespaceURI(),
304 // rie.____jaxb_ri____getLocalName(),
305 // rie.____jaxb_ri____getLocalName(), // use local name as qname
306 // "CDATA",
307 // ""); // we don't care about the attribute value
308 // } else
309 // atts = emptyAttributes;
310
311
312 // feed a dummy element to the acceptor.
313 StartTagInfo sti = new StartTagInfo(
314 DUMMY_ELEMENT_NS,
315 intfName,
316 intfName/*just pass the local name as QName.*/,
317 emptyAttributes,
318 this );
319
320
321 Acceptor child = acceptor.createChildAcceptor(sti,null);
322 if(child==null) {
323 // some required elements were missing. report errors
324 StringRef ref = new StringRef();
325 child = acceptor.createChildAcceptor(sti,ref);
326 context.reportEvent(target,ref.str);
327 }
328
329 if(o instanceof RIElement) {
330 RIElement rie = (RIElement)o;
331 if(!child.onAttribute(
332 rie.____jaxb_ri____getNamespaceURI(),
333 rie.____jaxb_ri____getLocalName(),
334 rie.____jaxb_ri____getLocalName(),
335 "",
336 null, null, null ))
337
338 // this object is not a valid member of the wildcard
339 context.reportEvent(target,
340 Messages.format( Messages.INCORRECT_CHILD_FOR_WILDCARD,
341 rie.____jaxb_ri____getNamespaceURI(),
342 rie.____jaxb_ri____getLocalName() ));
343 }
344
345 child.onEndAttributes(sti,null);
346
347
348 if(!acceptor.stepForward(child,null)) {
349 // this can't be possible, as the dummy element was
350 // generated by XJC.
351 throw new InternalError();
352 }
353
354
355 // we need a separate validator instance to validate a child object
356 context.validate(vo);
357
358 }
359
360 public void childAsAttributeBody( JAXBObject o ) throws SAXException {
361 /*
362 Dirty quick hack. When we split a schema into fragments, basically
363 every chlid object needs a place holder in the fragment
364 (so that the parent schema fragment can correctly validate that the
365 child objects are at their supposed places.)
366
367 For example, cconsider the following schema:
368
369 imagine:
370 <class>
371 <attribute>
372 <list>
373 <oneOrMore>
374 <ref name="bar"/>
375 </oneOrMore>
376 </list>
377 </attribute>
378 </class>
379
380 In our algorithm, the corresponding schema fragment will be:
381
382 <class>
383 <attribute>
384 <list>
385 <oneOrMore>
386 <value>\u0000full.class.name.of.BarImpl</value>
387 </oneOrMore>
388 </list>
389 </attribute>
390 </class>
391
392 If we find a child object inside an attribute
393 (that's why we are in this method BTW),
394 we generate a class name (with a special marker \u0000).
395 */
396
397 final ValidatableObject vo = Util.toValidatableObject(o);
398
399 if(vo==null) {
400 reportMissingObjectError();
401 return;
402 }
403
404 // put a class name with a special marker \u0000. This char is an invalid
405 // XML char, so sensible datatypes should reject this (although many
406 // datatype implementations will accept it in actuality)
407 text("\u0000"+vo.getPrimaryInterface().getName());
408
409 // validate a child object
410 context.validate(vo);
411 }
412
413
414 public void reportError( ValidationEvent e ) throws AbortSerializationException {
415 context.reportEvent(target,e);
416 }
417
418 //
419 //
420 // ID/IDREF validation
421 //
422 //
423 public String onID( IdentifiableObject owner, String value ) throws SAXException {
424 return context.onID(target,value);
425 }
426 public String onIDREF( IdentifiableObject value ) throws SAXException {
427 return context.onIDREF(target,value.____jaxb____getId());
428 }
429
430 //
431 //
432 // ValidationContext implementation. Used by MSV to obtain
433 // contextual information related to validation.
434 //
435 //
436 public String getBaseUri() { return null; }
437 public boolean isUnparsedEntity( String entityName ) {
438 // abandon the validation of ENTITY type.
439 return true;
440 }
441 public boolean isNotation( String notation ) {
442 // abandon the validation of NOTATION type.
443 return true;
444 }
445 public void onID( Datatype dt, String s ) {
446 // ID/IDREF validation will be done by ourselves.
447 // so we will not rely on the validator to perform this check.
448 // because we will use multiple instances of validators, so
449 // they cannot check global consistency.
450
451 // see onID/onIDREF of the ValidationContext.
452 }
453 public String resolveNamespacePrefix( String prefix ) {
454 return context.getNamespaceContext().getNamespaceURI(prefix);
455 }
456
457 }