/* (c) 2014 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.feature.retype; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.geoserver.feature.RetypingFeatureCollection; import org.geotools.data.DataStore; import org.geotools.data.Query; import org.geotools.data.FeatureListener; import org.geotools.data.FeatureSource; import org.geotools.data.Query; import org.geotools.data.QueryCapabilities; import org.geotools.data.ResourceInfo; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureLocking; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.data.simple.SimpleFeatureStore; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.Name; import org.opengis.filter.Filter; /** * Renaming wrapper for a {@link FeatureSource} instance, to be used along with * {@link RetypingDataStore} */ public class RetypingFeatureSource implements SimpleFeatureSource{ SimpleFeatureSource wrapped; FeatureTypeMap typeMap; RetypingDataStore store; Map listeners = new HashMap(); /** * Builds a retyping wrapper * @param wrapped * @param targetTypeName * @param targetSchema The target schema can have a different name and less attributes than the original one * */ public static SimpleFeatureSource getRetypingSource(SimpleFeatureSource wrapped, SimpleFeatureType targetSchema) throws IOException { FeatureTypeMap map = new FeatureTypeMap(wrapped.getSchema(), targetSchema); if(wrapped instanceof SimpleFeatureLocking) { return new RetypingFeatureLocking((SimpleFeatureLocking) wrapped, map); } else if(wrapped instanceof SimpleFeatureStore) { return new RetypingFeatureStore((SimpleFeatureStore) wrapped, map); } else { return new RetypingFeatureSource(wrapped, map); } } RetypingFeatureSource(RetypingDataStore ds, SimpleFeatureSource wrapped, FeatureTypeMap typeMap) { this.store = ds; this.wrapped = wrapped; this.typeMap = typeMap; } RetypingFeatureSource(SimpleFeatureSource wrapped, final FeatureTypeMap typeMap) throws IOException { this.wrapped = wrapped; this.typeMap = typeMap; this.store = new RetypingDataStore((DataStore) wrapped.getDataStore()) { @Override protected String transformFeatureTypeName(String originalName) { if(typeMap.getOriginalName().equals(originalName)) { // rename return typeMap.getName(); } else if(typeMap.getName().equals(originalName)) { // hide return null; } else { return originalName; } } @Override protected SimpleFeatureType transformFeatureType(SimpleFeatureType original) throws IOException { if(typeMap.getOriginalName().equals(original)) { return typeMap.featureType; } else { return super.transformFeatureType(original); } } @Override public String[] getTypeNames() throws IOException { // Populate local hashmaps with new values. Map<String, FeatureTypeMap> forwardMapLocal = new ConcurrentHashMap<String, FeatureTypeMap>(); Map<String, FeatureTypeMap> backwardsMapLocal = new ConcurrentHashMap<String, FeatureTypeMap>(); forwardMapLocal.put(typeMap.getOriginalName(), typeMap); backwardsMapLocal.put(typeMap.getName(), typeMap); // Replace the member variables. forwardMap = forwardMapLocal; backwardsMap = backwardsMapLocal; return new String[] {typeMap.getName()}; } }; } /** * Returns the same name than the feature type (ie, * {@code getSchema().getName()} to honor the simple feature land common * practice of calling the same both the Features produces and their types * * @since 1.7 * @see FeatureSource#getName() */ public Name getName() { return getSchema().getName(); } public void addFeatureListener(FeatureListener listener) { FeatureListener wrapper = new WrappingFeatureListener(this, listener); listeners.put(listener, wrapper); wrapped.addFeatureListener(wrapper); } public void removeFeatureListener(FeatureListener listener) { FeatureListener wrapper = (FeatureListener) listeners.get(listener); if (wrapper != null) { wrapped.removeFeatureListener(wrapper); listeners.remove(listener); } } public ReferencedEnvelope getBounds() throws IOException { // not fully correct if we use this to shave attributes too, but this is // not in the scope now return wrapped.getBounds(); } public ReferencedEnvelope getBounds(Query query) throws IOException { // not fully correct if we use this to shave attributes too, but this is // not in the scope now return wrapped.getBounds(store.retypeQuery(query, typeMap)); } public int getCount(Query query) throws IOException { return wrapped.getCount(store.retypeQuery(query, typeMap)); } public DataStore getDataStore() { return store; } public SimpleFeatureCollection getFeatures() throws IOException { return getFeatures(Query.ALL); } public SimpleFeatureCollection getFeatures(Query query) throws IOException { if (query.getTypeName() == null) { query = new Query(query); ((Query) query).setTypeName(typeMap.getName()); } else if (!typeMap.getName().equals(query.getTypeName())) { throw new IOException("Cannot query this feature source with " + query.getTypeName() + " since it serves only " + typeMap.getName()); } //GEOS-3210, if the query specifies a subset of property names we need to take that into // account SimpleFeatureType target = typeMap.getFeatureType(query); return new RetypingFeatureCollection(wrapped.getFeatures(store.retypeQuery(query, typeMap)), target); } public SimpleFeatureCollection getFeatures(Filter filter) throws IOException { return getFeatures(new Query(typeMap.getName(), filter)); } public SimpleFeatureType getSchema() { return typeMap.getFeatureType(); } public Set getSupportedHints() { return wrapped.getSupportedHints(); } public ResourceInfo getInfo() { return wrapped.getInfo(); } public QueryCapabilities getQueryCapabilities() { return wrapped.getQueryCapabilities(); } }