KickJava   Java API By Example, From Geeks To Geeks.

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


1 package com.sun.tools.xjc.runtime;
2
3 import javax.xml.bind.ValidationEvent;
4
5 import org.relaxng.datatype.Datatype;
6 import org.xml.sax.SAXException JavaDoc;
7 import org.xml.sax.helpers.AttributesImpl JavaDoc;
8
9 import com.sun.msv.grammar.IDContextProvider2;
10 import com.sun.msv.util.LightStack;
11 import com.sun.msv.util.StartTagInfo;
12 import com.sun.msv.util.StringRef;
13 import com.sun.msv.verifier.Acceptor;
14 import com.sun.msv.verifier.regexp.StringToken;
15 import com.sun.xml.bind.JAXBAssertionError;
16 import com.sun.xml.bind.JAXBObject;
17 import com.sun.xml.bind.RIElement;
18 import com.sun.xml.bind.marshaller.IdentifiableObject;
19 import com.sun.xml.bind.serializer.AbortSerializationException;
20 import com.sun.xml.bind.serializer.Util;
21 import com.sun.xml.bind.validator.Messages;
22
23 /**
24  * XMLSerializer that calls the native interface of MSV and performs validation.
25  * Used in a pair with a ValidationContext.
26  *
27  * @author Kohsuke Kawaguchi
28  */

