1
/* XMLSchemaBuilder.java --
2
Copyright (C) 2006 Free Software Foundation, Inc.
4
This file is part of GNU Classpath.
6
GNU Classpath is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2, or (at your option)
11
GNU Classpath is distributed in the hope that it will be useful, but
12
WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with GNU Classpath; see the file COPYING. If not, write to the
18
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21
Linking this library statically or dynamically with other modules is
22
making a combined work based on this library. Thus, the terms and
23
conditions of the GNU General Public License cover the whole
26
As a special exception, the copyright holders of this library give you
27
permission to link this library with independent modules to produce an
28
executable, regardless of the license terms of these independent
29
modules, and to copy and distribute the resulting executable under
30
terms of your choice, provided that you also meet, for each linked
31
independent module, the terms and conditions of the license of that
32
module. An independent module is a module which is not derived from
33
or based on this library. If you modify this library, you may extend
34
this exception to your version of the library, but you are not
35
obligated to do so. If you do not wish to do so, delete this
36
exception statement from your version. */
38
package gnu.xml.validation.xmlschema;
40
import java.util.LinkedHashSet;
41
import java.util.List;
43
import java.util.StringTokenizer;
44
import javax.xml.XMLConstants;
45
import javax.xml.namespace.QName;
46
import org.relaxng.datatype.DatatypeException;
47
import org.relaxng.datatype.DatatypeLibrary;
48
import org.relaxng.datatype.helpers.DatatypeLibraryLoader;
49
import org.w3c.dom.NamedNodeMap;
50
import org.w3c.dom.Node;
51
import gnu.xml.validation.datatype.Annotation;
52
import gnu.xml.validation.datatype.AtomicSimpleType;
53
import gnu.xml.validation.datatype.ListSimpleType;
54
import gnu.xml.validation.datatype.SimpleType;
55
import gnu.xml.validation.datatype.Type;
56
import gnu.xml.validation.datatype.UnionSimpleType;
59
* Parses an XML Schema DOM tree, constructing a compiled internal
62
* @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
64
class XMLSchemaBuilder
68
final DatatypeLibrary typeLibrary;
72
final String ns = XMLConstants.W3C_XML_SCHEMA_NS_URI;
73
typeLibrary = new DatatypeLibraryLoader().createDatatypeLibrary(ns);
76
void parseSchema(Node node)
77
throws DatatypeException
79
String uri = node.getNamespaceURI();
80
String name = node.getLocalName();
81
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
82
node.getNodeType() == Node.ELEMENT_NODE)
84
if ("schema".equals(name))
86
NamedNodeMap attrs = node.getAttributes();
87
String targetNamespace = getAttribute(attrs, "targetNamespace");
88
String version = getAttribute(attrs, "version");
89
String fd = getAttribute(attrs, "finalDefault");
90
int finalDefault = parseFullDerivationSet(fd);
91
String bd = getAttribute(attrs, "blockDefault");
92
int blockDefault = parseBlockSet(bd);
93
String afd = getAttribute(attrs, "attributeFormDefault");
94
boolean attributeFormQualified = "qualified".equals(afd);
95
String efd = getAttribute(attrs, "elementFormDefault");
96
boolean elementFormQualified = "qualified".equals(efd);
97
schema = new XMLSchema(targetNamespace, version,
98
finalDefault, blockDefault,
99
attributeFormQualified,
100
elementFormQualified);
101
for (Node child = node.getFirstChild(); child != null;
102
child = child.getNextSibling())
104
parseTopLevelElement(child);
109
// TODO throw schema exception
112
void parseTopLevelElement(Node node)
113
throws DatatypeException
115
String uri = node.getNamespaceURI();
116
String name = node.getLocalName();
117
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
118
node.getNodeType() == Node.ELEMENT_NODE)
120
if ("element".equals(name))
122
ElementDeclaration ed =
123
(ElementDeclaration) parseElement(node, null);
124
schema.elementDeclarations.put(ed.name, ed);
127
else if ("attribute".equals(name))
129
AttributeDeclaration ad =
130
(AttributeDeclaration) parseAttribute(node, true);
131
schema.attributeDeclarations.put(ad.name, ad);
134
else if ("type".equals(name))
138
else if ("group".equals(name))
142
else if ("attributeGroup".equals(name))
146
else if ("notation".equals(name))
150
else if ("identityConstraint".equals(name))
155
// TODO throw schema exception
158
Object parseAttribute(Node node, boolean scope)
159
throws DatatypeException
161
NamedNodeMap attrs = node.getAttributes();
162
String def = getAttribute(attrs, "default");
163
String fixed = getAttribute(attrs, "fixed");
164
int constraintType = AttributeDeclaration.NONE;
165
String constraintValue = null;
168
constraintType = AttributeDeclaration.DEFAULT;
169
constraintValue = def;
171
else if (fixed != null)
173
constraintType = AttributeDeclaration.FIXED;
174
constraintValue = fixed;
176
// TODO form = (qualified | unqualified)
177
String attrName = getAttribute(attrs, "name");
178
String attrNamespace = getAttribute(attrs, "targetNamespace");
179
String ref = getAttribute(attrs, "ref");
180
String use = getAttribute(attrs, "use");
181
String type = getAttribute(attrs, "type");
182
SimpleType datatype = (type == null) ? null :
183
parseSimpleType(asQName(type, node));
184
Annotation annotation = null;
185
for (Node child = node.getFirstChild(); child != null;
186
child = child.getNextSibling())
188
String uri = child.getNamespaceURI();
189
String name = child.getLocalName();
190
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
191
child.getNodeType() == Node.ELEMENT_NODE)
193
if ("annotation".equals(name))
195
annotation = parseAnnotation(child);
197
else if ("simpleType".equals(name))
199
datatype = parseSimpleType(child);
203
// TODO throw schema exception
209
return new AttributeDeclaration(scope,
212
new QName(attrNamespace, attrName),
218
boolean required = "required".equals(use);
220
AttributeDeclaration decl = (ref == null) ?
221
new AttributeDeclaration(scope,
222
AttributeDeclaration.NONE,
224
new QName(attrNamespace, attrName),
227
/*schema.getAttributeDeclaration(ref)*/ null;
228
return new AttributeUse(required,
235
int parseFullDerivationSet(String value)
237
int ret = XMLSchema.FINAL_NONE;
238
if ("#all".equals(value))
240
ret = XMLSchema.FINAL_ALL;
244
StringTokenizer st = new StringTokenizer(value, " ");
245
while (st.hasMoreTokens())
247
String token = st.nextToken();
248
if ("extension".equals(token))
250
ret |= XMLSchema.FINAL_EXTENSION;
252
else if ("restriction".equals(token))
254
ret |= XMLSchema.FINAL_RESTRICTION;
256
else if ("list".equals(token))
258
ret |= XMLSchema.FINAL_LIST;
260
else if ("union".equals(token))
262
ret |= XMLSchema.FINAL_UNION;
269
int parseSimpleTypeDerivationSet(String value)
271
int ret = XMLSchema.FINAL_NONE;
272
if ("#all".equals(value))
274
ret = XMLSchema.FINAL_LIST |
275
XMLSchema.FINAL_UNION |
276
XMLSchema.FINAL_RESTRICTION;
280
StringTokenizer st = new StringTokenizer(value, " ");
281
while (st.hasMoreTokens())
283
String token = st.nextToken();
284
if ("list".equals(token))
286
ret |= XMLSchema.FINAL_LIST;
288
else if ("union".equals(token))
290
ret |= XMLSchema.FINAL_UNION;
292
else if ("restriction".equals(token))
294
ret |= XMLSchema.FINAL_RESTRICTION;
301
int parseComplexTypeDerivationSet(String value)
303
int ret = XMLSchema.FINAL_NONE;
304
if ("#all".equals(value))
306
ret = XMLSchema.FINAL_EXTENSION | XMLSchema.FINAL_RESTRICTION;
310
StringTokenizer st = new StringTokenizer(value, " ");
311
while (st.hasMoreTokens())
313
String token = st.nextToken();
314
if ("extension".equals(token))
316
ret |= XMLSchema.FINAL_EXTENSION;
318
else if ("restriction".equals(token))
320
ret |= XMLSchema.FINAL_RESTRICTION;
327
int parseBlockSet(String value)
329
int ret = XMLSchema.BLOCK_NONE;
330
if ("#all".equals(value))
332
ret = XMLSchema.BLOCK_ALL;
336
StringTokenizer st = new StringTokenizer(value, " ");
337
while (st.hasMoreTokens())
339
String token = st.nextToken();
340
if ("extension".equals(token))
342
ret |= XMLSchema.BLOCK_EXTENSION;
344
else if ("restriction".equals(token))
346
ret |= XMLSchema.BLOCK_RESTRICTION;
348
else if ("substitution".equals(token))
350
ret |= XMLSchema.BLOCK_SUBSTITUTION;
357
int parseComplexTypeBlockSet(String value)
359
int ret = XMLSchema.BLOCK_NONE;
360
if ("#all".equals(value))
362
ret = XMLSchema.BLOCK_EXTENSION | XMLSchema.BLOCK_RESTRICTION;
366
StringTokenizer st = new StringTokenizer(value, " ");
367
while (st.hasMoreTokens())
369
String token = st.nextToken();
370
if ("extension".equals(token))
372
ret |= XMLSchema.BLOCK_EXTENSION;
374
else if ("restriction".equals(token))
376
ret |= XMLSchema.BLOCK_RESTRICTION;
383
Object parseElement(Node node, ElementDeclaration parent)
384
throws DatatypeException
386
NamedNodeMap attrs = node.getAttributes();
387
Integer minOccurs = null;
388
Integer maxOccurs = null;
389
Node parentNode = node.getParentNode();
390
boolean notTopLevel = !"schema".equals(parentNode.getLocalName());
393
String ref = getAttribute(attrs, "ref");
396
minOccurs = getOccurrence(getAttribute(attrs, "minOccurs"));
397
maxOccurs = getOccurrence(getAttribute(attrs, "maxOccurs"));
398
// TODO resolve top-level element declaration
399
ElementDeclaration ad = null;
400
return new Particle(minOccurs, maxOccurs, ad);
403
String elementName = getAttribute(attrs, "name");
404
String elementNamespace = getAttribute(attrs, "targetNamespace");
405
String type = getAttribute(attrs, "type");
406
Type datatype = (type != null) ?
407
parseSimpleType(asQName(type, node)) : null;
408
int scope = (parent == null) ?
411
String def = getAttribute(attrs, "default");
412
String fixed = getAttribute(attrs, "fixed");
413
int constraintType = AttributeDeclaration.NONE;
414
String constraintValue = null;
417
constraintType = AttributeDeclaration.DEFAULT;
418
constraintValue = def;
420
else if (fixed != null)
422
constraintType = AttributeDeclaration.FIXED;
423
constraintValue = fixed;
425
String sg = getAttribute(attrs, "substitutionGroup");
426
QName substitutionGroup = QName.valueOf(sg);
427
String sgPrefix = substitutionGroup.getPrefix();
428
if (sgPrefix != null && !"".equals(sgPrefix))
430
String sgName = substitutionGroup.getLocalPart();
431
String sgNamespace = node.lookupNamespaceURI(sgPrefix);
432
substitutionGroup = new QName(sgNamespace, sgName);
435
String block = getAttribute(attrs, "block");
436
int substitutionGroupExclusions = (block == null) ?
437
schema.blockDefault :
438
parseBlockSet(block);
439
String final_ = getAttribute(attrs, "final");
440
int disallowedSubstitutions = (final_ == null) ?
441
schema.finalDefault :
442
parseFullDerivationSet(final_);
444
boolean nillable = "true".equals(getAttribute(attrs, "nillable"));
445
boolean isAbstract = "true".equals(getAttribute(attrs, "abstract"));
449
minOccurs = getOccurrence(getAttribute(attrs, "minOccurs"));
450
maxOccurs = getOccurrence(getAttribute(attrs, "maxOccurs"));
451
String form = getAttribute(attrs, "form");
454
if ("qualified".equals(form))
456
elementNamespace = schema.targetNamespace;
459
else if (schema.elementFormQualified)
461
elementNamespace = schema.targetNamespace;
464
ElementDeclaration ed =
465
new ElementDeclaration(new QName(elementNamespace, elementName),
468
constraintType, constraintValue,
471
substitutionGroupExclusions,
472
disallowedSubstitutions,
475
for (Node child = node.getFirstChild(); child != null;
476
child = child.getNextSibling())
478
String uri = child.getNamespaceURI();
479
String name = child.getLocalName();
480
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
481
child.getNodeType() == Node.ELEMENT_NODE)
483
if ("annotation".equals(name))
485
ed.annotation = parseAnnotation(child);
487
else if ("simpleType".equals(name) && datatype == null)
489
ed.datatype = parseSimpleType(child);
491
else if ("complexType".equals(name) && datatype == null)
493
ed.datatype = parseComplexType(child, ed);
497
// throw schema exception
504
return new Particle(minOccurs, maxOccurs, ed);
512
Integer getOccurrence(String value)
516
return new Integer(1);
518
else if ("unbounded".equals(value))
524
return new Integer(value);
528
SimpleType parseSimpleType(QName typeName)
529
throws DatatypeException
531
SimpleType type = (SimpleType) schema.types.get(typeName);
532
if (!XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(typeName.getNamespaceURI()))
534
String localName = typeName.getLocalPart();
535
return (SimpleType) typeLibrary.createDatatype(localName);
538
SimpleType parseSimpleType(Node simpleType)
539
throws DatatypeException
541
NamedNodeMap attrs = simpleType.getAttributes();
542
String typeFinal = getAttribute(attrs, "final");
543
if (typeFinal == null)
545
Node schema = simpleType.getParentNode();
546
while (schema != null && !"schema".equals(schema.getLocalName()))
548
schema = schema.getParentNode();
552
NamedNodeMap schemaAttrs = schema.getAttributes();
553
typeFinal = getAttribute(schemaAttrs, "finalDefault");
556
int typeFinality = parseSimpleTypeDerivationSet(typeFinal);
557
QName typeName = asQName(getAttribute(attrs, "name"), simpleType);
559
Set facets = new LinkedHashSet();
560
int fundamentalFacets = 0; // TODO
561
SimpleType baseType = null; // TODO
562
Annotation annotation = null;
563
// TODO use DatatypeBuilder
564
for (Node child = simpleType.getFirstChild(); child != null;
565
child = child.getNextSibling())
567
String uri = child.getNamespaceURI();
568
String name = child.getLocalName();
569
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
570
child.getNodeType() == Node.ELEMENT_NODE)
572
if ("annotation".equals(name))
574
annotation = parseAnnotation(child);
576
else if ("restriction".equals(name))
580
else if ("list".equals(name))
582
variety = SimpleType.LIST;
585
else if ("union".equals(name))
587
variety = SimpleType.UNION;
592
return new SimpleType(typeName, variety, facets, fundamentalFacets,
593
baseType, annotation);
596
Type parseComplexType(Node complexType, ElementDeclaration parent)
597
throws DatatypeException
599
NamedNodeMap attrs = complexType.getAttributes();
600
QName typeName = asQName(getAttribute(attrs, "name"), complexType);
601
boolean isAbstract = "true".equals(getAttribute(attrs, "abstract"));
602
String block = getAttribute(attrs, "block");
603
int prohibitedSubstitutions = (block == null) ?
604
schema.blockDefault :
605
parseComplexTypeBlockSet(block);
606
String final_ = getAttribute(attrs, "final");
607
int finality = (final_ == null) ?
608
schema.finalDefault :
609
parseComplexTypeDerivationSet(final_);
610
ComplexType type = new ComplexType(typeName, isAbstract,
611
prohibitedSubstitutions, finality);
612
boolean mixed = "true".equals(getAttribute(attrs, "mixed"));
613
for (Node child = complexType.getFirstChild(); child != null;
614
child = child.getNextSibling())
616
String uri = child.getNamespaceURI();
617
String name = child.getLocalName();
618
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
619
child.getNodeType() == Node.ELEMENT_NODE)
621
if ("simpleContent".equals(name))
623
parseSimpleContent(child, type);
629
type.contentType = XMLSchema.CONTENT_MIXED;
634
void parseSimpleContent(Node simpleContent, ComplexType type)
635
throws DatatypeException
637
for (Node child = simpleContent.getFirstChild(); child != null;
638
child = child.getNextSibling())
640
String uri = child.getNamespaceURI();
641
String name = child.getLocalName();
642
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
643
child.getNodeType() == Node.ELEMENT_NODE)
645
if ("annotation".equals(name))
647
type.annotations.add(parseAnnotation(child));
649
else if ("restriction".equals(name))
651
type.derivationMethod = XMLSchema.FINAL_RESTRICTION;
652
parseRestriction(child, type);
654
else if ("extension".equals(name))
656
type.derivationMethod = XMLSchema.FINAL_EXTENSION;
657
parseExtension(child, type);
663
void parseRestriction(Node restriction, ComplexType type)
664
throws DatatypeException
666
NamedNodeMap attrs = restriction.getAttributes();
667
String base = getAttribute(attrs, "base");
668
QName baseType = asQName(base, restriction);
669
SimpleType simpleType = null;
670
for (Node child = restriction.getFirstChild(); child != null;
671
child = child.getNextSibling())
673
String uri = child.getNamespaceURI();
674
String name = child.getLocalName();
675
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
676
child.getNodeType() == Node.ELEMENT_NODE)
678
if ("annotation".equals(name))
680
type.annotations.add(parseAnnotation(child));
682
else if ("simpleType".equals(name))
684
type.contentType = XMLSchema.CONTENT_SIMPLE;
685
simpleType = parseSimpleType(child);
687
else if ("minExclusive".equals(name))
690
else if ("minInclusive".equals(name))
693
else if ("maxExclusive".equals(name))
696
else if ("maxInclusive".equals(name))
699
else if ("totalDigits".equals(name))
702
else if ("fractionDigits".equals(name))
705
else if ("length".equals(name))
708
else if ("minLength".equals(name))
711
else if ("maxLength".equals(name))
714
else if ("enumeration".equals(name))
717
else if ("whiteSpace".equals(name))
720
else if ("pattern".equals(name))
723
else if ("attribute".equals(name))
726
(AttributeUse) parseAttribute(child, false);
727
schema.attributeDeclarations.put(use.declaration.name,
729
type.attributeUses.add(use);
731
else if ("attributeGroup".equals(name))
733
NamedNodeMap agAttrs = child.getAttributes();
734
String ref = getAttribute(agAttrs, "ref");
735
QName ag = asQName(ref, child);
736
type.attributeUses.add(ag);
738
else if ("anyAttribute".equals(name))
740
type.attributeWildcard = parseAnyAttribute(child);
746
void parseExtension(Node extension, ComplexType type)
747
throws DatatypeException
749
NamedNodeMap attrs = extension.getAttributes();
750
String base = getAttribute(attrs, "base");
751
QName baseType = asQName(base, extension);
752
for (Node child = extension.getFirstChild(); child != null;
753
child = child.getNextSibling())
755
String uri = child.getNamespaceURI();
756
String name = child.getLocalName();
757
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
758
child.getNodeType() == Node.ELEMENT_NODE)
760
if ("annotation".equals(name))
762
type.annotations.add(parseAnnotation(child));
764
else if ("attribute".equals(name))
767
(AttributeUse) parseAttribute(child, false);
768
schema.attributeDeclarations.put(use.declaration.name,
770
type.attributeUses.add(use);
772
else if ("attributeGroup".equals(name))
774
NamedNodeMap agAttrs = child.getAttributes();
775
String ref = getAttribute(agAttrs, "ref");
776
QName ag = asQName(ref, child);
777
type.attributeUses.add(ag);
779
else if ("anyAttribute".equals(name))
781
type.attributeWildcard = parseAnyAttribute(child);
787
AnyAttribute parseAnyAttribute(Node node)
789
NamedNodeMap attrs = node.getAttributes();
790
String namespace = getAttribute(attrs, "namespace");
791
String pc = getAttribute(attrs, "processContents");
792
int processContents = AnyAttribute.STRICT;
793
if ("lax".equals(pc))
795
processContents = AnyAttribute.LAX;
797
else if ("skip".equals(pc))
799
processContents = AnyAttribute.SKIP;
801
AnyAttribute ret = new AnyAttribute(namespace, processContents);
802
for (Node child = node.getFirstChild(); child != null;
803
child = child.getNextSibling())
805
String uri = child.getNamespaceURI();
806
String name = child.getLocalName();
807
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(uri) &&
808
child.getNodeType() == Node.ELEMENT_NODE)
810
if ("annotation".equals(name))
812
ret.annotation = parseAnnotation(child);
819
Annotation parseAnnotation(Node node)
825
private static String getAttribute(NamedNodeMap attrs, String name)
827
Node attr = attrs.getNamedItem(name);
828
return (attr == null) ? null : attr.getNodeValue();
831
private static QName asQName(String text, Node resolver)
833
QName name = QName.valueOf(text);
834
String prefix = name.getPrefix();
835
if (prefix != null && prefix.length() > 0)
837
String uri = resolver.lookupNamespaceURI(prefix);
838
name = new QName(uri, name.getLocalPart());