/*
* 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.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
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.DefaultAssociationRole;
import org.apache.sis.feature.FeatureExt;
import org.apache.sis.feature.Features;
import org.apache.sis.feature.SingleAttributeTypeBuilder;
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.util.ObjectConverters;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.feature.xml.GMLConvention;
import org.geotoolkit.feature.xml.Utils;
import org.geotoolkit.util.NamesExt;
import org.geotoolkit.xml.AbstractConfigurable;
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.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.Group;
import org.geotoolkit.xsd.xml.v2001.GroupRef;
import org.geotoolkit.xsd.xml.v2001.LocalComplexType;
import org.geotoolkit.xsd.xml.v2001.LocalSimpleType;
import org.geotoolkit.xsd.xml.v2001.NamedGroup;
import org.geotoolkit.xsd.xml.v2001.NumFacet;
import org.geotoolkit.xsd.xml.v2001.OpenAttrs;
import org.geotoolkit.xsd.xml.v2001.Pattern;
import org.geotoolkit.xsd.xml.v2001.Restriction;
import org.geotoolkit.xsd.xml.v2001.Schema;
import org.geotoolkit.xsd.xml.v2001.SimpleContent;
import org.geotoolkit.xsd.xml.v2001.SimpleRestrictionType;
import org.geotoolkit.xsd.xml.v2001.SimpleType;
import org.geotoolkit.xsd.xml.v2001.TopLevelElement;
import org.geotoolkit.xsd.xml.v2001.Union;
import org.opengis.feature.AttributeType;
import org.opengis.feature.FeatureAssociation;
import org.opengis.feature.FeatureAssociationRole;
import org.opengis.feature.FeatureType;
import org.opengis.feature.MismatchedFeatureException;
import org.opengis.feature.PropertyType;
import org.opengis.util.GenericName;
/**
* Reader class to convert an XSD to OGC Feature Type.
*
* @author Guilhem Legal (Geomatys)
* @author Johann Sorel (Geomatys)
* @module
*/
public class JAXBFeatureTypeReader extends AbstractConfigurable {
private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.feature.xml.jaxp");
private static final String FLAG_ID = "isId";
private static final GenericName UNNAMED = NamesExt.create("unnamed");
private final XSDSchemaContext xsdContext;
private boolean skipStandardObjectProperties = false;
private final Map<GenericName,FeatureType> featureTypeCache = new HashMap<>();
public JAXBFeatureTypeReader() {
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 JAXBFeatureTypeReader(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;
}
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());
}
private 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);
}
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 BuildStack stack = new BuildStack();
final FeatureType ft = (FeatureType) getType(typeName.getNamespaceURI(), type, stack);
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 FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName(name);
ftb.setSuperTypes(ft);
final FeatureType renamed = ftb.build();
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());
}
}
}
}
private FeatureType getFeatureTypeFromSchema(Schema schema, String name){
final TopLevelElement element = schema.getElementByName(name);
final QName typeName = element.getType();
final ComplexType type = xsdContext.findComplexType(typeName);
final BuildStack stack = new BuildStack();
return (FeatureType) getType(typeName.getNamespaceURI(), type, stack);
}
/**
*
* @param qname
* @param stack
* @return FeatureType or GenericName
* @throws MismatchedFeatureException
*/
private Object getType(QName qname, BuildStack stack) throws MismatchedFeatureException {
final ComplexType type = xsdContext.findComplexType(qname);
if(type==null){
throw new MismatchedFeatureException("Unable to find complex type for name : "+ qname);
}
final GenericName name = extractFinalName(qname.getNamespaceURI(),type);
if (featureTypeCache.containsKey(name)) {
return featureTypeCache.get(name);
}
if (stack.contains(name)) {
//recursive build
return name;
}
stack.add(name);
return getType(qname.getNamespaceURI(), type, stack);
}
private Object getType(String namespaceURI, ComplexType type, BuildStack stack) {
final GenericName name = extractFinalName(namespaceURI,type);
if (featureTypeCache.containsKey(name)) {
return featureTypeCache.get(name);
}
System.out.println("GET TYPE "+name);
//read simple content type if defined
final SimpleContent simpleContent = type.getSimpleContent();
if(simpleContent!=null){
final ExtensionType sext = simpleContent.getExtension();
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 AttributeType st = (AttributeType) resolveType(base, stack);
final AttributeTypeBuilder atb = new FeatureTypeBuilder().addAttribute(st);
atb.setName(name);
//read attributes
for (PropertyType property : getAnnotatedAttributes(namespaceURI, sext.getAttributeOrAttributeGroup(), stack)) {
CharacteristicTypeBuilder cb = atb.getCharacteristic(property.getName().toString());
if (cb==null) {
atb.addCharacteristic((AttributeType) property);
} else {
//characteristic already exist
}
}
return atb.build();
}
final SimpleRestrictionType restriction = simpleContent.getRestriction();
if(restriction!=null){
final QName base = restriction.getBase();
if(base !=null){
final ComplexType sct = xsdContext.findComplexType(base);
if(sct!=null){
final AttributeType tct = (AttributeType) getType(namespaceURI, sct, stack);
final AttributeTypeBuilder atb = new FeatureTypeBuilder().addAttribute(tct);
atb.setName(name);
//read attributes
for (PropertyType property : getAnnotatedAttributes(namespaceURI, restriction.getAttributeOrAttributeGroup(), stack)) {
CharacteristicTypeBuilder cb = atb.getCharacteristic(property.getName().toString());
if (cb==null) {
atb.addCharacteristic((AttributeType) property);
} else {
//characteristic already exist
}
}
return atb.build();
}else{
// final PropertyType restType = resolveType(base, stack);
// addOrReplace(finalType.builder, atb.create(restType, NamesExt.create(namespaceURI, Utils.VALUE_PROPERTY_NAME), 0, 1, false, null));
}
}
}
throw new MismatchedFeatureException("Undefined simple type : "+name);
}
final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName(name);
//read attributes
for (PropertyType property : getAnnotatedAttributes(namespaceURI, type.getAttributeOrAttributeGroup(), stack)) {
addProperty(ftb, property);
}
//read sequence properties
for (PropertyType property : getGroupAttributes(namespaceURI, type.getSequence(), stack)) {
addProperty(ftb, property);
}
//read complex content if defined
final ComplexContent content = type.getComplexContent();
if (content != null) {
final ExtensionType extension = content.getExtension();
if (extension != null) {
final QName base = extension.getBase();
if (base!=null && !base.getLocalPart().equalsIgnoreCase("anytype")) {
final Object parent = getType(base, stack);
if (parent instanceof FeatureType) {
ftb.setSuperTypes((FeatureType)parent);
} else if(parent instanceof GenericName) {
//parent type is currently being resolved
return name;
}
}
//read attributes
for (PropertyType property : getAnnotatedAttributes(namespaceURI, extension.getAttributeOrAttributeGroup(), stack)) {
addProperty(ftb, property);
}
//read groups
for (PropertyType property : getGroupAttributes(namespaceURI, extension.getSequence(), stack)) {
addProperty(ftb, property);
}
}
}
//read choice if set
final ExplicitGroup choice = type.getChoice();
if (choice != null) {
//this is the case of gml:location
}
final FeatureType featureType = ftb.build();
featureTypeCache.put(name, featureType);
return featureType;
}
private List<PropertyType> getGroupAttributes(String namespaceURI, Group group, BuildStack stack) 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 PropertyType att = elementToAttribute(namespaceURI, ele, stack);
atts.add(att);
}else if(particle instanceof Any){
final Any ele = (Any) particle;
final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder();
atb.setName(namespaceURI, Utils.ANY_PROPERTY_NAME);
atb.setValueClass(Object.class);
//override properties which are defined
atb.setMinimumOccurs(ele.getMinOccurs() == null ? 0 : ele.getMinOccurs().intValue());
final String maxxAtt = ele.getMaxOccurs();
if("unbounded".equalsIgnoreCase(maxxAtt)) {
atb.setMaximumOccurs(Integer.MAX_VALUE);
} else if(maxxAtt!=null){
atb.setMaximumOccurs(Integer.parseInt(maxxAtt));
}
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(namespaceURI, ng, stack));
} else if (particle instanceof ExplicitGroup) {
final ExplicitGroup eg = (ExplicitGroup) particle;
atts.addAll(getGroupAttributes(namespaceURI, eg, stack));
} else {
throw new MismatchedFeatureException("Unexpected TYPE : "+particle);
}
}
return atts;
}
private PropertyType elementToAttribute(String namespaceURI, Element element, BuildStack stack) {
GenericName name = null;
final QName refName = element.getRef();
PropertyType refType = null;
if (refName != null) {
final Element parentElement = xsdContext.findGlobalElement(refName);
if (parentElement == null) {
throw new MismatchedFeatureException("unable to find referenced element : "+ refName);
}
refType = elementToAttribute(namespaceURI, parentElement, stack);
name = refType.getName();
}
//extract name
String localName = element.getName();
if (localName == null) {
localName = element.getId();
}
if (localName != null) {
//override name
name = NamesExt.create(namespaceURI, localName);
}
//extract min/max
final Integer[] minMax = getMinMax(element);
if(minMax[0]==null) minMax[0] = 1;
if(minMax[1]==null) minMax[1] = 1;
final QName typeName = element.getType();
if (typeName != null) {
final PropertyType parentType = resolveType(typeName, stack);
return reDefine(parentType, name, minMax[0], minMax[1], element.isNillable());
}
if (refType != null) {
return reDefine(refType, name, minMax[0], minMax[1], element.isNillable());
}
final LocalSimpleType simpleType = element.getSimpleType();
if (simpleType != null) {
final PropertyType restrictionType = toProperty(simpleType, stack);
return reDefine(restrictionType, name, minMax[0], minMax[1], element.isNillable());
}
final LocalComplexType complexType = element.getComplexType();
if (complexType != null) {
final FeatureTypeBuilder ftb = new FeatureTypeBuilder((FeatureType)getType(null, complexType, stack));
ftb.setName(name);
if(element.isNillable()) {
ftb.addAttribute(GMLConvention.NILLABLE_CHARACTERISTIC);
}
return new DefaultAssociationRole(Collections.singletonMap("name", name), ftb.build(), minMax[0], minMax[1]);
}
if (element.isAbstract()) {
//create an abstract feature type with nothing in it
final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName(name);
ftb.setAbstract(true);
if(element.isNillable()) {
ftb.addAttribute(GMLConvention.NILLABLE_CHARACTERISTIC);
}
return new DefaultAssociationRole(Collections.singletonMap("name", name), ftb.build(), minMax[0], minMax[1]);
} else {
throw new UnsupportedOperationException("No type defined for "+element);
}
}
/**
* Change property name, cardinality and nillability.
*
* @param type
* @param name
* @param minOcc
* @param maxOcc
* @param nillable
* @return
* @throws MismatchedFeatureException
*/
private PropertyType reDefine(PropertyType type, GenericName name, int minOcc, int maxOcc, boolean nillable) throws MismatchedFeatureException{
if (type instanceof AttributeType) {
final AttributeTypeBuilder atb = new FeatureTypeBuilder()
.addAttribute((AttributeType)type)
.setName(name)
.setMinimumOccurs(minOcc)
.setMaximumOccurs(maxOcc);
if(nillable) {
CharacteristicTypeBuilder cb = atb.getCharacteristic(GMLConvention.NILLABLE_PROPERTY.toString());
if (cb == null) cb = atb.addCharacteristic(GMLConvention.NILLABLE_CHARACTERISTIC);
cb.setDefaultValue(true);
}
return atb.build();
} else if (type instanceof FeatureAssociationRole) {
final Map properties = Collections.singletonMap("name", name);
try {
FeatureType valueType = ((FeatureAssociationRole)type).getValueType();
if (nillable) {
final FeatureTypeBuilder ftb = new FeatureTypeBuilder(valueType);
ftb.addAttribute(GMLConvention.NILLABLE_CHARACTERISTIC);
valueType = ftb.build();
}
return new DefaultAssociationRole(properties,valueType, minOcc, maxOcc);
} catch (IllegalStateException ex) {
return new DefaultAssociationRole(properties,
Features.getValueTypeName(type), minOcc, maxOcc);
}
} else {
throw new UnsupportedOperationException("Unexpected type "+type.getClass());
}
}
private List<PropertyType> getAnnotatedAttributes(String namespaceURI, final List<Annotated> atts, BuildStack stack) throws MismatchedFeatureException{
if (atts != null) {
final List<PropertyType> props = new ArrayList<>();
for (Annotated att : atts) {
if (att instanceof Attribute) {
props.add(getAnnotatedAttributes(namespaceURI, (Attribute) att, stack));
} else if(att instanceof AttributeGroupRef) {
props.addAll(getAnnotatedAttributes(namespaceURI, (AttributeGroupRef) att, stack));
}
}
return props;
} else {
return Collections.EMPTY_LIST;
}
}
private AttributeType getAnnotatedAttributes(String namespace, final Attribute att, BuildStack stack) throws MismatchedFeatureException{
final SingleAttributeTypeBuilder atb = new SingleAttributeTypeBuilder();
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, stack);
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, stack);
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.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 namespaceURI, final AttributeGroup group, BuildStack stack) throws MismatchedFeatureException{
// final QName ref = group.getRef();
// if (ref != null) {
// final NamedAttributeGroup refGroup = xsdContext.findAttributeGroup(ref);
// addOrReplace(descs, getAnnotatedAttributes(namespaceURI, refGroup));
// }
final List<PropertyType> descs = new ArrayList<>();
final List<Annotated> atts = group.getAttributeOrAttributeGroup();
if (atts != null) {
for (Annotated att : atts) {
if (att instanceof Attribute) {
descs.add(getAnnotatedAttributes(namespaceURI, (Attribute) att, stack));
} else if (att instanceof AttributeGroupRef) {
descs.addAll(getAnnotatedAttributes(namespaceURI, (AttributeGroupRef) att, stack));
}
}
}
return descs;
}
private PropertyType resolveAttributeValueName(Attribute att, BuildStack stack) throws MismatchedFeatureException{
//test direct type
final QName type = att.getType();
if (type != null) {
return resolveType(type, stack);
}
//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, stack);
}
//test local simple type
final LocalSimpleType simpleType = att.getSimpleType();
if(simpleType!=null){
return toProperty(simpleType, stack);
}
return null;
}
/**
*
* @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 PropertyType resolveType(QName name, BuildStack stack) 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 toProperty(simpleType, stack);
}else{
//could be a complex type ... for a simple content, that's not an error. xsd/xml makes no sense at all sometimes
final Object sct = getType(name, stack);
final Map properties = Collections.singletonMap("name", NamesExt.create(name));
if (sct==null) {
throw new MismatchedFeatureException("Could not find type : "+name);
} else if(sct instanceof GenericName) {
return new DefaultAssociationRole(properties, (GenericName)sct, 1, 1);
} else if(sct instanceof FeatureType) {
return new DefaultAssociationRole(properties, (FeatureType)sct, 1, 1);
} else if(sct instanceof AttributeType) {
return (AttributeType)sct;
} else {
throw new MismatchedFeatureException("Unexpected type "+sct);
}
}
}
private PropertyType toProperty(SimpleType simpleType, BuildStack stack) throws MismatchedFeatureException{
final Restriction restriction = simpleType.getRestriction();
if (restriction != null) {
QName base = restriction.getBase();
AttributeType baseType = null;
if (base != null) {
baseType = (AttributeType) resolveType(base, stack);
}
final LocalSimpleType localSimpleType = restriction.getSimpleType();
if (localSimpleType != null) {
baseType = (AttributeType) toProperty(localSimpleType, stack);
}
if (baseType != null) {
final AttributeTypeBuilder atb = new FeatureTypeBuilder().addAttribute(baseType);
for (Object facet : restriction.getFacets()) {
if(facet instanceof JAXBElement) {
String name = ((JAXBElement)facet).getName().getLocalPart();
facet = ((JAXBElement)facet).getValue();
if(facet instanceof NumFacet){
final NumFacet nf = (NumFacet) facet;
final int length = Integer.valueOf(nf.getValue());
if("maxLength".equalsIgnoreCase(name)){
atb.setMaximalLength(length);
}
}
} else if(facet instanceof Pattern) {
//TODO
}
}
return atb.build();
}
}
// 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 toProperty(refType, stack);
}else if(union.getSimpleType()!=null && !union.getSimpleType().isEmpty()){
final LocalSimpleType st = union.getSimpleType().get(0);
return toProperty(st, stack);
}
}
//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 toProperty(refType, stack);
}
return resolveType(subTypeName, stack);
}
final LocalSimpleType subtype = list.getSimpleType();
if(subtype!=null){
return toProperty(simpleType, stack);
}
}
if(Utils.existPrimitiveType(simpleType.getName())){
return resolveType(new QName(null, simpleType.getName()), stack);
}else{
return null;
}
}
/**
* Add property to feature type builder.
* This methods skips standard object properties, reset identifier parameters
* and unroll propertytypes.
*
* @param ftb
* @param property
*/
private void addProperty(FeatureTypeBuilder ftb, PropertyType property) {
if (skipStandardObjectProperties &&
(Utils.GML_ABSTRACT_FEATURE_PROPERTIES.contains(property.getName()) ||
Utils.GML_STANDARD_OBJECT_PROPERTIES.contains(property.getName()))
) {
return;
}
if (property instanceof AttributeType) {
final AttributeTypeBuilder atb = ftb.addAttribute((AttributeType)property);
if (FeatureExt.getCharacteristicValue(property, FLAG_ID, false)){
//special case, consider it as a property
atb.addRole(AttributeRole.IDENTIFIER_COMPONENT);
atb.characteristics().clear();
}
} else if (property instanceof FeatureAssociationRole) {
final FeatureAssociationRole far = (FeatureAssociationRole) property;
if (Features.getValueTypeName(far).tip().toString().endsWith("PropertyType")) {
//this is an encapsulated property, we unroll it
final FeatureType valueType = far.getValueType();
final Collection<? extends PropertyType> subProps = valueType.getProperties(true);
if (subProps.size()>=1) {
//we peek the first association, there should be only one
//but attributes are possible
for (PropertyType pt : subProps) {
if (pt instanceof FeatureAssociationRole) {
final FeatureAssociationRole subFar = (FeatureAssociationRole)pt;
ftb.addAssociation(subFar)
.setName(far.getName())
.setMinimumOccurs(far.getMinimumOccurs())
.setMaximumOccurs(far.getMaximumOccurs());
break;
}
}
} else {
//ignore this property
//throw new MismatchedFeatureException("Unvalid property type, was expecting a single association property but was :\n"+valueType);
}
} else {
ftb.addProperty(property);
}
} else {
ftb.addProperty(property);
}
}
private GenericName extractFinalName(String namespaceURI, ComplexType type) {
String localName = type.getName();
if(localName==null) localName = type.getId();
if(localName==null && namespaceURI==null) return UNNAMED;
//we remove the 'Type' extension for feature types.
GenericName name;
final boolean isFeatureType = xsdContext.isFeatureType(type);
if (isFeatureType && localName.endsWith("Type")) {
name = NamesExt.create(namespaceURI, localName.substring(0,localName.length()-4));
} else{
name = NamesExt.create(namespaceURI, localName);
}
return name;
}
/**
* Stores the names of types being created at this time.
*
*/
private static final class BuildStack extends HashSet<GenericName> {
final Set<QName> toResolve = new HashSet<>();
}
}