/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-2015, Geomatys * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotoolkit.feature.xml.jaxb; import java.io.InputStream; import java.io.Reader; import java.math.BigInteger; import java.net.URL; import java.util.AbstractMap; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import javax.xml.namespace.QName; import org.apache.sis.feature.SingleAttributeTypeBuilder; import org.apache.sis.feature.DefaultAssociationRole; import org.apache.sis.feature.FeatureExt; import org.apache.sis.feature.builder.AttributeRole; import org.apache.sis.feature.builder.AttributeTypeBuilder; import org.apache.sis.feature.builder.CharacteristicTypeBuilder; import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.apache.sis.feature.builder.PropertyTypeBuilder; import org.apache.sis.util.ObjectConverters; import org.geotoolkit.util.NamesExt; import org.geotoolkit.feature.xml.Utils; import org.geotoolkit.xml.AbstractConfigurable; import org.geotoolkit.xsd.xml.v2001.ComplexContent; import org.geotoolkit.xsd.xml.v2001.ComplexType; import org.geotoolkit.xsd.xml.v2001.Element; import org.geotoolkit.xsd.xml.v2001.ExplicitGroup; import org.geotoolkit.xsd.xml.v2001.ExtensionType; import org.geotoolkit.xsd.xml.v2001.LocalSimpleType; import org.geotoolkit.xsd.xml.v2001.OpenAttrs; import org.geotoolkit.xsd.xml.v2001.Schema; import org.geotoolkit.xsd.xml.v2001.SimpleType; import org.geotoolkit.xsd.xml.v2001.TopLevelElement; import org.opengis.util.GenericName; import org.geotoolkit.xsd.xml.v2001.Annotated; import org.geotoolkit.xsd.xml.v2001.Any; import org.geotoolkit.xsd.xml.v2001.Attribute; import org.geotoolkit.xsd.xml.v2001.AttributeGroup; import org.geotoolkit.xsd.xml.v2001.AttributeGroupRef; import org.geotoolkit.xsd.xml.v2001.ComplexRestrictionType; import org.geotoolkit.xsd.xml.v2001.Group; import org.geotoolkit.xsd.xml.v2001.GroupRef; import org.geotoolkit.xsd.xml.v2001.NamedAttributeGroup; import org.geotoolkit.xsd.xml.v2001.NamedGroup; import org.geotoolkit.xsd.xml.v2001.Restriction; import org.geotoolkit.xsd.xml.v2001.SimpleContent; import org.geotoolkit.xsd.xml.v2001.SimpleRestrictionType; import org.geotoolkit.xsd.xml.v2001.Union; import org.opengis.feature.MismatchedFeatureException; import org.apache.sis.util.logging.Logging; import org.geotoolkit.feature.xml.GMLConvention; import org.opengis.feature.AttributeType; import org.opengis.feature.FeatureAssociationRole; import org.opengis.feature.FeatureType; import org.opengis.feature.Operation; import org.opengis.feature.PropertyType; import org.w3c.dom.Node; /** * Reader class to convert an XSD to OGC Feature Type. * * @author Guilhem Legal (Geomatys) * @author Johann Sorel (Geomatys) * @module */ public class JAXBFeatureTypeReaderOld extends AbstractConfigurable { private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.feature.xml.jaxp"); private static final String FLAG_ID = "isId"; private final Map<QName,XSDFeatureType> typeCache = new HashMap<>(); private final List<Entry<ComplexType,XSDFeatureType>> unfinished = new ArrayList<>(); private final List<XSDFeatureType> uncompleted = new ArrayList<>(); private final XSDSchemaContext xsdContext; private boolean skipStandardObjectProperties = false; public JAXBFeatureTypeReaderOld() { this(null); } /** * * @param locationMap xsd imports or resources are often online, this map allows to replace * resources locations by new locations. It can be used to relocated toward a local file to * use offline for example. */ public JAXBFeatureTypeReaderOld(Map<String,String> locationMap) { xsdContext = new XSDSchemaContext(locationMap); } public boolean isSkipStandardObjectProperties() { return skipStandardObjectProperties; } public void setSkipStandardObjectProperties(boolean skip) { this.skipStandardObjectProperties = skip; } /** * Target namespace of the primary XSD. * This value is available only after reading. */ public String getTargetNamespace() { return xsdContext.targetNamespace; } /** * {@inheritDoc } */ public List<FeatureType> read(final String xml) throws JAXBException { return read((Object)xml); } /** * {@inheritDoc } */ public List<FeatureType> read(final InputStream in) throws JAXBException { return read((Object)in); } /** * {@inheritDoc } */ public List<FeatureType> read(final URL url) throws JAXBException { return read((Object)url); } /** * {@inheritDoc } */ public List<FeatureType> read(final Reader reader) throws JAXBException { return read((Object)reader); } /** * {@inheritDoc } */ public List<FeatureType> read(final Node element) throws JAXBException { return read((Object)element); } /** * {@inheritDoc } */ public FeatureType read(final String xml, final String name) throws JAXBException { return read((Object)xml,name); } /** * {@inheritDoc } */ public FeatureType read(final InputStream in, final String name) throws JAXBException { return read((Object)in,name); } /** * {@inheritDoc } */ public FeatureType read(final Reader reader, final String name) throws JAXBException { return read((Object)reader,name); } /** * {@inheritDoc } */ public FeatureType read(final Node node, final String name) throws JAXBException { return read((Object)node,name); } public FeatureType read(final Object candidate, final String name) throws JAXBException { final Schema schema = xsdContext.read(candidate,name); return getFeatureTypeFromSchema(schema, name); } public List<FeatureType> read(final Object candidate) throws JAXBException { final Entry<Schema, String> entry = xsdContext.read(candidate); return getAllFeatureTypeFromSchema(entry.getKey(), entry.getValue()); } public List<FeatureType> getAllFeatureTypeFromSchema(final Schema schema, final String baseLocation) throws MismatchedFeatureException { final List<FeatureType> result = new ArrayList<>(); // first we look for imported xsd // NOTE : we must list and fill the knownshemas map before analyzing // some xsd have cyclic references : core -> sub1 + sub2 , sub1 -> core final List<Entry<Schema,String>> refs = new ArrayList<>(); refs.add(new AbstractMap.SimpleEntry<>(schema, baseLocation)); xsdContext.listAllSchemas(schema, baseLocation, refs); for(Entry<Schema,String> entry : refs){ listFeatureTypes(entry.getKey(), result); } //resolve all types for (int i=0,n=result.size();i<n;i++) { result.set(i, ((XSDFeatureType)result.get(i)).builder.build()); } return result; } private void listFeatureTypes(Schema schema, List<FeatureType> result) throws MismatchedFeatureException{ // then we look for feature type and groups for (OpenAttrs opAtts : schema.getSimpleTypeOrComplexTypeOrGroup()) { if(opAtts instanceof TopLevelElement){ final TopLevelElement element = (TopLevelElement) opAtts; final QName typeName = element.getType(); if (typeName != null) { final ComplexType type = xsdContext.findComplexType(typeName); if (xsdContext.isFeatureType(type)) { final XSDFeatureType ft = getType(typeName.getNamespaceURI(), type, null); result.add(ft); //if the type name is not the same as the element name, make a subtype if(!ft.getName().tip().toString().equals(element.getName())){ final GenericName name = NamesExt.create(NamesExt.getNamespace(ft.getName()), element.getName()); final XSDFeatureType renamed = new XSDFeatureType(); renamed.builder.setName(name); renamed.builder.setSuperTypes(ft); uncompleted.add(renamed); result.add(renamed); } } else if (type == null && xsdContext.findSimpleType(typeName) == null) { LOGGER.log(Level.WARNING, "Unable to find a the declaration of type {0} in schemas.", typeName.getLocalPart()); continue; } } else { LOGGER.log(Level.WARNING, "null typeName for element : {0}", element.getName()); } } } //finish all substitution types for(Entry<ComplexType,XSDFeatureType> ct : unfinished){ completeType(ct.getKey(), ct.getValue()); } unfinished.clear(); uncompleted.clear(); } public XSDFeatureType getFeatureTypeFromSchema(final Schema schema, final String name) throws MismatchedFeatureException { final TopLevelElement element = schema.getElementByName(name); if (element != null) { final QName typeName = element.getType(); if (typeName != null) { final ComplexType type = xsdContext.findComplexType(typeName); final XSDFeatureType ct = getType(typeName.getNamespaceURI(), type, null); return ct; } else { LOGGER.log(Level.WARNING, "the element:{0} has no type", name); } } return null; } public XSDFeatureType getComplexType(GenericName name) throws MismatchedFeatureException{ return getType(new QName(NamesExt.getNamespace(name), name.tip().toString())); } // public PropertyDescriptor getElementType(GenericName name) throws MismatchedFeatureException{ // final Element parentElement = findGlobalElement(new QName(NamesExt.getNamespace(name), name.tip().toString())); // return elementToAttribute(parentElement, NamesExt.getNamespace(name)).get(0); // } private XSDFeatureType getType(QName qname) throws MismatchedFeatureException{ final XSDFeatureType ct = typeCache.get(qname); if(ct!=null) return ct; final ComplexType type = xsdContext.findComplexType(qname); if(type==null){ throw new MismatchedFeatureException("Unable to find complex type for name : "+ qname); }else{ return getType(qname.getNamespaceURI(), type, null); } } private XSDFeatureType getType(String namespace, ComplexType type, String elementName) throws MismatchedFeatureException{ return getType(namespace, type, elementName, false); } private XSDFeatureType getType(String namespace, ComplexType type, String elementName, boolean delay) throws MismatchedFeatureException{ String typeName = type.getName(); if(typeName==null || typeName.isEmpty()) typeName = elementName; final QName qname = new QName(namespace, typeName); final XSDFeatureType ct = typeCache.get(qname); if(ct!=null && ct.isLock()) return ct; final XSDFeatureType finalType; if(ct!=null){ finalType = ct; }else{ final boolean isFeatureType = xsdContext.isFeatureType(type); String properName = qname.getLocalPart(); //we remove the 'Type' extension for feature types. if (isFeatureType && properName.endsWith("Type")) { properName = properName.substring(0, properName.lastIndexOf("Type")); } final GenericName ftypeName = NamesExt.create(namespace, properName); finalType = new XSDFeatureType(); finalType.builder.setName(ftypeName); finalType.builder.setAbstract(type.isAbstract()); typeCache.put(qname, finalType); } if(delay){ //this is a subsitution, we don't resolve it now otherwise it will cause //a loop and some inherited properties will be missing unfinished.add(new AbstractMap.SimpleImmutableEntry<>(type, finalType)); return finalType; } completeType(type, finalType); return finalType; } private void completeType(ComplexType type, XSDFeatureType finalType) throws MismatchedFeatureException{ if(finalType.isLock()) return; final String namespace = NamesExt.getNamespace(finalType.getName()); //read attributes final List<Annotated> atts = type.getAttributeOrAttributeGroup(); if(atts!=null){ for(Annotated att : atts){ if(att instanceof Attribute){ addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (Attribute) att)); }else if(att instanceof AttributeGroupRef){ addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (AttributeGroupRef) att)); } } } //read sequence properties addOrReplace(finalType.builder, getGroupAttributes(namespace, type.getSequence())); boolean uncomplete = false; //read complex content if defined final ComplexContent content = type.getComplexContent(); ExtensionType ext = null; if (content != null) { ext = content.getExtension(); if (ext != null) { final QName base = ext.getBase(); if(base!=null && !base.getLocalPart().equalsIgnoreCase("anytype")){ final XSDFeatureType parent = getType(base); if(parent!=null){ if(!parent.isLock()){ uncomplete = true; } //if(!Utils.GML_FEATURE_TYPES.contains(parent.getName())){ finalType.builder.setSuperTypes(parent); //erase parent properties which are not identifier //TODO do we exclude gml properties ? //} } } //read attributes final List<Annotated> attexts = ext.getAttributeOrAttributeGroup(); if(attexts!=null){ for(Annotated att : attexts){ if(att instanceof Attribute){ addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (Attribute) att)); }else if(att instanceof AttributeGroupRef){ addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (AttributeGroupRef) att)); } } } //sequence attributes addOrReplace(finalType.builder, getGroupAttributes(namespace, ext.getSequence())); } // restrictions ComplexRestrictionType restriction = content.getRestriction(); if(restriction!=null){ // final QName base = restriction.getBase(); // if(base!=null && !base.getLocalPart().equalsIgnoreCase("anytype")){ // final org.geotoolkit.feature.type.ComplexType parent = getType(base); // if(parent!=null){ // addOrReplace(finalType.getDescriptors(), parent.getDescriptors()); // if(!Utils.GML_FEATURE_TYPES.contains(parent.getName())){ // finalType.changeParent(parent); // } // } // } addOrReplace(finalType.builder, getGroupAttributes(namespace, restriction.getSequence())); } } //read simple content type if defined final SimpleContent simpleContent = type.getSimpleContent(); if(simpleContent!=null){ final ExtensionType sext = simpleContent.getExtension(); final SimpleRestrictionType restriction = simpleContent.getRestriction(); if(sext!=null){ //simple type base, it must be : this is the content of the tag <tag>XXX<tag> //it is not named, so we call it value final QName base = sext.getBase(); final PropertyType st = resolveSimpleType(base); if(st instanceof FeatureAssociationRole){ addOrReplace(finalType.builder, st); }else{ finalType.builder.addAttribute((AttributeType) st); } //read attributes final List<Annotated> attexts = sext.getAttributeOrAttributeGroup(); if(attexts!=null){ for(Annotated att : attexts){ if(att instanceof Attribute){ addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (Attribute) att)); }else if(att instanceof AttributeGroupRef){ addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (AttributeGroupRef) att)); } } } } // if(restriction!=null){ // // final QName base = restriction.getBase(); // if(base !=null){ // final ComplexType sct = xsdContext.findComplexType(base); // if(sct!=null){ // final XSDFeatureType tct = getType(namespace, sct, null); // addOrReplace(finalType.builder, tct.getProperties(true)); // }else{ // final PropertyType restType = resolveSimpleType(base); // addOrReplace(finalType.builder, atb.create(restType, NamesExt.create(namespace, Utils.VALUE_PROPERTY_NAME), 0, 1, false, null)); // } // } // // // //read attributes // final List<Annotated> attexts = restriction.getAttributeOrAttributeGroup(); // if(attexts!=null){ // for(Annotated att : attexts){ // if(att instanceof Attribute){ // addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (Attribute) att)); // }else if(att instanceof AttributeGroupRef){ // addOrReplace(finalType.builder, getAnnotatedAttributes(namespace, (AttributeGroupRef) att)); // } // } // } // } } //read choice if set final ExplicitGroup choice = type.getChoice(); if(choice != null){ final Integer minOccurs = choice.getMinOccurs(); final String maxOccurs = choice.getMaxOccurs(); final List<PropertyType> choices = getGroupAttributes(namespace, choice); for(PropertyType pd : choices){ //change the min/max occurences int maxOcc = 1; if("unbounded".equalsIgnoreCase(maxOccurs)) { maxOcc = Integer.MAX_VALUE; } else if(maxOccurs!=null){ maxOcc = Integer.parseInt(maxOccurs); } //NOTE : a choice with max occurence ? yet we must consider the limitation //of each element final PropertyType rpd; if(pd instanceof Operation){ // final OperationDescriptor od = (OperationDescriptor) pd; // rpd = new DefaultOperationDescriptor(od.getType(), od.getName(), 0, maxOcc, od.isNillable()); throw new MismatchedFeatureException("Operation in choices not supported yet."); }else if(pd instanceof FeatureAssociationRole){ final FeatureAssociationRole ref = (FeatureAssociationRole) pd; final Map properties = Collections.singletonMap("name", ref.getName()); final FeatureAssociationRole asso = new DefaultAssociationRole(properties, ref.getValueType(), 0, maxOcc); rpd = asso; }else{ final SingleAttributeTypeBuilder adb = new SingleAttributeTypeBuilder(); adb.copy((AttributeType) pd); adb.setMinimumOccurs(0); adb.setMaximumOccurs(maxOcc); rpd = adb.build(); } addOrReplace(finalType.builder, rpd); } } // removeAttributes(finalType.builder, Utils.GML_ABSTRACT_FEATURE_PROPERTIES); // // //remove standard object properties if requested // if(skipStandardObjectProperties){ // removeAttributes(finalType.builder, Utils.GML_STANDARD_OBJECT_PROPERTIES); // } if(!uncomplete){ //finalType.lock(); }else{ uncompleted.add(finalType); } } // private static void removeAttributes(FeatureTypeBuilder type, Set<GenericName> propNames){ // final List<PropertyDescriptor> descs = type.getDescriptors(); // for(int i=descs.size()-1;i>=0;i--){ // if(propNames.contains(descs.get(i).getName())){ // descs.remove(i); // } // } // } private List<PropertyType> getGroupAttributes(String namespace, Group group) throws MismatchedFeatureException { if(group==null) return Collections.EMPTY_LIST; final List<PropertyType> atts = new ArrayList<>(); final List<Object> particles = group.getParticle(); for(Object particle : particles){ if(particle instanceof JAXBElement){ particle = ((JAXBElement)particle).getValue(); } if(particle instanceof Element){ final Element ele = (Element) particle; final List<PropertyType> att = elementToAttribute(ele, namespace); if(att!=null)atts.addAll(att); }else if(particle instanceof Any){ final Any ele = (Any) particle; final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); atb.setName(namespace, Utils.ANY_PROPERTY_NAME); atb.setValueClass(Object.class); copyMinMax(ele, atb); atts.add(atb.build()); }else if(particle instanceof GroupRef){ final GroupRef ref = (GroupRef) particle; final QName groupRef = ref.getRef(); final NamedGroup ng = xsdContext.findGlobalGroup(groupRef); atts.addAll(getGroupAttributes(namespace, ng)); }else if(particle instanceof ExplicitGroup){ final ExplicitGroup eg = (ExplicitGroup) particle; atts.addAll(getGroupAttributes(namespace, eg)); }else{ throw new MismatchedFeatureException("Unexpected TYPE : "+particle); } } return atts; } private AttributeType getAnnotatedAttributes(String namespace, final Attribute att) throws MismatchedFeatureException{ final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); atb.reset(); if(att.getRef()!=null){ namespace = att.getRef().getNamespaceURI(); //copy properties from parent final Attribute attRef = xsdContext.findGlobalAttribute(att.getRef()); final AttributeType atDesc = getAnnotatedAttributes(namespace, attRef); atb.copy(atDesc); atb.setName(namespace, atDesc.getName().tip().toString()); } else { namespace = null; } final String id = att.getId(); final String name = att.getName(); final String def = att.getDefault(); final AttributeType type = (AttributeType) resolveAttributeValueName(att); final String use = att.getUse(); if(id!=null || name!=null){ //find name final String tip = ((name==null) ? id : name); final GenericName attName = NamesExt.create(namespace, "@"+ tip); atb.setName(attName); //mark identifier fields if(type.getName().tip().toString().equals("ID")){ atb.addCharacteristic(FLAG_ID, Boolean.class, 1, 1, Boolean.TRUE); } } //find min/max occurences atb.setMinimumOccurs((use==null || "optional".equals(use)) ? 0 : 1); atb.setMaximumOccurs(1); atb.addCharacteristic(GMLConvention.NILLABLE_PROPERTY, Boolean.class, 0, 1, Boolean.FALSE); atb.setValueClass(type.getValueClass()); if(def!=null && !def.isEmpty()){ final Object defVal = ObjectConverters.convert(def, type.getValueClass()); atb.setDefaultValue(defVal); } return atb.build(); } private List<PropertyType> getAnnotatedAttributes(final String namespace, final AttributeGroup group) throws MismatchedFeatureException{ final List<PropertyType> descs = new ArrayList<>(); final List<Annotated> atts = group.getAttributeOrAttributeGroup(); if(atts!=null){ for(Annotated att : atts){ if(att instanceof Attribute){ addOrReplace(descs, getAnnotatedAttributes(namespace, (Attribute) att)); }else if(att instanceof AttributeGroupRef){ addOrReplace(descs, getAnnotatedAttributes(namespace, (AttributeGroupRef)att)); } } } final QName ref = group.getRef(); if(ref!=null){ final NamedAttributeGroup refGroup = xsdContext.findAttributeGroup(ref); addOrReplace(descs, getAnnotatedAttributes(namespace, refGroup)); } return descs; } private List<PropertyType> elementToAttribute(final Element attributeElement, final String namespace) throws MismatchedFeatureException { return elementToAttribute(attributeElement, namespace, false); } /** * Convert an Element to a AttributeDescriptor * Returns a list of AttributeDescriptors, other descriptors are substitution groups. * * * @param attributeElement * @param namespace * @return * @throws SchemaException */ private List<PropertyType> elementToAttribute(final Element attributeElement, final String namespace, boolean isSubstitute) throws MismatchedFeatureException { final List<PropertyType> results = new ArrayList<>(); //search for the parent description GenericName name = null; PropertyType parentDesc = null; if (attributeElement.getRef() != null) { final Element parentElement = xsdContext.findGlobalElement(attributeElement.getRef()); if (parentElement == null) { throw new MismatchedFeatureException("unable to find referenced element : "+ attributeElement.getRef()); } final List<PropertyType> parentAtt = elementToAttribute(parentElement, namespace); for(int i=1,n=parentAtt.size();i<n;i++){ //substitution groups results.add(parentAtt.get(i)); } parentDesc = parentAtt.get(0); name = parentDesc.getName(); } final String elementName = attributeElement.getName(); if(elementName!=null){ name = NamesExt.create(namespace, elementName); } final Integer[] minMax = getMinMax(attributeElement); if(minMax[0]==null) minMax[0] = 1; if(minMax[1]==null) minMax[1] = 1; //try to extract complex type final PropertyType baseDesc; if(attributeElement.getComplexType()!=null){ final FeatureType type = getType(namespace, attributeElement.getComplexType(),attributeElement.getName(),isSubstitute); final Map properties = Collections.singletonMap("name", name); final FeatureAssociationRole asso = new DefaultAssociationRole(properties, type, minMax[0], minMax[1]); baseDesc = asso; }else{ //Simple type QName elementType = attributeElement.getType(); // Try to extract base from a SimpleType if (elementType == null && attributeElement.getSimpleType() != null) { final LocalSimpleType simpleType = attributeElement.getSimpleType(); if (simpleType.getRestriction() != null) { elementType = simpleType.getRestriction().getBase(); } } if (elementType != null) { if(Utils.isPrimitiveType(elementType)){ final Class c = Utils.getTypeFromQName(elementType); if (c == null) { throw new MismatchedFeatureException("The attribute : " + attributeElement + " does no have a declared type."); } final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); atb.setMinimumOccurs(minMax[0]); atb.setMaximumOccurs(minMax[1]); atb.setValueClass(c); atb.setName(name); baseDesc = atb.build(); }else{ final PropertyType pt = resolveSimpleType(elementType); if(pt instanceof AttributeType){ final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); atb.setMinimumOccurs(minMax[0]); atb.setMaximumOccurs(minMax[1]); atb.setName(name); atb.setDefaultValue(((AttributeType)pt).getDefaultValue()); atb.setValueClass(((AttributeType)pt).getValueClass()); baseDesc = atb.build(); }else{ final FeatureAssociationRole ref = (FeatureAssociationRole) pt; final Map properties = Collections.singletonMap("name", name); final FeatureAssociationRole asso = new DefaultAssociationRole(properties, ref.getValueType(), minMax[0], minMax[1]); baseDesc = asso; } } }else if(parentDesc instanceof FeatureAssociationRole){ final Map properties = Collections.singletonMap("name", name); final FeatureAssociationRole asso = new DefaultAssociationRole(properties, ((FeatureAssociationRole)parentDesc).getValueType(), minMax[0], minMax[1]); baseDesc = asso; }else if(parentDesc instanceof AttributeType){ final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); atb.copy((AttributeType) parentDesc); atb.setName(name); atb.setMinimumOccurs(minMax[0]); atb.setMaximumOccurs(minMax[1]); baseDesc = atb.build(); }else{ final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); atb.setName(name); atb.setMinimumOccurs(minMax[0]); atb.setMaximumOccurs(minMax[1]); atb.setValueClass(Object.class); baseDesc = atb.build(); } } results.add(baseDesc); //check for substitutions // if(elementName!=null && !isSubstitute){ // final Collection<QName> substitutions = xsdContext.getSubstitutions(new QName(namespace, elementName)); // if(substitutions!=null && !substitutions.isEmpty()){ // for(QName sub : substitutions){ // final Element subEle = xsdContext.findGlobalElement(sub); // final List<PropertyType> subs = elementToAttribute(subEle,sub.getNamespaceURI(),true); // //create an alias operator for each of them // for(PropertyType ad : subs){ // if(ad instanceof Operation){ // throw new UnsupportedOperationException("Substitution is an operation, not supported."); // }else{ // final OperationType optype = new AliasOperation(ad.getName(), baseDesc.getName(), ad); // final OperationDescriptor desc = new DefaultOperationDescriptor(optype, // ad.getName(), baseDesc.getMinOccurs(), baseDesc.getMaxOccurs(), baseDesc.isNillable()); // results.add(desc); // } // } // } // } // } return results; } /** * * @param attributeElement * @return [0] minimum values count, may be null * [1] maximum values count, may be null */ private static Integer[] getMinMax(Element attributeElement) { final Integer[] minmax = new Integer[2]; //override properties which are defined minmax[0] = attributeElement.getMinOccurs(); final String maxxAtt = attributeElement.getMaxOccurs(); if("unbounded".equalsIgnoreCase(maxxAtt)) { minmax[1] = Integer.MAX_VALUE; } else if(maxxAtt!=null){ minmax[1] = Integer.parseInt(maxxAtt); } return minmax; } private static void copyMinMax(Any attributeElement, SingleAttributeTypeBuilder adb) { //override properties which are defined final BigInteger minAtt = attributeElement.getMinOccurs(); if(minAtt!=null){ adb.setMinimumOccurs(minAtt.intValue()); } final String maxxAtt = attributeElement.getMaxOccurs(); if("unbounded".equalsIgnoreCase(maxxAtt)) { adb.setMaximumOccurs(Integer.MAX_VALUE); } else if(maxxAtt!=null){ adb.setMaximumOccurs(Integer.parseInt(maxxAtt)); } } private PropertyType resolveAttributeValueName(Attribute att) throws MismatchedFeatureException{ //test direct type final QName type = att.getType(); if(type!=null){ return resolveSimpleType(type); } //test reference final QName ref = att.getRef(); if(ref!=null){ final Attribute parentAtt = xsdContext.findGlobalAttribute(ref); if(parentAtt==null){ throw new MismatchedFeatureException("The attribute : " + ref + " has not been found."); } return resolveAttributeValueName(parentAtt); } //test local simple type final LocalSimpleType simpleType = att.getSimpleType(); if(simpleType!=null){ return resolveSimpleType(simpleType); } return null; } private PropertyType resolveSimpleType(QName name) throws MismatchedFeatureException{ //check if primitive type if(Utils.existPrimitiveType(name.getLocalPart())){ final Class valueType = Utils.getTypeFromQName(name); final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder(); atb.setName(NamesExt.create(name)); atb.setValueClass(valueType); return atb.build(); } //check if a simple type exist final SimpleType simpleType = xsdContext.findSimpleType(name); if(simpleType!=null){ return resolveSimpleType(simpleType); }else{ //could be a complex type ... for a simple content, that's not an error. xsd/xml makes no sense at all sometimes final XSDFeatureType sct = getType(name); if(sct==null){ throw new MismatchedFeatureException("Could not find type : "+name); } final Map properties = Collections.singletonMap("name", NamesExt.create(name)); return new DefaultAssociationRole(properties, sct, 1, 1); } } private PropertyType resolveSimpleType(SimpleType simpleType) throws MismatchedFeatureException{ final Restriction restriction = simpleType.getRestriction(); if(restriction!=null){ QName base = restriction.getBase(); if(base!=null){ return resolveSimpleType(base); } final LocalSimpleType localSimpleType = restriction.getSimpleType(); if(localSimpleType!=null){ return resolveSimpleType(localSimpleType); } return null; } // TODO union can be a collection of anything // collection ? array ? Object.class ? most exact type ? final Union union = simpleType.getUnion(); if(union !=null){ if(union.getMemberTypes()!=null && !union.getMemberTypes().isEmpty()){ final QName name = union.getMemberTypes().get(0); final SimpleType refType = xsdContext.findSimpleType(name); if(refType==null){ throw new MismatchedFeatureException("Could not find type : "+name); } return resolveSimpleType(refType); }else if(union.getSimpleType()!=null && !union.getSimpleType().isEmpty()){ final LocalSimpleType st = union.getSimpleType().get(0); return resolveSimpleType(st); } } //TODO list type final org.geotoolkit.xsd.xml.v2001.List list = simpleType.getList(); if(list!=null){ final QName subTypeName = list.getItemType(); if(subTypeName!=null){ final SimpleType refType = xsdContext.findSimpleType(subTypeName); if(refType!=null){ return resolveSimpleType(refType); } return resolveSimpleType(subTypeName); } final LocalSimpleType subtype = list.getSimpleType(); if(subtype!=null){ return resolveSimpleType(simpleType); } } if(Utils.existPrimitiveType(simpleType.getName())){ return resolveSimpleType(new QName(null, simpleType.getName())); }else{ return null; } } private static void addOrReplace(FeatureTypeBuilder descs, PropertyType pd){ addOrReplace(descs, Collections.singleton(pd)); } private static void addOrReplace(FeatureTypeBuilder descs, Collection<? extends PropertyType> toAdd){ for(PropertyType pd : toAdd){ List<PropertyTypeBuilder> properties = descs.properties(); loop: for(int i=0;i<properties.size();i++){ if(properties.get(i).getName().equals(pd.getName())){ //remove existing property properties.remove(i); break loop; } } //add new property if(pd instanceof AttributeType){ final AttributeTypeBuilder<?> atb = descs.addAttribute((AttributeType) pd); //copy id information if(FeatureExt.getCharacteristicValue(pd, FLAG_ID, Boolean.FALSE)){ atb.addRole(AttributeRole.IDENTIFIER_COMPONENT); for(CharacteristicTypeBuilder<?> ctb : atb.characteristics()){ if(ctb.getName().tip().toString().equals(FLAG_ID)){ atb.characteristics().remove(ctb); break; } } } }else if(pd instanceof FeatureAssociationRole){ descs.addAssociation((FeatureAssociationRole) pd); }else if(pd instanceof Operation){ throw new RuntimeException("Operation type not supported yet."); } } } private static void addOrReplace(List<PropertyType> descs, PropertyType pd){ addOrReplace(descs, Collections.singleton(pd)); } private static void addOrReplace(List<PropertyType> descs, Collection<? extends PropertyType> toAdd){ loop: for(PropertyType pd : toAdd){ for(int i=0;i<descs.size();i++){ if(descs.get(i).getName().equals(pd.getName())){ //replace existing property descs.set(i, pd); continue loop; } } //add new property descs.add(pd); } } }