View Javadoc

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