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    }