1
2
3
4
5
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
80
81
82
83
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
93 target.serializeURIs(this);
94
95 endNamespaceDecls();
96
97 target.serializeAttributes(this);
98
99 endAttributes();
100
101
102 target.serializeBody(this);
103 writePendingText();
104
105 context.getNamespaceContext().endElement();
106
107 if(!acceptor.isAcceptState(null)) {
108
109
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
123
124
125
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
155 private String attNamespaceUri;
156 private String attLocalName;
157 private boolean insideAttribute;
158
159 public void startAttribute( String uri, String local ) {
160
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
170 buf.toString(),
171 this, null, null )) {
172
173
174
175
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
188 if(!acceptor.onText2( buf.toString(), this, null, null )) {
189
190
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
215
216
217
218 Acceptor child = acceptor.createChildAcceptor( sti, null );
219 if( child==null ) {
220
221
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
238
239 StringRef ref = new StringRef();
240 acceptor.isAcceptState(ref);
241 context.reportEvent(target,ref.str);
242 }
243
244
245 Acceptor child = acceptor;
246 acceptor = (Acceptor)stack.pop();
247 if(!acceptor.stepForward( child, null )) {
248
249
250 StringRef ref = new StringRef();
251 acceptor.stepForward( child, ref );
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
262
263
264
265
266
267
268 }
269
270 public void childAsURIs( JAXBObject o, String fieldName ) throws SAXException {
271
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
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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323 StartTagInfo sti = new StartTagInfo(
324 DUMMY_ELEMENT_NS,
325 intfName,
326 intfName
327 emptyAttributes,
328 this );
329
330
331 Acceptor child = acceptor.createChildAcceptor(sti,null);
332 if(child==null) {
333
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
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
360
361 throw new JAXBAssertionError();
362 }
363
364
365
366 context.validate(vo);
367
368 }
369
370 private void childAsAttributeBody( ValidatableObject vo, String fieldName ) throws SAXException {
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410 text("\u0000"+vo.getPrimaryInterface().getName(),fieldName);
411
412
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
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
436
437
438
439 public String getBaseUri() { return null; }
440 public boolean isUnparsedEntity( String entityName ) {
441
442 return true;
443 }
444 public boolean isNotation( String notation ) {
445
446 return true;
447 }
448 public void onID( Datatype dt, StringToken s ) {
449
450
451
452
453
454
455 }
456 public String resolveNamespacePrefix( String prefix ) {
457 return context.getNamespaceContext().getNamespaceURI(prefix);
458 }
459
460 }