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