001//
002// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v1.0.6-01/24/2006 06:15 PM(kohsuke)-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: 2012.10.03 at 04:27:47 AM CEST 
006//
007
008package org.jdtaus.mojo.resource.model.impl.runtime;
009
010import javax.xml.bind.ValidationEvent;
011
012import org.relaxng.datatype.Datatype;
013import org.xml.sax.SAXException;
014import org.xml.sax.helpers.AttributesImpl;
015
016import com.sun.msv.grammar.IDContextProvider2;
017import com.sun.msv.util.LightStack;
018import com.sun.msv.util.StartTagInfo;
019import com.sun.msv.util.StringRef;
020import com.sun.msv.verifier.Acceptor;
021import com.sun.msv.verifier.regexp.StringToken;
022import com.sun.xml.bind.JAXBAssertionError;
023import com.sun.xml.bind.JAXBObject;
024import com.sun.xml.bind.RIElement;
025import com.sun.xml.bind.marshaller.IdentifiableObject;
026import com.sun.xml.bind.serializer.AbortSerializationException;
027import com.sun.xml.bind.serializer.Util;
028import com.sun.xml.bind.validator.Messages;
029
030/**
031 * XMLSerializer that calls the native interface of MSV and performs validation.
032 * Used in a pair with a ValidationContext.
033 * 
034 * @author  Kohsuke Kawaguchi
035 */
036public class MSVValidator implements XMLSerializer, IDContextProvider2
037{
038    /** Current acceptor in use. */
039    private Acceptor acceptor;
040    
041    /** Context object that coordinates the entire validation effort. */
042    private final ValidationContext context;
043    
044    /** The object which we are validating. */
045    private final ValidatableObject target;
046    
047    final DefaultJAXBContextImpl jaxbContext;
048    
049    /**
050     * Acceptor stack. Whenever an element is found, the current acceptor is
051     * pushed to the stack and new one is created.
052     * 
053     * LightStack is a light-weight stack implementation
054     */
055    private final LightStack stack = new LightStack();
056
057    public NamespaceContext2 getNamespaceContext() {
058        return context.getNamespaceContext();
059    }
060    
061    /**
062     * To use this class, call the static validate method.
063     */
064    private MSVValidator( DefaultJAXBContextImpl _jaxbCtx, ValidationContext _ctxt, ValidatableObject vo ) {
065        jaxbContext = _jaxbCtx;
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( DefaultJAXBContextImpl jaxbCtx, ValidationContext context, ValidatableObject vo )
075            throws SAXException {
076        try {
077            new MSVValidator(jaxbCtx,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        context.getNamespaceContext().startElement();
091        
092        // validate attributes
093        target.serializeURIs(this);
094        
095        endNamespaceDecls();
096        
097        target.serializeAttributes(this);
098        
099        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}