View Javadoc

1   //
2   // This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v1.0.3-b18-fcs 
3   // See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
4   // Any modifications to this file will be lost upon recompilation of the source schema. 
5   // Generated on: 2004.12.16 at 07:09:42 EST 
6   //
7   
8   package net.sf.jour.config.impl.runtime;
9   
10  import javax.xml.bind.ValidationEvent;
11  
12  import org.relaxng.datatype.Datatype;
13  import org.xml.sax.SAXException;
14  import org.xml.sax.helpers.AttributesImpl;
15  
16  import com.sun.msv.grammar.IDContextProvider2;
17  import com.sun.msv.util.LightStack;
18  import com.sun.msv.util.StartTagInfo;
19  import com.sun.msv.util.StringRef;
20  import com.sun.msv.verifier.Acceptor;
21  import com.sun.msv.verifier.regexp.StringToken;
22  import com.sun.xml.bind.JAXBAssertionError;
23  import com.sun.xml.bind.JAXBObject;
24  import com.sun.xml.bind.RIElement;
25  import com.sun.xml.bind.marshaller.IdentifiableObject;
26  import com.sun.xml.bind.serializer.AbortSerializationException;
27  import com.sun.xml.bind.serializer.Util;
28  import com.sun.xml.bind.validator.Messages;
29  
30  /***
31   * XMLSerializer that calls the native interface of MSV and performs validation.
32   * Used in a pair with a ValidationContext.
33   * 
34   * @author  Kohsuke Kawaguchi
35   */
36  public class MSVValidator implements XMLSerializer, IDContextProvider2
37  {
38      /*** Current acceptor in use. */
39      private Acceptor acceptor;
40      
41      /*** Context object that coordinates the entire validation effort. */
42      private final ValidationContext context;
43      
44      /*** The object which we are validating. */
45      private final ValidatableObject target;
46      
47      final DefaultJAXBContextImpl jaxbContext;
48      
49      /***
50       * Acceptor stack. Whenever an element is found, the current acceptor is
51       * pushed to the stack and new one is created.
52       * 
53       * LightStack is a light-weight stack implementation
54       */
55      private final LightStack stack = new LightStack();
56  
57      public NamespaceContext2 getNamespaceContext() {
58          return context.getNamespaceContext();
59      }
60      
61      /***
62       * To use this class, call the static validate method.
63       */
64      private MSVValidator( DefaultJAXBContextImpl _jaxbCtx, ValidationContext _ctxt, ValidatableObject vo ) {
65          jaxbContext = _jaxbCtx;
66          acceptor = vo.createRawValidator().createAcceptor();
67          context = _ctxt;
68          target = vo;
69      }
70      
71      /***
72       * Validates the specified object and reports any error to the context.
73       */
74      public static void validate( DefaultJAXBContextImpl jaxbCtx, ValidationContext context, ValidatableObject vo )
75              throws SAXException {
76          try {
77              new MSVValidator(jaxbCtx,context,vo)._validate();
78          } catch( RuntimeException e ) {
79              // sometimes when a conversion between Java object and
80              // lexical value fails, it may throw an exception like
81              // NullPointerException or NumberFormatException.
82              //
83              // catch them and report them as an error.
84              context.reportEvent(vo,e);
85          }
86      }
87      
88      /*** performs the validation to the object specified in the constructor. */
89      private void _validate() throws SAXException {
90          context.getNamespaceContext().startElement();
91          
92          // validate attributes
93          target.serializeURIs(this);
94          
95          endNamespaceDecls();
96          
97          target.serializeAttributes(this);
98          
99          endAttributes();
100         
101         // validate content model
102         target.serializeBody(this);
103         writePendingText();
104         
105         context.getNamespaceContext().endElement();
106         
107         if(!acceptor.isAcceptState(null)) {
108             // some elements are missing
109             // report error
110             StringRef ref = new StringRef();
111             acceptor.isAcceptState(ref);
112             context.reportEvent(target,ref.str);
113         }
114     }
115     
116     public void endNamespaceDecls() throws SAXException {
117         context.getNamespaceContext().endNamespaceDecls();
118     }
119     
120     public void endAttributes() throws SAXException {
121         if(!acceptor.onEndAttributes( null, null )) {
122             // some required attributes are missing.
123             // report a validation error
124             // Note that we don't know which property of this object
125             // causes this error.
126             StringRef ref = new StringRef();
127             StartTagInfo sti = new StartTagInfo(
128                 currentElementUri,currentElementLocalName,currentElementLocalName,
129                 emptyAttributes,this);
130             acceptor.onEndAttributes( sti, ref );
131             context.reportEvent(target,ref.str);
132         }
133     }
134     
135     /*** stores text reported by the text method. */
136     private StringBuffer buf = new StringBuffer();
137         
138     public final void text( String text, String fieldName ) throws SAXException {
139         if(text==null) {
140             reportMissingObjectError(fieldName);
141             return;
142         }
143         
144         if(buf.length()!=0)
145             buf.append(' ');
146         buf.append(text);
147     }
148     
149     public void reportMissingObjectError(String fieldName) throws SAXException {
150         reportError(Util.createMissingObjectError(target,fieldName));
151     }
152     
153 
154     // used to keep attribute names until the endAttribute method is called.
155     private String attNamespaceUri;
156     private String attLocalName;
157     private boolean insideAttribute;
158 
159     public void startAttribute( String uri, String local ) {
160         // we will do the processing at the end element
161         this.attNamespaceUri = uri;
162         this.attLocalName = local;
163         insideAttribute = true;
164     }
165     
166     public void endAttribute() throws SAXException {
167         insideAttribute = false;
168         if(!acceptor.onAttribute2( attNamespaceUri, attLocalName,
169             attLocalName /* we don't have QName, so just use the local name */,
170             buf.toString(),
171             this, null, null )) {
172             
173             // either the name was incorrect (which is quite unlikely),
174             // or the value was wrong.
175             // report an error
176             StringRef ref = new StringRef();
177             acceptor.onAttribute2( attNamespaceUri, attLocalName, attLocalName,
178             buf.toString(), this, ref, null );
179             
180             context.reportEvent(target,ref.str);
181         }
182         
183         buf = new StringBuffer();
184     }
185     
186     private void writePendingText() throws SAXException {
187         // assert(textBuf!=null);
188         if(!acceptor.onText2( buf.toString(), this, null, null )) {
189             // this text is invalid.
190             // report an error
191             StringRef ref = new StringRef();
192             acceptor.onText2( buf.toString(), this, ref, null );
193             context.reportEvent(target,ref.str);
194         }
195         
196         if(buf.length()>1024)
197             buf = new StringBuffer();
198         else
199             buf.setLength(0);
200     }
201     
202     private String currentElementUri;
203     private String currentElementLocalName;
204     
205     public void startElement( String uri, String local ) throws SAXException {
206         writePendingText();
207         
208         context.getNamespaceContext().startElement();
209         
210         stack.push(acceptor);
211         
212         StartTagInfo sti = new StartTagInfo(uri,local,local,emptyAttributes,this);
213         
214         // we pass in an empty attributes, as there is just no way for us to
215         // properly re-construct attributes. Fortunately, I know MSV is not using
216         // attribute values, so this would work, but nevertheless this code is
217         // ugly. This is one of the problems of the "middle" approach.
218         Acceptor child = acceptor.createChildAcceptor( sti, null );
219         if( child==null ) {
220             // this element is invalid. probably, so this object is invalid
221             // report an error
222             StringRef ref = new StringRef();
223             child = acceptor.createChildAcceptor( sti, ref );
224             context.reportEvent(target,ref.str);
225         }
226         
227         this.currentElementUri = uri;
228         this.currentElementLocalName = local;
229         
230         acceptor = child;
231     }
232     
233     public void endElement() throws SAXException {
234         writePendingText();
235         
236         if(!acceptor.isAcceptState(null)) {
237             // some required elements are missing
238             // report error
239             StringRef ref = new StringRef();
240             acceptor.isAcceptState(ref);
241             context.reportEvent(target,ref.str);
242         }
243         
244         // pop the acceptor
245         Acceptor child = acceptor;
246         acceptor = (Acceptor)stack.pop();
247         if(!acceptor.stepForward( child, null )) {
248             // some required elements are missing.
249             // report an error
250             StringRef ref = new StringRef();
251             acceptor.stepForward( child, ref );  // force recovery and obtain an error message.
252             
253             context.reportEvent(target,ref.str);
254         }
255         
256         context.getNamespaceContext().endElement();
257     }
258     
259     
260     public void childAsAttributes( JAXBObject o, String fieldName ) throws SAXException {
261         // do nothing
262         
263         // either the onMarshallableObjectInElement method
264         // or the onMarshallableObjectInAttributeBody method will be 
265         // called for every content tree objects.
266         //
267         // so we don't need to validate an object within this method.
268     }
269 
270     public void childAsURIs( JAXBObject o, String fieldName ) throws SAXException {
271         // ditto.
272     }
273 
274     
275     /*** An empty <code>Attributes</code> object. */
276     private static final AttributesImpl emptyAttributes = new AttributesImpl();
277     
278     /*** namespace URI of dummy elements. TODO: allocate one namespace URI for this. */
279     public static final String DUMMY_ELEMENT_NS =
280         "http://java.sun.com/jaxb/xjc/dummy-elements";
281 
282     public void childAsBody( JAXBObject o, String fieldName ) throws SAXException {
283         //final ValidatableObject vo = Util.toValidatableObject(o);
284         final ValidatableObject vo =  jaxbContext.getGrammarInfo().castToValidatableObject(o);
285 
286         if(vo==null) {
287             reportMissingObjectError(fieldName);
288             return;
289         }
290         
291         if( insideAttribute )   childAsAttributeBody(vo,fieldName);
292         else                    childAsElementBody(o,vo);
293     }
294     
295     private void childAsElementBody( Object o, ValidatableObject vo ) throws SAXException {
296         String intfName = vo.getPrimaryInterface().getName();
297         intfName = intfName.replace('$','.');
298         
299         // if the object implements the RIElement interface,
300         // add a marker attribute to the dummy element.
301         //
302         // For example, if the object is org.acme.impl.FooImpl,
303         // the dummy element will look like
304         // <{DUMMY_ELEMENT_NS}org.acme.Foo
305         //          {<URI of this element>}:<local name of this element>="" />
306         // 
307         // This extra attribute is used to validate wildcards.
308 //        AttributesImpl atts;
309 //        if(o instanceof RIElement) {
310 //            RIElement rie = (RIElement)o;
311 //            atts = new AttributesImpl();
312 //            atts.addAttribute(
313 //                rie.____jaxb_ri____getNamespaceURI(),
314 //                rie.____jaxb_ri____getLocalName(),
315 //                rie.____jaxb_ri____getLocalName(),  // use local name as qname
316 //                "CDATA",
317 //                "");    // we don't care about the attribute value
318 //        } else
319 //            atts = emptyAttributes;
320             
321         
322         // feed a dummy element to the acceptor.
323         StartTagInfo sti = new StartTagInfo(
324             DUMMY_ELEMENT_NS,
325             intfName,
326             intfName/*just pass the local name as QName.*/,
327             emptyAttributes,
328             this );
329         
330             
331         Acceptor child = acceptor.createChildAcceptor(sti,null);
332         if(child==null) {
333             // some required elements were missing. report errors
334             StringRef ref = new StringRef();
335             child = acceptor.createChildAcceptor(sti,ref);
336             context.reportEvent(target,ref.str);
337         }
338         
339         if(o instanceof RIElement) {
340             RIElement rie = (RIElement)o;
341             if(!child.onAttribute2(
342                 rie.____jaxb_ri____getNamespaceURI(),
343                 rie.____jaxb_ri____getLocalName(),
344                 rie.____jaxb_ri____getLocalName(),
345                 "",
346                 null, null, null ))
347                 
348                 // this object is not a valid member of the wildcard
349                 context.reportEvent(target,
350                     Messages.format( Messages.INCORRECT_CHILD_FOR_WILDCARD,
351                         rie.____jaxb_ri____getNamespaceURI(),
352                         rie.____jaxb_ri____getLocalName() ));
353         }
354         
355         child.onEndAttributes(sti,null);
356         
357         
358         if(!acceptor.stepForward(child,null)) {
359             // this can't be possible, as the dummy element was 
360             // generated by XJC.
361             throw new JAXBAssertionError();
362         }
363 
364         
365         // we need a separate validator instance to validate a child object
366         context.validate(vo);
367         
368     }
369     
370     private void childAsAttributeBody( ValidatableObject vo, String fieldName ) throws SAXException {
371         /*
372         Dirty quick hack. When we split a schema into fragments, basically
373         every chlid object needs a place holder in the fragment
374         (so that the parent schema fragment can correctly validate that the
375         child objects are at their supposed places.)
376         
377         For example, cconsider the following schema:
378         
379         imagine:
380         <class>
381           <attribute>
382             <list>
383               <oneOrMore>
384                 <ref name="bar"/>
385               </oneOrMore>
386             </list>
387           </attribute>
388         </class>
389         
390         In our algorithm, the corresponding schema fragment will be:
391         
392         <class>
393           <attribute>
394             <list>
395               <oneOrMore>
396                 <value>\u0000full.class.name.of.BarImpl</value>
397               </oneOrMore>
398             </list>
399           </attribute>
400         </class>
401         
402         If we find a child object inside an attribute
403         (that's why we are in this method BTW),
404         we generate a class name (with a special marker \u0000).
405         */
406         
407         // put a class name with a special marker \u0000. This char is an invalid
408         // XML char, so sensible datatypes should reject this (although many
409         // datatype implementations will accept it in actuality)
410         text("\u0000"+vo.getPrimaryInterface().getName(),fieldName);
411 
412         // validate a child object
413         context.validate(vo);
414     }
415 
416 
417     public void reportError( ValidationEvent e ) throws AbortSerializationException {
418         context.reportEvent(target,e);
419     }
420 
421 //
422 //
423 // ID/IDREF validation
424 //
425 //
426     public String onID( IdentifiableObject owner, String value ) throws SAXException {
427         return context.onID(target,value);
428     }
429     public String onIDREF( IdentifiableObject value ) throws SAXException {
430         return context.onIDREF(target,value.____jaxb____getId());
431     }
432 
433 //
434 //  
435 // ValidationContext implementation. Used by MSV to obtain
436 // contextual information related to validation.
437 //
438 //
439     public String getBaseUri() { return null; }
440     public boolean isUnparsedEntity( String entityName ) {
441         // abandon the validation of ENTITY type.
442         return true;
443     }
444     public boolean isNotation( String notation ) {
445         // abandon the validation of NOTATION type.
446         return true;
447     }
448     public void onID( Datatype dt, StringToken s ) {
449         // ID/IDREF validation will be done by ourselves.
450         // so we will not rely on the validator to perform this check.
451         // because we will use multiple instances of validators, so 
452         // they cannot check global consistency.
453         
454         // see onID/onIDREF of the ValidationContext.
455     }
456     public String resolveNamespacePrefix( String prefix ) {
457         return context.getNamespaceContext().getNamespaceURI(prefix);
458     }
459     
460 }