import java.io.IOException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.NavigableSet;
import java.util.Objects;
import java.util.Set;
+import java.util.SortedMap;
import java.util.TreeMap;
-import java.util.TreeSet;
+import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xerces.impl.xs.XSImplementationImpl;
import org.apache.xerces.impl.xs.util.StringListImpl;
+import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
import org.apache.xerces.xs.StringList;
import org.apache.xerces.xs.XSAttributeDeclaration;
+import org.apache.xerces.xs.XSAttributeUse;
+import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSException;
import org.apache.xerces.xs.XSImplementation;
import org.apache.xerces.xs.XSLoader;
import org.apache.xerces.xs.XSModel;
+import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSNamedMap;
+import org.apache.xerces.xs.XSObjectList;
+import org.apache.xerces.xs.XSParticle;
+import org.apache.xerces.xs.XSSimpleTypeDefinition;
+import org.apache.xerces.xs.XSTerm;
import org.apache.xerces.xs.XSTypeDefinition;
+import org.argeo.api.acr.CrAttributeType;
import org.argeo.api.cms.CmsLog;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
// cached
private Schema schema;
- DocumentBuilderFactory documentBuilderFactory;
+ private DocumentBuilderFactory documentBuilderFactory;
private XSModel xsModel;
- private NavigableSet<QName> types;
+ private SortedMap<QName, Map<QName, CrAttributeType>> types;
private boolean validating = true;
+ private final static boolean limited = true;
+
public TypesManager() {
- schemaFactory = SchemaFactory.newDefaultInstance();
+ schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// types
- types = new TreeSet<>((qn1, qn2) -> {
+ types = new TreeMap<>((qn1, qn2) -> {
if (Objects.equals(qn1.getNamespaceURI(), qn2.getNamespaceURI())) {// same namespace
return qn1.getLocalPart().compareTo(qn2.getLocalPart());
} else {
}
public Set<QName> listTypes() {
-// TODO cache it?
- return types;
+ return types.keySet();
+ }
+
+ public Map<QName, CrAttributeType> getAttributeTypes(QName type) {
+ if (!types.containsKey(type))
+ throw new IllegalArgumentException("Unkown type");
+ return types.get(type);
}
private synchronized void reload() {
schema = schemaFactory.newSchema(sources.toArray(new Source[sources.size()]));
// document builder factory
- documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ // we force usage of Xerces for predictability
+ documentBuilderFactory = limited ? DocumentBuilderFactory.newInstance() : new DocumentBuilderFactoryImpl();
documentBuilderFactory.setNamespaceAware(true);
- documentBuilderFactory.setXIncludeAware(true);
- documentBuilderFactory.setSchema(getSchema());
- documentBuilderFactory.setValidating(validating);
+ if (!limited) {
+ documentBuilderFactory.setXIncludeAware(true);
+ documentBuilderFactory.setSchema(getSchema());
+ documentBuilderFactory.setValidating(validating);
+ }
// XS model
// TODO use JVM implementation?
xsModel = xsLoader.loadURIList(sl);
// types
- XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
- for (int i = 0; i < map.getLength(); i++) {
- XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
- QName type = new QName(eDec.getNamespace(), eDec.getName());
- types.add(type);
- }
+// XSNamedMap map = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
+// for (int i = 0; i < map.getLength(); i++) {
+// XSElementDeclaration eDec = (XSElementDeclaration) map.item(i);
+// QName type = new QName(eDec.getNamespace(), eDec.getName());
+// types.add(type);
+// }
+ collectTypes();
} catch (XSException | SAXException e) {
throw new IllegalStateException("Cannot relaod types");
}
}
+ private void collectTypes() {
+ types.clear();
+ // elements
+ XSNamedMap topLevelElements = xsModel.getComponents(XSConstants.ELEMENT_DECLARATION);
+ for (int i = 0; i < topLevelElements.getLength(); i++) {
+ XSElementDeclaration eDec = (XSElementDeclaration) topLevelElements.item(i);
+ collectElementDeclaration("", eDec);
+ }
+
+ // types
+ XSNamedMap topLevelTypes = xsModel.getComponents(XSConstants.TYPE_DEFINITION);
+ for (int i = 0; i < topLevelTypes.getLength(); i++) {
+ XSTypeDefinition tDef = (XSTypeDefinition) topLevelTypes.item(i);
+ collectType(tDef, null, null);
+ }
+
+ }
+
+ private void collectType(XSTypeDefinition tDef, String namespace, String nameHint) {
+ if (tDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
+ XSComplexTypeDefinition ctDef = (XSComplexTypeDefinition) tDef;
+ if (ctDef.getContentType() != XSComplexTypeDefinition.CONTENTTYPE_SIMPLE
+ || ctDef.getAttributeUses().getLength() > 0 || ctDef.getAttributeWildcard() != null) {
+ collectComplexType("", null, ctDef);
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + tDef.getTypeCategory());
+ }
+ }
+ }
+
+ private void collectComplexType(String prefix, QName parent, XSComplexTypeDefinition ctDef) {
+ if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE) {
+
+ // content with attributes and a string value
+
+ XSSimpleTypeDefinition stDef = ctDef.getSimpleType();
+ // QName name = new QName(stDef.getNamespace(), stDef.getName());
+ // log.warn(prefix + "Simple " + ctDef + " - " + attributes);
+// System.err.println(prefix + "Simple from " + parent + " - " + attributes);
+//
+// if (parentAttributes != null) {
+// for (QName attr : attributes.keySet()) {
+// if (!parentAttributes.containsKey(attr))
+// System.err.println(prefix + " - " + attr + " not available in parent");
+//
+// }
+// }
+
+ } else if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_ELEMENT
+ || ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_MIXED) {
+ XSParticle p = ctDef.getParticle();
+
+ collectParticle(prefix, p, false);
+ } else if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_EMPTY) {
+ // Parent only contains attributes
+// if (parent != null)
+// System.err.println(prefix + "Empty from " + parent + " - " + attributes);
+// if (parentAttributes != null) {
+// for (QName attr : attributes.keySet()) {
+// if (!parentAttributes.containsKey(attr))
+// System.err.println(prefix + " - " + attr + " not available in parent");
+//
+// }
+// }
+// log.debug(prefix + "Empty " + ctDef.getNamespace() + ":" + ctDef.getName() + " - " + attributes);
+ } else {
+ throw new IllegalArgumentException("Unsupported type " + ctDef.getTypeCategory());
+ }
+ }
+
+ private void collectParticle(String prefix, XSParticle particle, boolean multipleFromAbove) {
+ boolean orderable = false;
+
+ XSTerm term = particle.getTerm();
+
+ if (particle.getMaxOccurs() == 0) {
+ return;
+ }
+
+ boolean mandatory = false;
+ if (particle.getMinOccurs() > 0) {
+ mandatory = true;
+ }
+
+ boolean multiple = false;
+ if (particle.getMaxOccurs() > 1 || particle.getMaxOccursUnbounded()) {
+ multiple = true;
+ }
+ if (!multiple && multipleFromAbove)
+ multiple = true;
+
+ if (term.getType() == XSConstants.ELEMENT_DECLARATION) {
+ XSElementDeclaration eDec = (XSElementDeclaration) term;
+
+ collectElementDeclaration(prefix, eDec);
+ // If this particle is a wildcard (an <xs:any> )then it
+ // is converted into a node def.
+ } else if (term.getType() == XSConstants.WILDCARD) {
+ // TODO can be anything
+
+ // If this particle is a model group (one of
+ // <xs:sequence>, <xs:choice> or <xs:all>) then
+ // it subparticles must be processed.
+ } else if (term.getType() == XSConstants.MODEL_GROUP) {
+ XSModelGroup mg = (XSModelGroup) term;
+
+ if (mg.getCompositor() == XSModelGroup.COMPOSITOR_SEQUENCE) {
+ orderable = true;
+ }
+ XSObjectList list = mg.getParticles();
+ for (int i = 0; i < list.getLength(); i++) {
+ XSParticle pp = (XSParticle) list.item(i);
+ collectParticle(prefix + " ", pp, multiple);
+ }
+ }
+ }
+
+ private void collectElementDeclaration(String prefix, XSElementDeclaration eDec) {
+ QName name = new QName(eDec.getNamespace(), eDec.getName());
+ XSTypeDefinition tDef = eDec.getTypeDefinition();
+
+ XSComplexTypeDefinition ctDef = null;
+ Map<QName, CrAttributeType> attributes = new HashMap<>();
+ if (tDef.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
+ XSSimpleTypeDefinition stDef = (XSSimpleTypeDefinition) tDef;
+// System.err.println(prefix + "Simple element " + name);
+ } else if (tDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
+ ctDef = (XSComplexTypeDefinition) tDef;
+ if (ctDef.getContentType() == XSComplexTypeDefinition.CONTENTTYPE_SIMPLE
+ && ctDef.getAttributeUses().getLength() == 0 && ctDef.getAttributeWildcard() == null) {
+ XSSimpleTypeDefinition stDef = ctDef.getSimpleType();
+// System.err.println(prefix + "Simplified element " + name);
+ } else {
+ if (!types.containsKey(name)) {
+// System.out.println(prefix + "Element " + name);
+
+ XSObjectList list = ctDef.getAttributeUses();
+ for (int i = 0; i < list.getLength(); i++) {
+ XSAttributeUse au = (XSAttributeUse) list.item(i);
+ XSAttributeDeclaration ad = au.getAttrDeclaration();
+ QName attrName = new QName(ad.getNamespace(), ad.getName());
+ // Get the simple type def for this attribute
+ XSSimpleTypeDefinition std = ad.getTypeDefinition();
+ attributes.put(attrName, xsToCrType(std.getBuiltInKind()));
+// System.out.println(prefix + " - " + attrName + " = " + attributes.get(attrName));
+ }
+ // REGISTER
+ types.put(name, attributes);
+ if (ctDef != null)
+ collectComplexType(prefix + " ", name, ctDef);
+ }
+ }
+ }
+
+ }
+
+ private CrAttributeType xsToCrType(short kind) {
+ CrAttributeType propertyType;
+ switch (kind) {
+ case XSConstants.ANYSIMPLETYPE_DT:
+ case XSConstants.STRING_DT:
+ case XSConstants.ID_DT:
+ case XSConstants.ENTITY_DT:
+ case XSConstants.NOTATION_DT:
+ case XSConstants.NORMALIZEDSTRING_DT:
+ case XSConstants.TOKEN_DT:
+ case XSConstants.LANGUAGE_DT:
+ case XSConstants.NMTOKEN_DT:
+ propertyType = CrAttributeType.STRING;
+ break;
+ case XSConstants.BOOLEAN_DT:
+ propertyType = CrAttributeType.BOOLEAN;
+ break;
+ case XSConstants.DECIMAL_DT:
+ case XSConstants.FLOAT_DT:
+ case XSConstants.DOUBLE_DT:
+ propertyType = CrAttributeType.DOUBLE;
+ break;
+ case XSConstants.DURATION_DT:
+ case XSConstants.DATETIME_DT:
+ case XSConstants.TIME_DT:
+ case XSConstants.DATE_DT:
+ case XSConstants.GYEARMONTH_DT:
+ case XSConstants.GYEAR_DT:
+ case XSConstants.GMONTHDAY_DT:
+ case XSConstants.GDAY_DT:
+ case XSConstants.GMONTH_DT:
+ propertyType = CrAttributeType.DATE_TIME;
+ break;
+ case XSConstants.HEXBINARY_DT:
+ case XSConstants.BASE64BINARY_DT:
+ case XSConstants.ANYURI_DT:
+ propertyType = CrAttributeType.ANY_URI;
+ break;
+ case XSConstants.QNAME_DT:
+ case XSConstants.NAME_DT:
+ case XSConstants.NCNAME_DT:
+ // TODO support QName?
+ propertyType = CrAttributeType.STRING;
+ break;
+ case XSConstants.IDREF_DT:
+ // TODO support references?
+ propertyType = CrAttributeType.STRING;
+ break;
+ case XSConstants.INTEGER_DT:
+ case XSConstants.NONPOSITIVEINTEGER_DT:
+ case XSConstants.NEGATIVEINTEGER_DT:
+ case XSConstants.LONG_DT:
+ case XSConstants.INT_DT:
+ case XSConstants.SHORT_DT:
+ case XSConstants.BYTE_DT:
+ case XSConstants.NONNEGATIVEINTEGER_DT:
+ case XSConstants.UNSIGNEDLONG_DT:
+ case XSConstants.UNSIGNEDINT_DT:
+ case XSConstants.UNSIGNEDSHORT_DT:
+ case XSConstants.UNSIGNEDBYTE_DT:
+ case XSConstants.POSITIVEINTEGER_DT:
+ propertyType = CrAttributeType.LONG;
+ break;
+ case XSConstants.LISTOFUNION_DT:
+ case XSConstants.LIST_DT:
+ case XSConstants.UNAVAILABLE_DT:
+ propertyType = CrAttributeType.STRING;
+ break;
+ default:
+ propertyType = CrAttributeType.STRING;
+ break;
+ }
+ return propertyType;
+ }
+
public DocumentBuilder newDocumentBuilder() {
try {
DocumentBuilder dBuilder = documentBuilderFactory.newDocumentBuilder();