/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.feature.retype;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
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
* @return
*/
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);
}
}
};
}
/**
* 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();
if ( query.getPropertyNames() != Query.ALL_NAMES ) {
target = SimpleFeatureTypeBuilder.retype(target, query.getPropertyNames());
}
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();
}
}