1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 package org.exolab.castor.xml.schema.reader;
47
48
49 import java.io.InputStream;
50 import java.util.Enumeration;
51 import java.util.HashMap;
52 import java.util.Properties;
53 import java.util.StringTokenizer;
54
55 import org.exolab.castor.net.URIResolver;
56 import org.exolab.castor.net.util.URIResolverImpl;
57 import org.exolab.castor.xml.AttributeSet;
58 import org.exolab.castor.xml.Namespaces;
59 import org.exolab.castor.xml.XMLException;
60 import org.exolab.castor.xml.schema.Annotation;
61 import org.exolab.castor.xml.schema.AttributeDecl;
62 import org.exolab.castor.xml.schema.AttributeGroupDecl;
63 import org.exolab.castor.xml.schema.ComplexType;
64 import org.exolab.castor.xml.schema.ElementDecl;
65 import org.exolab.castor.xml.schema.Form;
66 import org.exolab.castor.xml.schema.SchemaContext;
67 import org.exolab.castor.xml.schema.ModelGroup;
68 import org.exolab.castor.xml.schema.RedefineSchema;
69 import org.exolab.castor.xml.schema.Schema;
70 import org.exolab.castor.xml.schema.SchemaException;
71 import org.exolab.castor.xml.schema.SchemaNames;
72 import org.exolab.castor.xml.schema.ScopableResolver;
73 import org.exolab.castor.xml.schema.SimpleType;
74 import org.exolab.castor.xml.util.AttributeSetImpl;
75
76
77
78
79
80 public class SchemaUnmarshaller extends ComponentReader {
81
82
83
84
85
86
87 public static final String XSD_NAMESPACE
88 = "http://www.w3.org/2001/XMLSchema";
89
90
91
92
93
94
95 public static final String[] UNSUPPORTED_NAMESPACES = {
96 "http://www.w3.org/2000/10/XMLSchema",
97 "http://www.w3.org/1999/XMLSchema"
98 };
99
100
101
102
103
104
105
106
107
108 private boolean _include = false;
109
110
111
112
113 private ComponentReader _unmarshaller;
114
115
116
117
118 private int _annotationDepth = 0;
119
120
121
122
123 private int _depth = 0;
124
125 boolean skipAll = false;
126
127 Schema _schema = null;
128
129 private boolean foundSchemaDef = false;
130
131
132
133
134
135 private String _defaultNS = null;
136
137
138
139
140 private SchemaUnmarshallerState _state = null;
141
142
143
144
145 private RemappedPrefixes _prefixMappings = null;
146
147
148
149
150
151
152
153
154
155
156 public SchemaUnmarshaller(final SchemaContext schemaContext)
157 throws XMLException {
158 this(schemaContext, null, null);
159 foundSchemaDef = false;
160 }
161
162
163
164
165
166
167
168 public SchemaUnmarshaller(
169 final SchemaContext schemaContext,
170 final SchemaUnmarshallerState state)
171 throws XMLException {
172 this(schemaContext, null, null);
173 _state = state;
174 foundSchemaDef = false;
175 }
176
177
178
179
180
181
182
183
184
185
186
187
188
189 public SchemaUnmarshaller(
190 final SchemaContext schemaContext,
191 final boolean include,
192 final SchemaUnmarshallerState state,
193 final URIResolver uriResolver)
194 throws XMLException {
195
196 this(schemaContext, null, uriResolver);
197 _state = state;
198 _include = include;
199 foundSchemaDef = false;
200 }
201
202
203
204
205
206
207
208
209 public SchemaUnmarshaller(
210 final SchemaContext schemaContext,
211 final AttributeSet atts) throws XMLException {
212 this(schemaContext, atts, null);
213 }
214
215
216
217
218
219
220
221
222 private SchemaUnmarshaller(
223 final SchemaContext schemaContext,
224 final AttributeSet atts,
225 final URIResolver uriResolver)
226 throws XMLException {
227 super(schemaContext);
228
229 _schema = new Schema();
230
231
232 _schema.removeNamespace("");
233 if (getResolver() == null) {
234 setResolver(new ScopableResolver());
235 }
236 if (uriResolver == null) {
237 setURIResolver(new URIResolverImpl());
238 } else {
239 setURIResolver(uriResolver);
240 }
241 foundSchemaDef = true;
242 _state = new SchemaUnmarshallerState();
243 init(atts);
244 }
245
246
247
248
249
250
251 public Schema getSchema() {
252 return _schema;
253 }
254
255
256
257
258
259 public void setSchema(final Schema schema) {
260 _schema = schema;
261 }
262
263
264
265
266
267 public Object getObject() {
268 return getSchema();
269 }
270
271
272
273
274
275
276
277 public String elementName() {
278 return SchemaNames.SCHEMA;
279 }
280
281
282
283
284
285
286
287
288 private void init(final AttributeSet atts) throws XMLException {
289 if (atts == null) {
290 return;
291 }
292
293 String attValue = null;
294
295 String nsURI = atts.getValue(SchemaNames.TARGET_NS_ATTR);
296 if (nsURI != null && nsURI.length() == 0) {
297 throw new SchemaException("empty string is not a legal namespace.");
298 }
299 if ((nsURI != null) && (nsURI.length() > 0)) {
300 if (!_state.cacheIncludedSchemas) {
301
302
303 if ((_include) && (!_schema.getTargetNamespace().equals(nsURI))) {
304 throw new SchemaException("The target namespace of the included "
305 + "components must be the same as the target namespace "
306 + "of the including schema");
307 }
308 }
309 _schema.setTargetNamespace(nsURI);
310 }
311
312 _schema.setId(atts.getValue(SchemaNames.ID_ATTR));
313 _schema.setVersion(atts.getValue(SchemaNames.VERSION_ATTR));
314
315
316 if (!_include || _state.cacheIncludedSchemas) {
317 _schema.setSchemaLocation(getDocumentLocator().getSystemId());
318 }
319
320
321 String form = atts.getValue(SchemaNames.ATTR_FORM_DEFAULT_ATTR);
322 if (form != null) {
323 _schema.setAttributeFormDefault(Form.valueOf(form));
324 }
325
326
327 form = atts.getValue(SchemaNames.ELEM_FORM_DEFAULT_ATTR);
328 if (form != null) {
329 _schema.setElementFormDefault(Form.valueOf(form));
330 }
331
332
333 attValue = atts.getValue(SchemaNames.BLOCK_DEFAULT_ATTR);
334 if (attValue != null) {
335 _schema.setBlockDefault(attValue);
336 }
337
338
339 attValue = atts.getValue(SchemaNames.FINAL_DEFAULT_ATTR);
340 if (attValue != null) {
341 _schema.setFinalDefault(attValue);
342 }
343
344
345 attValue = atts.getValue(SchemaNames.VERSION_ATTR);
346 if (attValue != null) {
347 _schema.setVersion(attValue);
348 }
349
350 }
351
352
353
354
355
356
357 private void handleNamespaces(final Namespaces namespaces)
358 throws XMLException {
359
360 if (namespaces == null) {
361 return;
362 }
363
364 Enumeration enumeration = namespaces.getLocalNamespaces();
365
366 while (enumeration.hasMoreElements()) {
367
368 String ns = (String) enumeration.nextElement();
369 String[] prefixes = namespaces.getNamespacePrefixes(ns);
370
371 if (prefixes.length == 0) {
372
373
374 String error = "unexpected error processing the following "
375 + "namespace: '" + ns + "'; the prefix could not be resolved.";
376 throw new XMLException(error);
377 }
378
379 boolean hasCollisions = false;
380 for (int pIdx = 0; pIdx < prefixes.length; pIdx++) {
381 String prefix = prefixes[pIdx];
382
383
384
385
386 String tmpURI = _schema.getNamespace(prefix);
387 if ((tmpURI != null) && (foundSchemaDef)) {
388 if (!tmpURI.equals(ns)) {
389 if (!hasCollisions) {
390 hasCollisions = true;
391 if (_prefixMappings == null) {
392 _prefixMappings = new RemappedPrefixes();
393 } else {
394 _prefixMappings = _prefixMappings.newRemappedPrefixes();
395 }
396 }
397
398
399 if (prefix.length() == 0) {
400 prefix = "ns";
401 }
402
403 int count = 1;
404 String newPrefix = prefix + count;
405 tmpURI = _schema.getNamespace(newPrefix);
406 while (tmpURI != null) {
407 if (tmpURI.equals(ns)) {
408
409 break;
410 }
411 ++count;
412 newPrefix = prefix + count;
413 tmpURI = _schema.getNamespace(newPrefix);
414 }
415 _prefixMappings.addMapping(prefix, newPrefix);
416 prefix = newPrefix;
417 } else {
418
419 if (_prefixMappings != null) {
420 if (_prefixMappings.isRemappedPrefix(prefix)) {
421
422 _prefixMappings.addMapping(prefix, prefix);
423 }
424 }
425 }
426 }
427
428
429 if (prefix.length() == 0) {
430 _defaultNS = ns;
431
432 _schema.addNamespace("", _defaultNS);
433 } else {
434
435 for (int nsIdx = 0; nsIdx < UNSUPPORTED_NAMESPACES.length; nsIdx++) {
436 if (ns.equals(UNSUPPORTED_NAMESPACES[nsIdx])) {
437 error("The following namespace \"" + ns
438 + "\" is no longer supported. Please update to "
439 + " the W3C XML Schema Recommendation.");
440 }
441 }
442 _schema.addNamespace(prefix, ns);
443 }
444 }
445 }
446
447 }
448
449
450
451
452
453
454
455
456
457
458 private void handleRemapping(final String name, final String namespace,
459 final AttributeSetImpl atts) {
460
461 if (_prefixMappings == null) {
462 return;
463 }
464
465
466 _prefixMappings.depth++;
467
468 String[] remapAtts = (String[]) RemappedPrefixes.QNAME_TABLE.get(name);
469
470 if (remapAtts != null) {
471 for (int i = 0; i < remapAtts.length; i++) {
472 String value = atts.getValue(remapAtts[i]);
473 if (value != null) {
474 value = _prefixMappings.remapQName(value);
475 atts.setAttribute(remapAtts[i], value);
476 }
477 }
478 }
479
480 }
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496 public void startElement(final String name,
497 String namespace, final AttributeSet atts,
498 final Namespaces nsDecls) throws XMLException {
499
500 if (skipAll) {
501 return;
502 }
503
504
505
506
507
508
509
510
511 if (_annotationDepth == 0) {
512 handleNamespaces(nsDecls);
513 }
514
515
516
517 if ((!foundSchemaDef) && (namespace == null)) {
518 if (_defaultNS == null) {
519 _defaultNS = XSD_NAMESPACE;
520 namespace = XSD_NAMESPACE;
521 System.out.println("No namespace declaration has been " +
522 "found for " + name);
523 System.out.print(" * assuming default namespace of ");
524 System.out.println(XSD_NAMESPACE);
525 }
526 }
527 if (namespace == null) {
528 namespace = _defaultNS;
529
530 }
531
532
533 if (name.equals(SchemaNames.ANNOTATION)) {
534 ++_annotationDepth;
535 }
536
537
538 if (!XSD_NAMESPACE.equals(namespace)) {
539 if (_annotationDepth == 0) {
540 error("'" + name + "' has not been declared in the XML "
541 + "Schema namespace.");
542 }
543 }
544
545
546 if (_annotationDepth == 0) {
547 if (_prefixMappings != null) {
548 handleRemapping(name, namespace, (AttributeSetImpl) atts);
549 }
550 }
551
552
553 if (_unmarshaller != null) {
554 try {
555 _unmarshaller.startElement(name, namespace, atts, nsDecls);
556 } catch (RuntimeException rtx) {
557 error(rtx);
558 }
559 ++_depth;
560 return;
561 }
562
563 if (name.equals(SchemaNames.SCHEMA)) {
564
565 if (foundSchemaDef) {
566 illegalElement(name);
567 }
568
569 foundSchemaDef = true;
570 init(atts);
571 return;
572 }
573
574
575 if (name.equals(SchemaNames.ANNOTATION)) {
576 _unmarshaller = new AnnotationUnmarshaller(getSchemaContext(), atts);
577 } else if (name.equals(SchemaNames.ATTRIBUTE)) {
578
579 _unmarshaller = new AttributeUnmarshaller(getSchemaContext(), _schema, atts);
580 } else if (name.equals(SchemaNames.ATTRIBUTE_GROUP)) {
581
582 _unmarshaller = new AttributeGroupUnmarshaller(getSchemaContext(), _schema, atts);
583 } else if (name.equals(SchemaNames.COMPLEX_TYPE)) {
584
585 _unmarshaller
586 = new ComplexTypeUnmarshaller(getSchemaContext(), _schema, atts);
587 } else if (name.equals(SchemaNames.ELEMENT)) {
588
589 _unmarshaller
590 = new ElementUnmarshaller(getSchemaContext(), _schema, atts);
591 } else if (name.equals(SchemaNames.SIMPLE_TYPE)) {
592
593 _unmarshaller = new SimpleTypeUnmarshaller(getSchemaContext(), _schema, atts);
594 } else if (name.equals(SchemaNames.GROUP)) {
595
596 _unmarshaller = new ModelGroupUnmarshaller(getSchemaContext(), _schema, atts);
597 } else if (name.equals(SchemaNames.INCLUDE)) {
598
599 _unmarshaller
600 = new IncludeUnmarshaller(getSchemaContext(), _schema, atts,
601 getURIResolver(), getDocumentLocator(), _state);
602 } else if (name.equals(SchemaNames.IMPORT)) {
603
604 _unmarshaller
605 = new ImportUnmarshaller(getSchemaContext(), _schema, atts,
606 getURIResolver(), getDocumentLocator(), _state);
607 } else if (name.equals(SchemaNames.REDEFINE)) {
608
609 _unmarshaller
610 = new RedefineUnmarshaller(getSchemaContext(), _schema, atts,
611 getURIResolver(), getDocumentLocator(), _state);
612 } else {
613
614
615
616 System.out.print('<');
617 System.out.print(name);
618 System.out.print("> elements are either currently unsupported ");
619 System.out.println("or non-valid schema elements.");
620 _unmarshaller = new UnknownUnmarshaller(getSchemaContext(), name);
621 }
622
623
624
625 }
626
627
628
629
630
631
632
633
634
635 public void endElement(String name, String namespace)
636 throws XMLException {
637 if (skipAll) {
638 return;
639 }
640
641
642
643
644
645
646 if (namespace == null) {
647 namespace = _defaultNS;
648 }
649
650
651 if (name.equals(SchemaNames.ANNOTATION)) {
652 --_annotationDepth;
653 }
654
655
656 if (_prefixMappings != null) {
657 if (_prefixMappings.depth == 0) {
658 _prefixMappings = _prefixMappings.getParent();
659 } else {
660 --_prefixMappings.depth;
661 }
662 }
663
664
665 if ((_unmarshaller != null) && (_depth > 0)) {
666 _unmarshaller.endElement(name, namespace);
667 --_depth;
668 return;
669 }
670
671
672 name = name.intern();
673
674 if (name == SchemaNames.SCHEMA) {
675 return;
676 }
677
678
679 if ((_unmarshaller != null)) {
680 if (!name.equals(_unmarshaller.elementName())) {
681 String err = "error: missing end element for ";
682 err += _unmarshaller.elementName();
683 throw new SchemaException(err);
684 }
685 } else {
686 String err = "error: missing start element for " + name;
687 throw new SchemaException(err);
688 }
689
690
691 _unmarshaller.finish();
692
693
694 if (name.equals(SchemaNames.ANNOTATION)) {
695 _schema.addAnnotation((Annotation) _unmarshaller.getObject());
696 } else if (name.equals(SchemaNames.ATTRIBUTE)) {
697
698 _schema.addAttribute((AttributeDecl) _unmarshaller.getObject());
699 } else if (name.equals(SchemaNames.ATTRIBUTE_GROUP)) {
700
701 Object obj = _unmarshaller.getObject();
702 try {
703 _schema.addAttributeGroup((AttributeGroupDecl) obj);
704 } catch (ClassCastException ex) {
705 String err = "Top-level AttributeGroups must be defining "
706 + "AttributeGroups and not referring AttributeGroups.";
707 error(err);
708 }
709 } else if (name.equals(SchemaNames.COMPLEX_TYPE)) {
710
711 ComplexType complexType = null;
712 complexType = ((ComplexTypeUnmarshaller) _unmarshaller).getComplexType();
713 _schema.addComplexType(complexType);
714 if (complexType.getName() != null) {
715 getResolver().addResolvable(complexType.getReferenceId(), complexType);
716 } else {
717 System.out.println("warning: top-level complexType with no name.");
718 }
719 } else if (name.equals(SchemaNames.SIMPLE_TYPE)) {
720
721 SimpleType simpleType = null;
722 simpleType = ((SimpleTypeUnmarshaller) _unmarshaller).getSimpleType();
723 _schema.addSimpleType(simpleType);
724 getResolver().addResolvable(simpleType.getReferenceId(), simpleType);
725 } else if (name.equals(SchemaNames.ELEMENT)) {
726
727 ElementDecl element = null;
728 element = ((ElementUnmarshaller) _unmarshaller).getElement();
729 _schema.addElementDecl(element);
730 } else if (name.equals(SchemaNames.GROUP)) {
731
732 ModelGroup group = null;
733 group = (((ModelGroupUnmarshaller) _unmarshaller).getGroup());
734 _schema.addModelGroup(group);
735 } else if (name.equals(SchemaNames.REDEFINE)) {
736
737 RedefineSchema redefine = null;
738 redefine = (RedefineSchema) (((RedefineUnmarshaller) _unmarshaller).getObject());
739 if ((redefine.getSchemaLocation() == null) && (redefine.hasRedefinition())) {
740 _schema.removeRedefineSchema(redefine);
741 String err = "A <redefine> structure with no 'schemaLocation' "
742 + "attribute must contain only <annotation> elements";
743 error(err);
744 }
745 }
746
747 _unmarshaller = null;
748 }
749
750
751
752
753
754
755 public void characters(final char[] ch, final int start, final int length)
756 throws XMLException {
757
758 if (_unmarshaller != null) {
759 _unmarshaller.characters(ch, start, length);
760 }
761 }
762
763
764
765
766
767
768
769
770 static class RemappedPrefixes {
771
772 public static final String RESOURCE_NAME
773 = "prefixremap.properties";
774
775 public static final String RESOURCE_LOCATION =
776 "/org/exolab/castor/xml/schema/reader/";
777
778 public static final HashMap QNAME_TABLE = new HashMap();
779 private static boolean initialized = false;
780
781 static {
782
783 synchronized (QNAME_TABLE) {
784
785 if (!initialized) {
786
787 initialized = true;
788
789
790
791
792 QNAME_TABLE.put(SchemaNames.ATTRIBUTE, new String [] {
793 SchemaNames.REF_ATTR, SchemaNames.TYPE_ATTR });
794
795
796 QNAME_TABLE.put(SchemaNames.ATTRIBUTE_GROUP, new String [] {
797 SchemaNames.REF_ATTR });
798
799
800 QNAME_TABLE.put(SchemaNames.ELEMENT, new String [] {
801 SchemaNames.REF_ATTR, SchemaNames.TYPE_ATTR });
802
803
804 QNAME_TABLE.put(SchemaNames.EXTENSION, new String [] {
805 SchemaNames.BASE_ATTR });
806
807
808 QNAME_TABLE.put(SchemaNames.GROUP, new String [] {
809 SchemaNames.REF_ATTR });
810
811
812 QNAME_TABLE.put(SchemaNames.RESTRICTION, new String [] {
813 SchemaNames.BASE_ATTR });
814
815
816
817 String filename = RESOURCE_LOCATION + RESOURCE_NAME;
818 InputStream is = SchemaUnmarshaller.class.getResourceAsStream(filename);
819 Properties props = new Properties();
820 if (is != null) {
821 try {
822 props.load(is);
823 } catch (java.io.IOException iox) {
824
825 }
826 }
827
828 Enumeration keys = props.propertyNames();
829 while (keys.hasMoreElements()) {
830 String name = (String) keys.nextElement();
831 StringTokenizer st = new StringTokenizer(props.getProperty(name), ",");
832 String[] atts = new String[st.countTokens()];
833 int index = 0;
834 while (st.hasMoreTokens()) {
835 atts[index++] = st.nextToken();
836 }
837 QNAME_TABLE.put(name, atts);
838 }
839
840 }
841 }
842 }
843
844 private HashMap _prefixes = null;
845
846 private RemappedPrefixes _parent = null;
847
848 int depth = 0;
849
850 public boolean isRemappedPrefix(String prefix) {
851
852 if (prefix == null) prefix = "";
853
854 if (_prefixes != null) {
855 if (_prefixes.get(prefix) != null) return true;
856 }
857
858 if (_parent != null) {
859 return _parent.isRemappedPrefix(prefix);
860 }
861 return false;
862 }
863
864 public RemappedPrefixes getParent() {
865 return _parent;
866 }
867
868 public String getPrefixMapping(final String oldPrefix) {
869
870 if (_prefixes != null) {
871 String newPrefix = (String)_prefixes.get(oldPrefix);
872 if (newPrefix != null)
873 return newPrefix;
874 }
875
876 if (_parent != null) {
877 return _parent.getPrefixMapping(oldPrefix);
878 }
879
880 return oldPrefix;
881 }
882
883 public RemappedPrefixes newRemappedPrefixes() {
884 RemappedPrefixes rp = new RemappedPrefixes();
885 rp._parent = this;
886 return rp;
887 }
888
889 public void addMapping(final String oldPrefix, final String newPrefix) {
890 if (_prefixes == null) {
891 _prefixes = new HashMap();
892 }
893 _prefixes.put(oldPrefix, newPrefix);
894 }
895
896 public String remapQName(String value) {
897 if (value == null) {
898 return null;
899 }
900
901
902 int idx = value.indexOf(':');
903 String prefix = "";
904 if (idx >= 0) {
905 prefix = value.substring(0, idx);
906 } else {
907 idx = -1;
908 }
909 String newPrefix = getPrefixMapping(prefix);
910 if (!prefix.equals(newPrefix)) {
911 if (newPrefix.length() == 0) {
912 value = value.substring(idx + 1);
913 } else {
914 value = newPrefix + ":" + value.substring(idx + 1);
915 }
916 }
917
918 return value;
919 }
920
921 }
922
923 }
924