From: Mathieu Baudier Date: Sat, 4 Jun 2022 09:46:47 +0000 (+0200) Subject: Analyse XSD to define attribute types and list content names. X-Git-Tag: v2.3.10~205 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=8acca40eb96fef7df712f0cbf5e5ffc13d48fcbd;p=lgpl%2Fargeo-commons.git Analyse XSD to define attribute types and list content names. --- diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java b/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java index 446449ec0..5d6c57dd5 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/CrAttributeType.java @@ -112,6 +112,9 @@ public enum CrAttributeType implements ContentNameSupplier { } catch (IllegalArgumentException e) { // silent } + + // TODO support QName as a type? It would require a NamespaceContext + // see https://www.oreilly.com/library/view/xml-schema/0596002521/re91.html // default return STRING.getFormatter().parse(str); diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java index 092876d39..b416d9365 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java @@ -64,7 +64,7 @@ public class CmsContentRepository implements ProvidedRepository { typesManager.init(); Set types = typesManager.listTypes(); for (QName type : types) { - log.debug(type); + log.debug(type + " - " + typesManager.getAttributeTypes(type)); } systemSession = newSystemSession(); diff --git a/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java b/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java index dd2646fcd..f9077f0a6 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java @@ -2,13 +2,13 @@ package org.argeo.cms.acr; 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.namespace.QName; import javax.xml.parsers.DocumentBuilder; @@ -24,14 +24,22 @@ import org.apache.xerces.impl.xs.XSImplementationImpl; import org.apache.xerces.impl.xs.util.StringListImpl; 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; @@ -52,7 +60,7 @@ class TypesManager { private Schema schema; DocumentBuilderFactory documentBuilderFactory; private XSModel xsModel; - private NavigableSet types; + private SortedMap> types; private boolean validating = true; @@ -60,7 +68,7 @@ class TypesManager { schemaFactory = SchemaFactory.newDefaultInstance(); // 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 { @@ -99,8 +107,13 @@ class TypesManager { } public Set listTypes() { -// TODO cache it? - return types; + return types.keySet(); + } + + public Map getAttributeTypes(QName type) { + if (!types.containsKey(type)) + throw new IllegalArgumentException("Unkown type"); + return types.get(type); } private synchronized void reload() { @@ -129,17 +142,249 @@ class TypesManager { 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 )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 + // , or ) 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 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();