/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2016, 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 java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import org.apache.sis.util.ArgumentChecks; import org.opengis.feature.AttributeType; import org.opengis.feature.Feature; import org.opengis.feature.FeatureAssociationRole; import org.opengis.feature.FeatureInstantiationException; import org.opengis.feature.FeatureType; import org.opengis.feature.Operation; import org.opengis.feature.PropertyNotFoundException; import org.opengis.feature.PropertyType; import org.opengis.util.GenericName; import org.opengis.util.InternationalString; import org.opengis.util.ScopedName; /** * FeatureType implementation which define a filtered view of a reference * feature type. * * * @author Johann Sorel (Geomatys) * @module */ public class ViewFeatureType implements DecoratedFeatureType { private static final PropertyType AMBIGUOUS = new DefaultAttributeType(Collections.singletonMap("name", "ambiguous"),Object.class,0,0,null); private final FeatureType base; private final Set<GenericName> fullNames = new HashSet<>(); private final Map<String,PropertyType> names = new HashMap<>(); private boolean isSimple; /** * Filter feature type properties. * * @param base reference feature type * @param propertyNames properties to include in the feature type view */ public ViewFeatureType(FeatureType base, String ... propertyNames) { this(base, new HashSet<>(Arrays.asList(propertyNames))); } /** * Filter feature type properties. * * @param base reference feature type * @param propertyNames properties to include in the feature type view */ public ViewFeatureType(FeatureType base, Set<String> propertyNames) { ArgumentChecks.ensureNonNull("type", base); this.base = base; //NOTE : copied and modified from DefaultFeatureType.computeTransientFields isSimple = true; for (String pname : propertyNames) { PropertyType property = base.getProperty(pname); final int minimumOccurs, maximumOccurs; if (property instanceof AttributeType<?>) { minimumOccurs = ((AttributeType<?>) property).getMinimumOccurs(); maximumOccurs = ((AttributeType<?>) property).getMaximumOccurs(); isSimple &= (minimumOccurs == maximumOccurs); } else if (property instanceof FeatureAssociationRole) { minimumOccurs = ((FeatureAssociationRole) property).getMinimumOccurs(); maximumOccurs = ((FeatureAssociationRole) property).getMaximumOccurs(); isSimple = false; } else { minimumOccurs = 0; maximumOccurs = 0; } if (maximumOccurs != 0) { isSimple &= (maximumOccurs == 1); } //replace operations by ViewOperation if(property instanceof Operation){ property = new DecoratedOperation((Operation) property); } final GenericName fullName = property.getName(); fullNames.add(fullName); GenericName name = fullName; names.put(name.toString(), property); while (name instanceof ScopedName) { name = ((ScopedName)name).tail(); if(names.containsKey(name.toString())){ //name is ambigus names.put(name.toString(), AMBIGUOUS); break; }else{ names.put(name.toString(), property); } } //name tip part if(!names.containsKey(name.toString())){ names.put(name.toString(), property); } } } @Override public FeatureType getDecoratedType() { return base; } /** * Redirect to wrapped feature type. * * @return name */ @Override public GenericName getName() { return base.getName(); } /** * Redirect to wrapped feature type. * * @return abstract */ @Override public boolean isAbstract() { return base.isAbstract(); } /** * * @return true if type is simple */ @Override public boolean isSimple() { return isSimple; } /** * Redirect to wrapped feature type. * * @return definition */ @Override public InternationalString getDefinition() { return base.getDefinition(); } /** * Redirect to wrapped feature type. * * @return designation */ @Override public InternationalString getDesignation() { return base.getDesignation(); } /** * Redirect to wrapped feature type. * * @return description */ @Override public InternationalString getDescription() { return base.getDescription(); } @Override public PropertyType getProperty(String name) throws PropertyNotFoundException { final PropertyType type = names.get(name); if (type==null) { throw new PropertyNotFoundException("No property for name "+name); }else if (type==AMBIGUOUS) { throw new PropertyNotFoundException("Ambiguous name "+name); } return type; } @Override public Collection<? extends PropertyType> getProperties(boolean includeSuperTypes) { final Collection<PropertyType> properties = new ArrayList<>(); final Collection<? extends PropertyType> basePropertiers = base.getProperties(includeSuperTypes); for (PropertyType pt : basePropertiers) { if(fullNames.contains(pt.getName())){ properties.add(getProperty(pt.getName().toString())); } } return properties; } @Override public Set<? extends FeatureType> getSuperTypes() { return Collections.EMPTY_SET; } /** * NOTE : copied and modified from DefaultFeatureType.isAssignableFrom * * Returns {@code true} if this type is same or a super-type of the given type. * The check is based mainly on the feature type {@linkplain #getName() name}, which should be unique. * * <div class="note"><b>Analogy:</b> * if we compare {@code FeatureType} to {@link Class} in the Java language, then this method is equivalent * to {@link Class#isAssignableFrom(Class)}.</div> * * @param type the type to be checked. * @return {@code true} if instances of the given type can be assigned to association of this type. */ @Override public boolean isAssignableFrom(final FeatureType type) { if (type == this) { return true; // Optimization for a common case. } ArgumentChecks.ensureNonNull("type", type); return maybeAssignableFrom(this, type); } /** * NOTE : copied and modified from DefaultFeatureType.maybeAssignableFrom * * Returns {@code true} if the given base type may be the same or a super-type of the given type, using only * the name as a criterion. This is a faster check than {@link #isAssignableFrom(FeatureType)}. * * <p>Performance note: callers should verify that {@code base != type} before to invoke this method.</p> */ static boolean maybeAssignableFrom(final FeatureType base, final FeatureType type) { // Slower path for non-SIS implementations. if (Objects.equals(base.getName(), type.getName())) { return true; } for (final FeatureType superType : type.getSuperTypes()) { if (base == superType || maybeAssignableFrom(base, superType)) { return true; } } return false; } @Override public Feature newInstance() throws FeatureInstantiationException, UnsupportedOperationException { return newInstance(base.newInstance()); } public Feature newInstance(Feature base) throws FeatureInstantiationException, UnsupportedOperationException { return new ViewFeature(base); } @Override public boolean equals(Object obj) { if (obj == this) { return true; } if(obj instanceof FeatureType) { final FeatureType that = (FeatureType) obj; return isAbstract() == that.isAbstract() && getName() == that.getName() && getDefinition() == that.getDefinition() && getDescription() == that.getDescription() && getDesignation() == that.getDesignation() && getSuperTypes().equals(that.getSuperTypes()) && getProperties(false).equals(that.getProperties(false)); } return false; } @Override public String toString() { return FeatureFormat.sharedFormat(this); } private class ViewFeature extends AbstractFeature implements DecoratedFeature { private final Feature base; private ViewFeature(Feature base) { super(ViewFeatureType.this); this.base = base; } @Override public DecoratedFeatureType getType() { return ViewFeatureType.this; } @Override public Feature getDecoratedFeature() { return base; } @Override public Object getPropertyValue(String name) throws PropertyNotFoundException { final PropertyType n = names.get(name); if (n==null) { throw new PropertyNotFoundException("No property for name "+name); }else if (n==AMBIGUOUS) { throw new PropertyNotFoundException("Ambiguous name "+name); } return base.getPropertyValue(name); } @Override public void setPropertyValue(String name, Object value) throws IllegalArgumentException { final PropertyType n = names.get(name); if (n==null) { throw new PropertyNotFoundException("No property for name "+name); }else if (n==AMBIGUOUS) { throw new PropertyNotFoundException("Ambiguous name "+name); } base.setPropertyValue(name, value); } } }