29 public class MSVValidator implements XMLSerializer, IDContextProvider2
30 {
31     /** Current acceptor in use. */
32     private Acceptor acceptor;
33     
34     /** Context object that coordinates the entire validation effort. */
35     private final ValidationContext context;
36     
37     /** The object which we are validating. */
38     private final ValidatableObject target;
39     
40     final DefaultJAXBContextImpl jaxbContext;
41     
42     /**
43      * Acceptor stack. Whenever an element is found, the current acceptor is
44      * pushed to the stack and new one is created.
45      *
46      * LightStack is a light-weight stack implementation
47      */

48     private final LightStack stack = new LightStack();
49
50     public NamespaceContext2 getNamespaceContext() {
51         return context.getNamespaceContext();
52     }
53     
54     /**
55      * To use this class, call the static validate method.
56      */

57     private MSVValidator( DefaultJAXBContextImpl _jaxbCtx, ValidationContext _ctxt, ValidatableObject vo ) {
58         jaxbContext = _jaxbCtx;
59         acceptor = vo.createRawValidator().createAcceptor();
60         context = _ctxt;
61         target = vo;
62     }
63     
64     /**
65      * Validates the specified object and reports any error to the context.
66      */

67     public static void validate( DefaultJAXBContextImpl jaxbCtx, ValidationContext context, ValidatableObject vo )
68             throws SAXException JavaDoc {
69         try {
70             new MSVValidator(jaxbCtx,context,vo)._validate();
71         } catch( RuntimeException JavaDoc e ) {
72             // sometimes when a conversion between Java object and
73
// lexical value fails, it may throw an exception like
74
// NullPointerException or NumberFormatException.
75
//
76
// catch them and report them as an error.
77
context.reportEvent(vo,e);
78         }
79     }
80     
81     /** performs the validation to the object specified in the constructor. */
82     private void _validate() throws SAXException JavaDoc {
83         context.getNamespaceContext().startElement();
84         
85         // validate attributes
86
target.serializeURIs(this);
87         
88         endNamespaceDecls();
89         
90         target.serializeAttributes(this);
91         
92         endAttributes();
93         
94         // validate content model
95
target.serializeBody(this);
96         writePendingText();
97         
98         context.getNamespaceContext().endElement();
99         
100         if(!acceptor.isAcceptState(null)) {
101             // some elements are missing
102
// report error
103
StringRef ref = new StringRef();
104             acceptor.isAcceptState(ref);
105             context.reportEvent(target,ref.str);
106         }
107     }
108     
109     public void endNamespaceDecls() throws SAXException JavaDoc {
110         context.getNamespaceContext().endNamespaceDecls();
111     }
112     
113     public void endAttributes() throws SAXException JavaDoc {
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 JavaDoc buf = new StringBuffer JavaDoc();
130         
131     public final void text( String JavaDoc text, String JavaDoc fieldName ) throws SAXException JavaDoc {
132         if(text==null) {
133             reportMissingObjectError(fieldName);
134             return;
135         }
136         
137         if(buf.length()!=0)
138             buf.append(' ');
139         buf.append(text);
140     }
141     
142     public void reportMissingObjectError(String JavaDoc fieldName) throws SAXException JavaDoc {
143         reportError(Util.createMissingObjectError(target,fieldName));
144     }
145     
146
147     // used to keep attribute names until the endAttribute method is called.
148
private String JavaDoc attNamespaceUri;
149     private String JavaDoc attLocalName;
150     private boolean insideAttribute;
151
152     public void startAttribute( String JavaDoc uri, String JavaDoc local ) {
153         // we will do the processing at the end element
154
this.attNamespaceUri = uri;
155         this.attLocalName = local;
156         insideAttribute = true;
157     }
158     
159     public void endAttribute() throws SAXException JavaDoc {
160         insideAttribute = false;
161         if(!acceptor.onAttribute2( attNamespaceUri, attLocalName,
162             attLocalName /* we don't have QName, so just use the local name */,
163             buf.toString(),
164             this, null, null )) {
165             
166             // either the name was incorrect (which is quite unlikely),
167
// or the value was wrong.
168
// report an error
169
StringRef ref = new StringRef();
170             acceptor.onAttribute2( attNamespaceUri, attLocalName, attLocalName,
171             buf.toString(), this, ref, null );
172             
173             context.reportEvent(target,ref.str);
174         }
175         
176         buf = new StringBuffer JavaDoc();
177     }
178     
179     private void writePendingText() throws SAXException JavaDoc {
180         // assert(textBuf!=null);
181
if(!acceptor.onText2( buf.toString(), this, null, null )) {
182             // this text is invalid.
183
// report an error
184
StringRef ref = new StringRef();
185             acceptor.onText2( buf.toString(), this, ref, null );
186             context.reportEvent(target,ref.str);
187         }
188         
189         if(buf.length()>1024)
190             buf = new StringBuffer JavaDoc();
191         else
192             buf.setLength(0);
193     }
194     
195     private String JavaDoc currentElementUri;
196     private String JavaDoc currentElementLocalName;
197     
198     public void startElement( String JavaDoc uri, String JavaDoc local ) throws SAXException JavaDoc {
199         writePendingText();
200         
201         context.getNamespaceContext().startElement();
202         
203         stack.push(acceptor);
204         
205         StartTagInfo sti = new StartTagInfo(uri,local,local,emptyAttributes,this);
206         
207         // we pass in an empty attributes, as there is just no way for us to
208
// properly re-construct attributes. Fortunately, I know MSV is not using
209
// attribute values, so this would work, but nevertheless this code is
210
// ugly. This is one of the problems of the "middle" approach.
211
Acceptor child = acceptor.createChildAcceptor( sti, null );
212         if( child==null ) {
213             // this element is invalid. probably, so this object is invalid
214
// report an error
215
StringRef ref = new StringRef();
216             child = acceptor.createChildAcceptor( sti, ref );
217             context.reportEvent(target,ref.str);
218         }
219         
220         this.currentElementUri = uri;
221         this.currentElementLocalName = local;
222         
223         acceptor = child;
224     }
225     
226     public void endElement() throws SAXException JavaDoc {
227         writePendingText();
228         
229         if(!acceptor.isAcceptState(null)) {
230             // some required elements are missing
231
// report error
232
StringRef ref = new StringRef();
233             acceptor.isAcceptState(ref);
234             context.reportEvent(target,ref.str);
235         }
236         
237         // pop the acceptor
238
Acceptor child = acceptor;
239         acceptor = (Acceptor)stack.pop();
240         if(!acceptor.stepForward( child, null )) {
241             // some required elements are missing.
242
// report an error
243
StringRef ref = new StringRef();
244             acceptor.stepForward( child, ref ); // force recovery and obtain an error message.
245

246             context.reportEvent(target,ref.str);
247         }
248         
249         context.getNamespaceContext().endElement();
250     }
251     
252     
253     public void childAsAttributes( JAXBObject o, String JavaDoc fieldName ) throws SAXException JavaDoc {
254         // do nothing
255

256         // either the onMarshallableObjectInElement method
257
// or the onMarshallableObjectInAttributeBody method will be
258
// called for every content tree objects.
259
//
260
// so we don't need to validate an object within this method.
261
}
262
263     public void childAsURIs( JAXBObject o, String JavaDoc fieldName ) throws SAXException JavaDoc {
264         // ditto.
265
}
266
267     
268     /** An empty <code>Attributes</code> object. */
269     private static final AttributesImpl JavaDoc emptyAttributes = new AttributesImpl JavaDoc();
270     
271     /** namespace URI of dummy elements. TODO: allocate one namespace URI for this. */
272     public static final String JavaDoc DUMMY_ELEMENT_NS =
273         "http://java.sun.com/jaxb/xjc/dummy-elements";
274
275     public void childAsBody( JAXBObject o, String JavaDoc fieldName ) throws SAXException JavaDoc {
276         //final ValidatableObject vo = Util.toValidatableObject(o);
277
final ValidatableObject vo = jaxbContext.getGrammarInfo().castToValidatableObject(o);
278
279         if(vo==null) {
280             reportMissingObjectError(fieldName);
281             return;
282         }
283         
284         if( insideAttribute ) childAsAttributeBody(vo,fieldName);
285         else childAsElementBody(o,vo);
286     }
287     
288     private void childAsElementBody( Object JavaDoc o, ValidatableObject vo ) throws SAXException JavaDoc {
289         String JavaDoc intfName = vo.getPrimaryInterface().getName();
290         intfName = intfName.replace('$','.');
291         
292         // if the object implements the RIElement interface,
293
// add a marker attribute to the dummy element.
294
//
295
// For example, if the object is org.acme.impl.FooImpl,
296
// the dummy element will look like
297
// <{DUMMY_ELEMENT_NS}org.acme.Foo
298
// {<URI of this element>}:<local name of this element>="" />
299
//
300
// This extra attribute is used to validate wildcards.
301
// AttributesImpl atts;
302
// if(o instanceof RIElement) {
303
// RIElement rie = (RIElement)o;
304
// atts = new AttributesImpl();
305
// atts.addAttribute(
306
// rie.____jaxb_ri____getNamespaceURI(),
307
// rie.____jaxb_ri____getLocalName(),
308
// rie.____jaxb_ri____getLocalName(), // use local name as qname
309
// "CDATA",
310
// ""); // we don't care about the attribute value
311
// } else
312
// atts = emptyAttributes;
313

314         
315         // feed a dummy element to the acceptor.
316
StartTagInfo sti = new StartTagInfo(
317             DUMMY_ELEMENT_NS,
318             intfName,
319             intfName/*just pass the local name as QName.*/,
320             emptyAttributes,
321             this );
322         
323             
324         Acceptor child = acceptor.createChildAcceptor(sti,null);
325         if(child==null) {
326             // some required elements were missing. report errors
327
StringRef ref = new StringRef();
328             child = acceptor.createChildAcceptor(sti,ref);
329             context.reportEvent(target,ref.str);
330         }
331         
332         if(o instanceof RIElement) {
333             RIElement rie = (RIElement)o;
334             if(!child.onAttribute2(
335                 rie.____jaxb_ri____getNamespaceURI(),
336                 rie.____jaxb_ri____getLocalName(),
337                 rie.____jaxb_ri____getLocalName(),
338                 "",
339                 null, null, null ))
340                 
341                 // this object is not a valid member of the wildcard
342
context.reportEvent(target,
343                     Messages.format( Messages.INCORRECT_CHILD_FOR_WILDCARD,
344                         rie.____jaxb_ri____getNamespaceURI(),
345                         rie.____jaxb_ri____getLocalName() ));
346         }
347         
348         child.onEndAttributes(sti,null);
349         
350         
351         if(!acceptor.stepForward(child,null)) {
352             // this can't be possible, as the dummy element was
353
// generated by XJC.
354
throw new JAXBAssertionError();
355         }
356
357         
358         // we need a separate validator instance to validate a child object
359
context.validate(vo);
360         
361     }
362     
363     private void childAsAttributeBody( ValidatableObject vo, String JavaDoc fieldName ) throws SAXException JavaDoc {
364         /*
365         Dirty quick hack. When we split a schema into fragments, basically
366         every chlid object needs a place holder in the fragment
367         (so that the parent schema fragment can correctly validate that the
368         child objects are at their supposed places.)
369         
370         For example, cconsider the following schema:
371         
372         imagine:
373         <class>
374           <attribute>
375             <list>
376               <oneOrMore>
377                 <ref name="bar"/>
378               </oneOrMore>
379             </list>
380           </attribute>
381         </class>
382         
383         In our algorithm, the corresponding schema fragment will be:
384         
385         <class>
386           <attribute>
387             <list>
388               <oneOrMore>
389                 <value>full.class.name.of.BarImpl</value>
390               </oneOrMore>
391             </list>
392           </attribute>
393         </class>
394         
395         If we find a child object inside an attribute
396         (that's why we are in this method BTW),
397         we generate a class name (with a special marker ).
398         */

399         
400         // put a class name with a special marker \u0000. This char is an invalid
401
// XML char, so sensible datatypes should reject this (although many
402
// datatype implementations will accept it in actuality)
403
text("\u0000"+vo.getPrimaryInterface().getName(),fieldName);
404
405         // validate a child object
406
context.validate(vo);
407     }
408
409
410     public void reportError( ValidationEvent e ) throws AbortSerializationException {
411         context.reportEvent(target,e);
412     }
413
414 //
415
//
416
// ID/IDREF validation
417
//
418
//
419
public String JavaDoc onID( IdentifiableObject owner, String JavaDoc value ) throws SAXException JavaDoc {
420         return context.onID(target,value);
421     }
422     public String JavaDoc onIDREF( IdentifiableObject value ) throws SAXException JavaDoc {
423         return context.onIDREF(target,value.____jaxb____getId());
424     }
425
426 //
427
//
428
// ValidationContext implementation. Used by MSV to obtain
429
// contextual information related to validation.
430
//
431
//
432
public String JavaDoc getBaseUri() { return null; }
433     public boolean isUnparsedEntity( String JavaDoc entityName ) {
434         // abandon the validation of ENTITY type.
435
return true;
436     }
437     public boolean isNotation( String JavaDoc notation ) {
438         // abandon the validation of NOTATION type.
439
return true;
440     }
441     public void onID( Datatype dt, StringToken s ) {
442         // ID/IDREF validation will be done by ourselves.
443
// so we will not rely on the validator to perform this check.
444
// because we will use multiple instances of validators, so
445
// they cannot check global consistency.
446

447         // see onID/onIDREF of the ValidationContext.
448
}
449     public String JavaDoc resolveNamespacePrefix( String JavaDoc prefix ) {
450         return context.getNamespaceContext().getNamespaceURI(prefix);
451     }
452     
453 }
454
Popular Tags