/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 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.apache.sis.feature; import com.vividsolutions.jts.geom.Geometry; import java.lang.reflect.Array; import java.net.URI; import java.net.URL; import java.rmi.server.UID; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.internal.feature.ArrayFeature; import org.apache.sis.internal.feature.BiFunction; import org.apache.sis.internal.feature.FeatureLoop; import org.apache.sis.internal.feature.Predicate; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ObjectConverters; import org.apache.sis.util.Static; import org.apache.sis.util.UnconvertibleObjectException; import org.apache.sis.util.iso.DefaultNameSpace; import org.apache.sis.util.logging.Logging; import org.geotoolkit.filter.identity.DefaultFeatureId; import org.geotoolkit.parameter.Parameters; import org.opengis.coverage.grid.GridCoverage; import org.opengis.feature.Attribute; import org.opengis.feature.AttributeType; import org.opengis.feature.Feature; import org.opengis.feature.FeatureAssociation; import org.opengis.feature.FeatureAssociationRole; import org.opengis.feature.FeatureType; import org.opengis.feature.IdentifiedType; import org.opengis.feature.Operation; import org.opengis.feature.Property; import org.opengis.feature.PropertyType; import org.opengis.filter.identity.FeatureId; import org.opengis.geometry.Envelope; import org.opengis.metadata.maintenance.ScopeCode; import org.opengis.metadata.quality.ConformanceResult; import org.opengis.metadata.quality.DataQuality; import org.opengis.metadata.quality.Element; import org.opengis.metadata.quality.Result; import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterNotFoundException; import org.opengis.parameter.ParameterValue; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.GenericName; import org.opengis.util.NameFactory; import org.apache.sis.internal.feature.AttributeConvention; import org.apache.sis.internal.system.DefaultFactories; import static org.apache.sis.feature.AbstractIdentifiedType.NAME_KEY; import org.apache.sis.feature.builder.FeatureTypeBuilder; /** * NOTE : merge with Apache SIS 'org.apache.sis.feature.Features' class. * * @author Johann Sorel (Geomatys) */ public final class FeatureExt extends Static { /** * Default attribute type names separator. */ public static final String SEPARATOR = String.valueOf(DefaultNameSpace.DEFAULT_SEPARATOR); /** * Convention name of the feature symbolizers. * Some features may have self defined symbology, this is the case of kml,dwg,... */ public static final GenericName ATTRIBUTE_SYMBOLIZERS = DefaultFactories.forBuildin(NameFactory.class) .createLocalName(AttributeConvention.IDENTIFIER_PROPERTY.scope(), "@symbolizers"); /** * Method for creating feature id's when none is specified. */ public static String createDefaultFeatureId() { // According to GML and XML schema standards, FID is a XML ID // (http://www.w3.org/TR/xmlschema-2/#ID), whose acceptable values are those that match an // NCNAME production (http://www.w3.org/TR/1999/REC-xml-names-19990114/#NT-NCName): // NCName ::= (Letter | '_') (NCNameChar)* /* An XML Name, minus the ":" */ // NCNameChar ::= Letter | Digit | '.' | '-' | '_' | CombiningChar | Extender // We have to fix the generated UID replacing all non word chars with an _ (it seems // they area all ":") //return "fid-" + NON_WORD_PATTERN.matcher(new UID().toString()).replaceAll("_"); // optimization, since the UID toString uses only ":" and converts long and integers // to strings for the rest, so the only non word character is really ":" return "fid-" + new UID().toString().replace(':', '_'); } /** * Validate feature state. * This method is a shortcut to loop on feature dataquality results. * If one ConformanceResult is false then an IllegalArgumentException is throw. * Otherwise the function return doing nothing. * * @param feature * @throws IllegalArgumentException */ public static void isValid(Feature feature) throws IllegalArgumentException { //Get data quality of the feature final DataQuality quality; if(feature instanceof AbstractFeature){ quality = ((AbstractFeature)feature).quality(); }else{ //use default validator final Validator v = new Validator(ScopeCode.FEATURE); final FeatureType type = feature.getType(); for (final PropertyType pt : type.getProperties(true)) { final Property property = feature.getProperty(pt.getName().toString()); final DataQuality pq; if (property instanceof AbstractAttribute<?>) { pq = ((AbstractAttribute<?>) property).quality(); } else if (property instanceof AbstractAssociation) { pq = ((AbstractAssociation) property).quality(); } else { continue; } if (pq != null) { // Should not be null, but let be safe. v.quality.getReports().addAll(pq.getReports()); } } quality = v.quality; } //loop on quality elements and check conformance results boolean valid = true; search: for(Element element : quality.getReports()){ for(Result result : element.getResults()){ //NOTE : other type of result are ignored for now // other results may requiere threshold and other informations // to be evaluated if(result instanceof ConformanceResult){ final Boolean pass = ((ConformanceResult)result).pass(); if(Boolean.FALSE.equals(pass)){ valid = false; break search; } } } } if(!valid){ throw new IllegalArgumentException(quality.toString()); } } /** * Create a new array feature. * * @param type simple FeatureType * @return ArrayFeature */ public static ArrayFeature newArrayInstance(FeatureType type){ return new DefaultArrayFeature((DefaultFeatureType) type); } /** * Build a FeatureId for given feature. * * This method expect the feature to have the IDENTIFIER_PROPERTY field. * * @param feature * @return FeatureId */ public static FeatureId getId(Feature feature) { return new DefaultFeatureId(String.valueOf(feature.getPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString()))); } public static void setId(Feature feature, FeatureId id) { feature.setPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString(), id.getID()); } /** * Extract feature envelope. * The envelope is based on the default geometry only. * * @param feature * @return Envelope, can be null if there is no default geometry */ public static Envelope getEnvelope(Feature feature) { GeneralEnvelope bounds = null; for (final PropertyType pt : feature.getType().getProperties(true)) { if (AttributeConvention.isGeometryAttribute(pt)) { final Object val = feature.getPropertyValue(pt.getName().toString()); if (val instanceof Geometry) { final Geometry geom = (Geometry) val; final com.vividsolutions.jts.geom.Envelope env = geom.getEnvelopeInternal(); if (env != null && !env.isNull()) { // extract geometry enveloppe // TODO: take CRS in account. CoordinateReferenceSystem crs = FeatureExt.getCRS(pt); final GeneralEnvelope genv; if (crs!=null) { genv = new GeneralEnvelope(crs); } else { genv = new GeneralEnvelope(2); } genv.setRange(0, env.getMinX(), env.getMaxX()); genv.setRange(1, env.getMinY(), env.getMaxY()); if (bounds == null) { bounds = genv; } else { bounds.add(genv); } } } } } return bounds; } /** * Loop on properties, returns true if there is at least one geometry property. * * @param type * @return true if type has a geometry. */ public static boolean hasAGeometry(FeatureType type){ for (PropertyType pt : type.getProperties(true)){ if (AttributeConvention.isGeometryAttribute(pt)) return true; } return false; } /** * Extract default geometry property crs. * * @param type * @return CoordinateReferenceSystem or null */ public static CoordinateReferenceSystem getCRS(FeatureType type){ try { final IdentifiedType prop = type.getProperty(AttributeConvention.GEOMETRY_PROPERTY.toString()); if (prop instanceof PropertyType) { return getCRS((PropertyType) prop); } else { return null; } } catch (IllegalArgumentException ex) { //no default geometry property return null; } } /** * Extract CRS characteristic if it exist. * * @param type * @return CoordinateReferenceSystem or null */ public static CoordinateReferenceSystem getCRS(PropertyType type){ return getCharacteristicValue(type, AttributeConvention.CRS_CHARACTERISTIC.toString(), null); } /** * Extract characteristic value if it exist. * * @param <T> expected value class * @param type base type to search in * @param charName characteristic name * @param defaulValue default value if characteristic is missing or null. * @return characteristic value or default value is not found */ public static <T> T getCharacteristicValue(PropertyType type, String charName, T defaulValue){ while(type instanceof Operation){ type = (PropertyType) ((Operation)type).getResult(); } if(type instanceof AttributeType){ final AttributeType at = (AttributeType) ((AttributeType)type).characteristics().get(charName); if(at!=null){ T val = (T) at.getDefaultValue(); return val==null ? defaulValue : val; } } return defaulValue; } /** * Extract field lengh characteristic if it exist. * * @param type * @return Length or null */ public static Integer getLengthCharacteristic(AttributeType type){ final AttributeType at = (AttributeType) type.characteristics().get(AttributeConvention.MAXIMAL_LENGTH_CHARACTERISTIC.toString()); if(at!=null){ return (Integer) at.getDefaultValue(); } return null; } /** * Get AttributeType or FeatureAssociationRole of the given property. * * @param property * @return */ public static PropertyType getType(Property property){ if(property instanceof Attribute){ return ((Attribute)property).getType(); }else { return ((FeatureAssociation)property).getRole(); } } /** * Copy values from first to second feature. * This method will only copy values which exist in both types. * * @param feature * @param copy * @param deep make a deep copy of property values */ public static void copy(Feature feature, Feature copy, boolean deep){ final FeatureType baseType = feature.getType(); final FeatureType copyType = copy.getType(); final Collection<? extends PropertyType> props = copyType.getProperties(true); for(PropertyType pt : props){ final GenericName gname = pt.getName(); String name; PropertyType bt; try{ name = gname.toString(); bt = baseType.getProperty(name); }catch(IllegalArgumentException ex){ //property does not exist in base type try{ name = gname.tip().toString(); bt = baseType.getProperty(name); }catch(IllegalArgumentException e){ //property does not exist in base type continue; } } if(pt instanceof AttributeType){ Object val = feature.getPropertyValue(name); if(val!=null){ if(deep) val = deepCopy(val); copy.setPropertyValue(name, ObjectConverters.convert(val,((AttributeType) pt).getValueClass())); } }else if(pt instanceof FeatureAssociationRole){ Object val = feature.getPropertyValue(name); if(val instanceof Collection){ final Collection col = (Collection) val; final Collection cpCol = new ArrayList(col.size()); for(Iterator ite=col.iterator();ite.hasNext();){ Feature f = (Feature)ite.next(); if(deep) f = deepCopy(f); cpCol.add(f); } copy.setPropertyValue(name, cpCol); }else if(val!=null){ if(deep) val = deepCopy(val); copy.setPropertyValue(name, val); } } } } /** * Create a copy of given feature. * This is not a deep copy, only the feature and associated feature are copied, * values are not copied. * * @param feature * @return */ public static Feature copy(Feature feature){ return copy(feature, false); } /** * Make a deep copy of given Feature. * * @param feature Feature to copy * @return Deep copy of the feature */ public static Feature deepCopy(Feature feature){ return copy(feature, true); } /** * * @param feature * @param deep true for a deep copy * @return */ private static Feature copy(Feature feature, boolean deep){ final FeatureType type = feature.getType(); if (type instanceof DecoratedFeatureType) { final DecoratedFeatureType decoratingType = (DecoratedFeatureType)type; Feature decoratedFeature = ((DecoratedFeature)feature).getDecoratedFeature(); decoratedFeature = deepCopy(decoratedFeature); return decoratingType.newInstance(decoratedFeature); } else { final Feature cp = type.newInstance(); final Collection<? extends PropertyType> props = type.getProperties(true); for (PropertyType pt : props) { if (pt instanceof AttributeType ){ final String name = pt.getName().toString(); final Object val = feature.getPropertyValue(name); if(val!=null){ cp.setPropertyValue(name, deep ? deepCopy(val) : val); } } else if(pt instanceof FeatureAssociationRole) { final String name = pt.getName().toString(); final Object val = feature.getPropertyValue(name); if (deep) { if(val!=null){ cp.setPropertyValue(name, deepCopy(val)); } } else { if(val instanceof Collection){ final Collection col = (Collection) val; final Collection cpCol = new ArrayList(col.size()); for(Iterator ite=col.iterator();ite.hasNext();){ cpCol.add(copy((Feature)ite.next())); } cp.setPropertyValue(name, cpCol); }else if(val!=null){ cp.setPropertyValue(name, copy((Feature)val)); } } } } return cp; } } /** * Make a copy of given object. * Multiplace cases are tested to make a deep copy. * * @param candidate * @return copied object */ public static Object deepCopy(final Object candidate) { if(candidate==null) return null; if(candidate instanceof String || candidate instanceof Number || candidate instanceof URL || candidate instanceof URI || candidate.getClass().isPrimitive() || candidate instanceof Character || candidate instanceof GridCoverage){ //we consider those immutable return candidate; }else if(candidate instanceof Feature){ return deepCopy((Feature)candidate); }else if(candidate instanceof org.geotoolkit.util.Cloneable){ return ((org.geotoolkit.util.Cloneable)candidate).clone(); }else if(candidate instanceof Geometry){ return ((Geometry)candidate).clone(); }else if(candidate instanceof Date){ return ((Date)candidate).clone(); }else if(candidate instanceof Date){ return ((Date)candidate).clone(); }else if(candidate instanceof Object[]){ final Object[] array = (Object[])candidate; final Object[] copy = new Object[array.length]; for (int i = 0; i < array.length; i++) { copy[i] = deepCopy(array[i]); } return copy; }else if(candidate instanceof List){ final List list = (List)candidate; final int size = list.size(); final List cp = new ArrayList(size); for(int i=0;i<size;i++){ cp.add(deepCopy(list.get(i))); } return cp; }else if (candidate instanceof Map) { final Map map = (Map)candidate; final Map cp = new HashMap(map.size()); for(final Iterator<Map.Entry> ite=map.entrySet().iterator(); ite.hasNext();) { final Map.Entry entry = ite.next(); cp.put(entry.getKey(), deepCopy(entry.getValue())); } return Collections.unmodifiableMap(cp); } //array type final Class clazz = candidate.getClass(); if(clazz.isArray()){ final Class compClazz = clazz.getComponentType(); final int length = Array.getLength(candidate); final Object cp = Array.newInstance(compClazz, length); if(compClazz.isPrimitive()){ System.arraycopy(candidate, 0, cp, 0, length); }else{ for(int i=0;i<length; i++){ Array.set(cp, i, deepCopy(Array.get(candidate, i))); } } return cp; } //could not copy return candidate; } /** * Get default geometry property. * * @param type FeatureType * @return geometry AttributeType or null */ public static AttributeType<?> getDefaultGeometryAttribute(FeatureType type){ try{ PropertyType prop = type.getProperty(AttributeConvention.GEOMETRY_PROPERTY.toString()); if(prop instanceof LinkOperation){ prop = type.getProperty( ((LinkOperation)prop).referentName); } if(prop instanceof AttributeType){ return (AttributeType<?>) prop; } }catch(IllegalArgumentException ex){ } return null; } /** * Get default geometry property value. * This method searches for the default geometry property and return it's value * if it exist. * * @param type FeatureType * @return geometry AttributeType or null */ public static Object getDefaultGeometryAttributeValue(Feature type){ try{ return type.getPropertyValue(AttributeConvention.GEOMETRY_PROPERTY.toString()); }catch(IllegalArgumentException ex){ return null; } } /** * Create a new feature type including only the properties in the given array. * * @param type original feature type * @param propertyNames properies to preserve * @return Reduced feature type */ public static FeatureType createSubType(FeatureType type, String ... propertyNames) throws IllegalArgumentException { final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName(type.getName()); ftb.setAbstract(type.isAbstract()); ftb.setSuperTypes(type.getSuperTypes().toArray(new FeatureType[0])); for(String name : propertyNames){ ftb.addProperty(type.getProperty(name)); } return ftb.build(); } /** * Create a new feature type including only the properties in the given array. * * @param type original feature type * @param propertyNames properies to preserve * @return Reduced feature type */ public static FeatureType createSubType(FeatureType type, GenericName ... propertyNames){ final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName(type.getName()); ftb.setAbstract(type.isAbstract()); ftb.setSuperTypes(type.getSuperTypes().toArray(new FeatureType[0])); for(GenericName name : propertyNames){ ftb.addProperty(type.getProperty(name.toString())); } return ftb.build(); } /** * Create a new feature type including only the properties in the given array * and changing the geometry type properties crs. * * @param type original feature type * @param crs change geometric properties crs * @param propertyNames properies to preserve * @return Reducedd feature type */ public static FeatureType createSubType(FeatureType type, CoordinateReferenceSystem crs, GenericName ... propertyNames){ final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName(type.getName()); ftb.setAbstract(type.isAbstract()); ftb.setSuperTypes(type.getSuperTypes().toArray(new FeatureType[0])); for(GenericName name : propertyNames){ final PropertyType pt = type.getProperty(name.toString()); if(pt instanceof AttributeType && Geometry.class.isAssignableFrom(((AttributeType)pt).getValueClass()) ){ final AttributeType qualifier = new DefaultAttributeType( Collections.singletonMap(NAME_KEY, AttributeConvention.CRS_CHARACTERISTIC), CoordinateReferenceSystem.class,1,1,crs); AttributeType at = (AttributeType) pt; at = new DefaultAttributeType(extractIdentification(pt), at.getValueClass(), at.getMinimumOccurs(), at.getMaximumOccurs(), at.getDefaultValue(), qualifier); ftb.addProperty(at); }else{ ftb.addProperty(type.getProperty(name.toString())); } } return ftb.build(); } /** * Create a transformed version of the feature type with the new CRS. * * @param type FeatureType * @param crs , new crs for geometric attributes * @return FeatureType */ public static FeatureType transform(FeatureType type, CoordinateReferenceSystem crs){ final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); ftb.setName(type.getName()); ftb.setAbstract(type.isAbstract()); ftb.setSuperTypes(type.getSuperTypes().toArray(new FeatureType[0])); for(PropertyType pt : type.getProperties(false)){ if(pt instanceof AttributeType && Geometry.class.isAssignableFrom(((AttributeType)pt).getValueClass()) ){ final AttributeType qualifier = new DefaultAttributeType( Collections.singletonMap(NAME_KEY, AttributeConvention.CRS_CHARACTERISTIC), CoordinateReferenceSystem.class,1,1,crs); AttributeType at = (AttributeType) pt; at = new DefaultAttributeType(extractIdentification(pt), at.getValueClass(), at.getMinimumOccurs(), at.getMaximumOccurs(), at.getDefaultValue(), qualifier); ftb.addProperty(at); }else{ ftb.addProperty(pt); } } return ftb.build(); } private static Map extractIdentification(PropertyType type){ final Map map = new HashMap(); map.put(DefaultFeatureType.NAME_KEY,type.getName()); map.put(DefaultFeatureType.DEFINITION_KEY,type.getDefinition()); map.put(DefaultFeatureType.DESCRIPTION_KEY,type.getDescription()); map.put(DefaultFeatureType.DESIGNATION_KEY,type.getDesignation()); return map; } //////////////////////////////////////////////////////////////////////////// // PARAMETERS API MAPPING OPERATIONS /////////////////////////////////////// //////////////////////////////////////////////////////////////////////////// /** * Convert a ComplexAttribute in a parameter. * * @param source : attributeto convert * @param desc : parameter descriptor * @return ParameterValueGroup */ public static ParameterValueGroup toParameter(final Feature source, final ParameterDescriptorGroup desc) { ArgumentChecks.ensureNonNull("source", source); ArgumentChecks.ensureNonNull("desc", desc); final ParameterValueGroup target = desc.createValue(); fill(source,target); return target; } /** * Convert a ParameterValueGroup in a feature. * * @param source : parameter to convert * @return Feature */ public static Feature toFeature(final ParameterValueGroup source) { ArgumentChecks.ensureNonNull("source", source); return toFeature(source,null); } /** * Convert a ParameterValueGroup in a feature. * * @param source : parameter to convert * @param targetType : wanted type, may contain more or less parameters. * @return Feature */ public static Feature toFeature(final ParameterValueGroup source, FeatureType targetType) { ArgumentChecks.ensureNonNull("source", source); if(targetType == null){ targetType = FeatureTypeExt.toFeatureType(source.getDescriptor()); } final Feature target = targetType.newInstance(); fill(source,target); return target; } /** * Convert a map in a feature. * * @param source * @param targetType * @return Feature */ public static Feature toFeature(final Map<String,?> source, FeatureType targetType){ final Feature feature = targetType.newInstance(); for(Entry<String,?> entry : source.entrySet()){ final String key = entry.getKey(); try{ feature.setPropertyValue(key, entry.getValue()); }catch(IllegalArgumentException ex){ //normal } } return feature; } /** * Convert a Feature in a Map of values. * * @param att : property to convert * @return Map */ public static Map<String,Object> toMap(final Feature att) { ArgumentChecks.ensureNonNull("att", att); final Map<String,Object> map = new HashMap<>(); FeatureLoop.loop(att, (Predicate)null, new BiFunction<PropertyType, Object, Object>() { @Override public Object apply(PropertyType t, Object u) { map.put(t.getName().tip().toString(), u); return u; } }); return map; } /** * Convert a ParameterValueGroup in a Map of values. * * @param source : parameter to convert * @return Map * * @deprecated Use method {@link Parameters#toMap(org.opengis.parameter.ParameterValueGroup)} instead. */ public static Map<String,Object> toMap(final ParameterValueGroup source) { ArgumentChecks.ensureNonNull("source", source); return toMap(toFeature(source)); } /** * Transform a Map in a ParameterValueGroup. * A default parameter is first created and all key found in the map * that match the descriptor will be completed. * * @param params * @param desc * @return * * @deprecated Use method {@link Parameters#toParameter(java.util.Map, org.opengis.parameter.ParameterDescriptorGroup)} instead. */ public static ParameterValueGroup toParameter(final Map<String, ?> params, final ParameterDescriptorGroup desc) { ArgumentChecks.ensureNonNull("params", params); ArgumentChecks.ensureNonNull("desc", desc); return toParameter(params, desc, true); } /** * Transform a Map in a ParameterValueGroup. * A default parameter is first created and all key found in the map * that match the descriptor will be completed. * * @param params * @param desc * @param checkMandatory : will return a parameter only if all mandatory values * have been found in the map. * @return * * @deprecated Use method {@link Parameters#toParameter(java.util.Map, org.opengis.parameter.ParameterDescriptorGroup, boolean)} instead. */ public static ParameterValueGroup toParameter(final Map<String, ?> params, final ParameterDescriptorGroup desc, final boolean checkMandatory) { ArgumentChecks.ensureNonNull("params", params); ArgumentChecks.ensureNonNull("desc", desc); if(checkMandatory){ for(GeneralParameterDescriptor de : desc.descriptors()){ if(de.getMinimumOccurs()>0 && !(params.containsKey(de.getName().getCode()))){ //a mandatory parameter is not present return null; } } } final ParameterValueGroup parameter = desc.createValue(); for(final Entry<String, ?> entry : params.entrySet()){ final GeneralParameterDescriptor subdesc; try{ subdesc = desc.descriptor(entry.getKey()); }catch(ParameterNotFoundException ex){ //do nothing, the map may contain other values for other uses continue; } if(!(subdesc instanceof ParameterDescriptor)){ //we can not recreate value groups continue; } final ParameterDescriptor pd = (ParameterDescriptor) subdesc; final ParameterValue param; try{ param = Parameters.getOrCreate(pd,parameter); }catch(ParameterNotFoundException ex){ //do nothing, the map may contain other values for other uses continue; } Object val = entry.getValue(); try { val = ObjectConverters.convert(val, pd.getValueClass()); param.setValue(val); } catch (UnconvertibleObjectException e) { Logging.recoverableException(Logging.getLogger("org.apache.sis"), FeatureExt.class, "toParameter", e); // TODO - do we really want to ignore? } } return parameter; } /** * Build a {@link Property} from a {@link ParameterValue}. * @param parameter {@link ParameterValue} * @return a {@link Property} */ public static Property toProperty (final ParameterValue parameter) { final ParameterDescriptor descriptor = parameter.getDescriptor(); final Object value = parameter.getValue(); final AttributeType at = (AttributeType) FeatureTypeExt.toPropertyType(descriptor); final Attribute property = at.newInstance(); property.setValue(value); return property; } private static void fill(final ParameterValueGroup source, final Feature target){ final ParameterDescriptorGroup paramdesc = source.getDescriptor(); for(final PropertyType desc : target.getType().getProperties(true)){ if(desc instanceof FeatureAssociationRole){ final FeatureAssociationRole assRole = (FeatureAssociationRole) desc; try{ final List<ParameterValueGroup> groups = source.groups(desc.getName().tip().toString()); if(groups != null){ for(ParameterValueGroup gr : groups){ final FeatureAssociation att = assRole.newInstance(); final Feature val = assRole.getValueType().newInstance(); att.setValue(val); fill(gr,val); target.setProperty(att); } } }catch(Exception ex){ //parameter might not exist of might be a group } }else if(desc instanceof AttributeType){ final AttributeType at = (AttributeType) desc; final String code = desc.getName().tip().toString(); final GeneralParameterValue gpv = searchParameter(source, code); if(gpv instanceof ParameterValue){ target.setPropertyValue(code, ((ParameterValue)gpv).getValue()); } } } } private static void fill(final Feature source, final ParameterValueGroup target){ for(final GeneralParameterDescriptor gpd : target.getDescriptor().descriptors()){ final Property property = source.getProperty(gpd.getName().getCode()); if(gpd instanceof ParameterDescriptor){ final ParameterDescriptor desc = (ParameterDescriptor) gpd; for(final Object v : ((Attribute)property).getValues()){ Parameters.getOrCreate(desc, target).setValue(v); } }else if(gpd instanceof ParameterDescriptorGroup){ final ParameterDescriptorGroup desc = (ParameterDescriptorGroup) gpd; final FeatureAssociation asso = (FeatureAssociation) property; for(Feature prop : asso.getValues()){ ParameterValueGroup subGroup = null; if (desc.getMaximumOccurs() != 1) { subGroup = target.addGroup(desc.getName().getCode()); } else { if (desc.getMinimumOccurs() == 1) { subGroup = target.groups(desc.getName().getCode()).get(0); } if( subGroup == null) { subGroup = target.addGroup(desc.getName().getCode()); } } fill(prop,subGroup); } } } } /** * Equivalent to Parameters.getOrCreate but doesn't create the value if it does not exist. * * @param group * @param code * @return GeneralParameterValue */ public static GeneralParameterValue searchParameter(final ParameterValueGroup group, final String code){ ArgumentChecks.ensureNonNull("group", group); for(GeneralParameterValue param : group.values()){ if(param instanceof ParameterValue){ final ParameterValue pv = (ParameterValue) param; if(pv.getDescriptor().getName().getCode().equals(code)){ return pv; } }else if(param instanceof ParameterValueGroup){ final ParameterValueGroup pvg = (ParameterValueGroup) param; if(pvg.getDescriptor().getName().getCode().equals(code)){ return pvg; } } } return null; } /** * Check that given feature types have got the exact same properties. It is * sort of an equality, where type name and property order are ignored. * @param first First type to compare. * @param second Second type to compare. * @param checkSuperTypes True if super types properties must be included in * the comparison, false otherwise. * @return True if both feature types contains the same properties (whatever * order theyr appear in), false otherwise. */ public static boolean sameProperties(final FeatureType first, final FeatureType second, boolean checkSuperTypes) { final Collection<? extends PropertyType> firstProperties = first.getProperties(checkSuperTypes); final Collection<? extends PropertyType> secondProperties = second.getProperties(checkSuperTypes); return firstProperties.size() == secondProperties.size() && firstProperties.containsAll(secondProperties); } }