1
2
3
4
5
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
81
82
83
84
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
94 target.serializeURIs(this);
95
96 endNamespaceDecls();
97
98 target.serializeAttributes(this);
99
100 endAttributes();
101
102
103 target.serializeBody(this);
104 writePendingText();
105
106 context.getNamespaceContext().endElement();
107
108 if (!acceptor.isAcceptState(null)) {
109
110
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
124
125
126
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
155 private String attNamespaceUri;
156
157 private String attLocalName;
158
159 private boolean insideAttribute;
160
161 public void startAttribute(String uri, String local) {
162
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
172
173
174
175
176
177
178
179
180
181 , buf.toString(), this, null, null)) {
182
183
184
185
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
198 if (!acceptor.onText2(buf.toString(), this, null, null)) {
199
200
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
227
228
229
230
231 Acceptor child = acceptor.createChildAcceptor(sti, null);
232 if (child == null) {
233
234
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
251
252 StringRef ref = new StringRef();
253 acceptor.isAcceptState(ref);
254 context.reportEvent(target, ref.str);
255 }
256
257
258 Acceptor child = acceptor;
259 acceptor = (Acceptor) stack.pop();
260 if (!acceptor.stepForward(child, null)) {
261
262
263 StringRef ref = new StringRef();
264 acceptor.stepForward(child, ref);
265
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
276
277
278
279
280
281
282 }
283
284 public void childAsURIs(JAXBObject o, String fieldName) throws SAXException {
285
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
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
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341 StartTagInfo sti = new StartTagInfo(DUMMY_ELEMENT_NS, intfName,
342 intfName
343 emptyAttributes, this);
344
345 Acceptor child = acceptor.createChildAcceptor(sti, null);
346 if (child == null) {
347
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
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
370
371 throw new JAXBAssertionError();
372 }
373
374
375 context.validate(vo);
376
377 }
378
379 private void childAsAttributeBody(ValidatableObject vo, String fieldName)
380 throws SAXException {
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 text("\u0000" + vo.getPrimaryInterface().getName(), fieldName);
408
409
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
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
435
436
437
438 public String getBaseUri() {
439 return null;
440 }
441
442 public boolean isUnparsedEntity(String entityName) {
443
444 return true;
445 }
446
447 public boolean isNotation(String notation) {
448
449 return true;
450 }
451
452 public void onID(Datatype dt, StringToken s) {
453
454
455
456
457
458
459 }
460
461 public String resolveNamespacePrefix(String prefix) {
462 return context.getNamespaceContext().getNamespaceURI(prefix);
463 }
464
465 }