X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Facr%2FTypesManager.java;h=23d6042d7ac23c53a8d7a797c74cd8274e07c4f0;hb=c0342975a37c70895c2e8f6b341d790700168d7f;hp=dd2646fcd71ecb86d70da0ec56e868031124a0e0;hpb=cc1dd97ebcc32e1bd754073ad23def182f460452;p=lgpl%2Fargeo-commons.git 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..23d6042d7 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java @@ -2,14 +2,14 @@ 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.XMLConstants; import javax.xml.namespace.QName; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; @@ -22,16 +22,27 @@ import javax.xml.validation.Validator; 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.acr.NamespaceUtils; +import org.argeo.api.acr.RuntimeNamespaceContext; import org.argeo.api.cms.CmsLog; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; @@ -40,7 +51,7 @@ import org.xml.sax.SAXParseException; /** Register content types. */ class TypesManager { private final static CmsLog log = CmsLog.getLog(TypesManager.class); - private Map prefixes = new TreeMap<>(); +// private Map prefixes = new TreeMap<>(); // immutable factories private SchemaFactory schemaFactory; @@ -50,57 +61,56 @@ class TypesManager { // cached private Schema schema; - DocumentBuilderFactory documentBuilderFactory; + private DocumentBuilderFactory documentBuilderFactory; private XSModel xsModel; - private NavigableSet types; + private SortedMap> types; - private boolean validating = true; + private boolean validating = false; + + private final static boolean limited = false; public TypesManager() { - schemaFactory = SchemaFactory.newDefaultInstance(); + schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // types - types = new TreeSet<>((qn1, qn2) -> { - if (Objects.equals(qn1.getNamespaceURI(), qn2.getNamespaceURI())) {// same namespace - return qn1.getLocalPart().compareTo(qn2.getLocalPart()); - } else { - return qn1.getNamespaceURI().compareTo(qn2.getNamespaceURI()); - } - }); + types = new TreeMap<>(NamespaceUtils.QNAME_COMPARATOR); } - public synchronized void init() { -// prefixes.put(CrName.CR_DEFAULT_PREFIX, CrName.CR_NAMESPACE_URI); -// prefixes.put("basic", CrName.CR_NAMESPACE_URI); -// prefixes.put("owner", CrName.CR_NAMESPACE_URI); -// prefixes.put("posix", CrName.CR_NAMESPACE_URI); - + public void init() { for (CmsContentTypes cs : CmsContentTypes.values()) { - StreamSource source = new StreamSource(cs.getResource().toExternalForm()); - sources.add(source); - if (prefixes.containsKey(cs.getDefaultPrefix())) - throw new IllegalStateException("Prefix " + cs.getDefaultPrefix() + " is already mapped with " - + prefixes.get(cs.getDefaultPrefix())); - prefixes.put(cs.getDefaultPrefix(), cs.getNamespace()); + if (cs.getResource() != null) { + StreamSource source = new StreamSource(cs.getResource().toExternalForm()); + sources.add(source); + } + RuntimeNamespaceContext.register(cs.getNamespace(), cs.getDefaultPrefix()); } reload(); } - public synchronized void registerTypes(String defaultPrefix, String namespace, String xsdSystemId) { - if (prefixes.containsKey(defaultPrefix)) - throw new IllegalStateException( - "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix)); - prefixes.put(defaultPrefix, namespace); - - sources.add(new StreamSource(xsdSystemId)); - reload(); + public void registerTypes(String defaultPrefix, String namespace, String xsdSystemId) { +// if (prefixes.containsKey(defaultPrefix)) +// throw new IllegalStateException( +// "Prefix " + defaultPrefix + " is already mapped with " + prefixes.get(defaultPrefix)); +// prefixes.put(defaultPrefix, namespace); + RuntimeNamespaceContext.register(namespace, defaultPrefix); + + if (xsdSystemId != null) { + sources.add(new StreamSource(xsdSystemId)); + reload(); + log.debug(() -> "Registered types " + namespace + " from " + xsdSystemId); + } } 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() { @@ -109,11 +119,14 @@ class TypesManager { 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? @@ -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"); + throw new IllegalStateException("Cannot reload types", e); } } + 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(); @@ -208,13 +453,14 @@ class TypesManager { try { validator.validate(source); } catch (SAXException e) { - throw new IllegalArgumentException("Provided source is not valid", e); + log.error(source + " is not valid ", e); + // throw new IllegalArgumentException("Provided source is not valid", e); } } - public Map getPrefixes() { - return prefixes; - } +// public Map getPrefixes() { +// return prefixes; +// } public List getSources() { return sources;