package org.geotools.data.efeature;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.geotools.data.DataStore;
import org.geotools.data.DefaultServiceInfo;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Query;
import org.geotools.data.ServiceInfo;
import org.geotools.data.Transaction;
import org.geotools.data.store.ContentDataStore;
import org.geotools.data.store.ContentEntry;
import org.geotools.data.store.ContentFeatureSource;
import org.geotools.data.store.ContentFeatureStore;
import org.geotools.data.store.ContentState;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.NameImpl;
import org.opengis.feature.FeatureFactory;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.FeatureTypeFactory;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import com.vividsolutions.jts.geom.GeometryFactory;
/**
* Geotools {@link DataStore} for {@link EFeature}s
*
* @author kengu
*
*/
public class EFeatureDataStore extends ContentDataStore {
/**
* Publisher URI
*/
public static final String PUBLISHER = "http://www.osgeo.org";
protected final URI eURI;
protected final String eNsURI;
protected final String eContextID;
protected final String eDomainID;
protected final String eTypeQuery;
protected final Query eDataStoreQuery;
protected final boolean eWritable;
protected DefaultServiceInfo serviceInfo;
/**
* Cached {@link EFeaturePackageInfo} instance
*/
protected final EFeaturePackageInfo ePackageInfo;
// -----------------------------------------------------
// Constructors
// -----------------------------------------------------
/**
* A {@link EFeature} {@link DataStore} implementation class.
* @param eContextID - {@link EFeatureContext} instance id
* @param eDomainID - {@link EditingDomain} instance extension id
* @param eNsURI - {@link EPackage} name space
* @param eURI - {@link URI} formated string to EMF {@link Resource} containing
* {@link EFeature}s
* @param eTypes - {@link EFeature} types on the following format:
* <pre>
* eTypes:=<eType1>+...+<eTypeN>
*
* where
*
* eType := <eFolder>.<eFeature>
* eFolder := the name of the {@link EFeatureFolderInfo folder} which contains the {@link EFeatureInfo feature}
* eFeature := the name of the {@link EFeatureInfo feature type}
* </pre>
* @param eWritable - if <code>true</code> {@link EFeature}s are writable
* ({@link EFeatureWriter#UPDATE} | {@link EFeatureWriter#APPEND})
* @throws IOException
* @throws IllegalArgumentException If any argument is invalid.
* @see {@link URI#createURI(String)}
*/
public EFeatureDataStore(String eContextID, String eDomainID, String eNsURI,
String eURI, String eTypes, boolean eWritable) throws IOException, IllegalArgumentException {
//
// Forward using same context factory as EFeatureDataStoreFactory
//
this(EFeatureDataStoreFactory.eGetContextFactory().eContext(eContextID),eDomainID,eNsURI,eURI,eTypes,eWritable);
}
/**
* A {@link EFeature} {@link DataStore} implementation class.
* <p>
* @param eContext - {@link EFeatureContext} instance
* @param eDomainID - {@link EditingDomain} instance id
* @param eNsURI - {@link EPackage} name space
* @param eURI - {@link URI} formated string to EMF {@link Resource} containing
* {@link EFeature}s
* @param eTypes - {@link EFeature} type query on the following format:
* <pre>
* eTypes:=<eType1>+...+<eTypeN>
*
* where
*
* eType := <eFolder>.<eFeature>
* eFolder := the name of the {@link EFeatureFolderInfo folder} which contains the {@link EFeatureInfo feature}
* eFeature := the name of the {@link EFeatureInfo feature type}
* </pre>
* @param eWritable - if <code>true</code> {@link EFeature}s are writable
* ({@link EFeatureWriter#UPDATE} | {@link EFeatureWriter#APPEND})
* @throws IOException
* @throws IllegalArgumentException If any argument is invalid.
* @see {@link URI#createURI(String)}
*/
public EFeatureDataStore(EFeatureContext eContext, String eDomainID, String eNsURI,
String eURI, String eTypes, boolean eWritable) throws IOException, IllegalArgumentException {
//
// Forward to ContentStore constructor
//
super();
//
// Get context ID
//
this.eContextID = eContext.eContextID();
//
// Get EFeatureStore information
//
this.ePackageInfo = EFeatureDataStoreFactory.ePackageInfo(eContextID, eNsURI);
if (this.ePackageInfo == null) {
throw new IOException("EFeatureDataStore structure not "
+ "found in context: '" + eContextID + "/"
+ eDomainID + "/" + eURI );
}
//
// Cache other information
//
this.eDomainID = eDomainID;
this.eTypeQuery = eTypes;
this.eNsURI = eNsURI;
this.eURI = URI.createURI(eURI);
this.eWritable = eWritable;
//
// TODO Create EFeature query from 'eQuery' (must be added)
//
this.eDataStoreQuery = Query.ALL;
}
// -----------------------------------------------------
// Unsupported ContentDataStore methods
// -----------------------------------------------------
/**
* {@link EFeatureDataStore} does not support {@link FeatureTypeFactory}
* @throws UnsupportedOperationException Operation not supported
*/
@Override
public FeatureTypeFactory getFeatureTypeFactory() {
throw new UnsupportedOperationException("EFeatureDataStore does not support FeatureTypeFactory");
}
/**
* {@link EFeatureDataStore} does not support {@link FeatureTypeFactory}
* @throws UnsupportedOperationException Operation not supported
*/
@Override
public void setFeatureTypeFactory(FeatureTypeFactory typeFactory) {
throw new UnsupportedOperationException("EFeatureDataStore does not support FeatureTypeFactory");
}
/**
* {@link EFeatureDataStore} does not support {@link FeatureFactory}
* @throws UnsupportedOperationException Operation not supported
*/
@Override
public FeatureFactory getFeatureFactory() {
throw new UnsupportedOperationException("EFeatureDataStore does not support FeatureFactory");
}
/**
* {@link EFeatureDataStore} does not support {@link FeatureFactory}
* @throws UnsupportedOperationException Operation not supported
*/
@Override
public void setFeatureFactory(FeatureFactory featureFactory) {
throw new UnsupportedOperationException("EFeatureDataStore does not support FeatureFactory");
}
/**
* {@link EFeatureDataStore} does not support {@link GeometryFactory}
* @throws UnsupportedOperationException Operation not supported
*/
@Override
public GeometryFactory getGeometryFactory() {
throw new UnsupportedOperationException("EFeatureDataStore does not support GeometryFactory");
}
/**
* {@link EFeatureDataStore} does not support {@link GeometryFactory}
* @throws UnsupportedOperationException Operation not supported
*/
@Override
public void setGeometryFactory(GeometryFactory geometryFactory) {
throw new UnsupportedOperationException("EFeatureDataStore does not support GeometryFactory");
}
// -----------------------------------------------------
// Overridden ContentDataStore methods
// -----------------------------------------------------
@Override
public FilterFactory getFilterFactory() {
if(filterFactory==null) {
filterFactory = CommonFactoryFinder.getFilterFactory(null);
}
return filterFactory;
}
@Override
public ContentEntry getEntry(Name name) {
// TODO Auto-generated method stub
return super.getEntry(name);
}
// -----------------------------------------------------
// EFeatureDataStore convenience methods
// -----------------------------------------------------
/**
* Returns a {@link EFeatureWriter} for the specified type name and transaction.
* <p>
* This is a convenience method for <code>getFeatureWriter(typeName,filter,tx)</code>,
* which returns a writer capable of both updating and appending {@link EFeature features}
* </p>
* @param eType - (required) {@link EFeature} type name on the format:
* <pre>
* eType := <eFolder>.<eFeature>
*
* where
*
* eFolder := the name of the {@link EFeatureFolderInfo folder} which contains the {@link EFeatureInfo feature}
* eFeature := the name of the {@link EFeatureInfo feature}
* </pre>
* @param filter - (Optional) {@link Filter} selecting {@link EFeature features} to be updated.
* If <code>null</code>, {@link Filter#INCLUDE} is assumed.
* @param tx - (Optional) {@link Transaction} controlling modifications. If <code>null</code>,
* {@link Transaction#AUTO_COMMIT} is assumed.
* @return a {@link EFeatureWriter} instance.
* @see {@link #getFeatureWriter(String, Filter, Transaction)
*/
public final FeatureWriter<SimpleFeatureType, SimpleFeature> getEFeatureWriter(String eType, Filter filter,
Transaction tx) throws IOException {
tx = ensureTransaction(tx);
filter = ensureFilter(filter);
return getFeatureWriter( eType, filter, tx );
}
/**
* Returns a {@link EFeatureWriter} for the specified type name and transaction.
* <p>
* This returns a writer only capable of updating {@link EFeature features}
* </p>
* @param eType - (required) {@link EFeature} type name on the format:
* <pre>
* eType := <eFolder>.<eFeature>
*
* where
*
* eFolder := the name of the {@link EFeatureFolderInfo folder} which contains the {@link EFeatureInfo feature}
* eFeature := the name of the {@link EFeatureInfo feature}
* </pre>
* @param filter - (Optional) {@link Filter} selecting {@link EFeature features} to be updated.
* If <code>null</code>, {@link Filter#INCLUDE} is assumed.
* @param tx - (Optional) {@link Transaction} controlling modifications. If <code>null</code>,
* {@link Transaction#AUTO_COMMIT} is assumed.
* @return a {@link FeatureWriter} instance.
*/
public final FeatureWriter<SimpleFeatureType, SimpleFeature> getEFeatureWriterUpdate(String eType, Filter filter,
Transaction tx) throws IOException {
tx = ensureTransaction(tx);
filter = ensureFilter(filter);
ContentFeatureStore featureStore = ensureFeatureStore(eType,tx);
return featureStore.getWriter( filter , WRITER_UPDATE );
}
/**
* Returns an appending {@link EFeatureWriter} for the specified type name and
* transaction.
* <p>
* This is a convenience method for <code>getFeatureWriterAppend(typeName,tx)</code>,
* which returns a writer only capable of appending {@link EFeature features}
* (no filter is therefore required).
* </p>
* @param eType - (required) {@link EFeature} type name on the format:
* <pre>
* eType := <eFolder>.<eFeature>
*
* where
*
* eFolder := the name of the {@link EFeatureFolderInfo folder} which contains the {@link EFeatureInfo feature}
* eFeature := the name of the {@link EFeatureInfo feature}
* </pre>
* @param tx - (Optional) {@link Transaction} controlling modifications. If <code>null</code>,
* {@link Transaction#AUTO_COMMIT} is assumed.
* @return a {@link FeatureWriter} instance.
* @see {@link #getEFeatureWriterAppend(String, Transaction)
*/
public final FeatureWriter<SimpleFeatureType, SimpleFeature> getEFeatureWriterAppend(
String eType, Transaction tx) throws IOException {
tx = ensureTransaction(tx);
return getFeatureWriterAppend(eType, tx );
}
// -----------------------------------------------------
// EFeatureDataStore methods
// -----------------------------------------------------
/**
* Get {@link EPackage} names space URI as string.
*/
public String eNsURI() {
return eNsURI;
}
public EFeatureContext eContext() {
return ePackageInfo.eContext();
}
/**
* Get {@link EditingDomain} instance.
*
* @return an {@link EditingDomain} instance
*/
public EditingDomain eDomain() {
return eContext().eGetDomain(eDomainID);
}
/**
* Get {@link EPackage} instance.
*
* @return the {@link EPackage} instance.
*/
public EPackage ePackage() {
return eContext().eGetPackage(eNsURI);
}
/**
* Get {@link URI} to {@link Resource} containing {@link EFeature}s or {@link EFeature}
* compatible data.
*
* @return an {@link URI} instance
*/
public URI eResourceURI() {
return eURI;
}
/**
* Get the {@link Resource} that contains the data.
*/
public Resource eResource() {
return eContext().eGetResource(eDomainID, eURI, true);
}
/**
* Information about this service.
* <p>
* This method offers access to a summary of header or metadata
* information describing the service.
* </p>
* @return SeviceInfo
*/
@Override
public ServiceInfo getInfo() {
if(serviceInfo==null) {
serviceInfo = new DefaultServiceInfo();
serviceInfo.setTitle("EFeature DataStore");
serviceInfo.setKeywords(new HashSet<String>(
Arrays.asList("EFeature", "EMF", "EMF Query", "EMF Transaction")));
serviceInfo.setDescription( "The EFeature DataStore module adds support " +
"for spatial read and write operations to EMF models." );
serviceInfo.setSchema( java.net.URI.create( ePackageInfo.eNsURI ) );
serviceInfo.setPublisher( java.net.URI.create(PUBLISHER) );
}
serviceInfo.setSource( java.net.URI.create(eURI.toString()) );
return serviceInfo;
}
/**
* Get {@link EFeaturePackageInfo structure information}.
* </p>
* @return a {@link EFeaturePackageInfo} instance.
*/
public EFeaturePackageInfo eStructure() {
return ePackageInfo;
}
/**
* Convenience method for calling {@link #getFeatureReader(Query, Transaction)}
* with {@link Transaction#AUTO_COMMIT} as the transaction.
*
* @param query - the {@link EFeature} query
* @return a {@link EFeatureReader} instance.
* @throws IOException
*/
public EFeatureReader getFeatureReader(Query query) throws IOException {
return getFeatureReader(query, Transaction.AUTO_COMMIT);
}
@Override
public EFeatureReader getFeatureReader(Query query, Transaction transaction) throws IOException {
return (EFeatureReader)super.getFeatureReader(query, transaction);
}
/**
* Get a {@link EFeatureReader} for given {@link EFeature} type name.
* </p>
* @param eType - {@link EFeature} type name on the format:
* <pre>
* eType := <eFolder>.<eFeature>
*
* where
*
* eFolder := the name of the {@link EFeatureFolderInfo folder} which contains the {@link EFeatureInfo feature}
* eFeature := the name of the {@link EFeatureInfo feature}
* </pre>
* @return an {@link EFeatureReader} of given {@link EFeature} type
*/
public EFeatureReader getFeatureReader(String eType) throws IOException {
return getFeatureReader(new Query(eType,Filter.INCLUDE));
}
// -----------------------------------------------------
// ContentDataStore implementation
// -----------------------------------------------------
/**
* Gets the names of {@link FeatureType feature types} available
* in this {@code EFeatureDataStore store}.
* <p>
* Since {@link EFeatureDataStore}s only have one name space by design,
* this method is guaranteed to return a list of unique
* names since the all unqualified type names are present in the same
* name space.
* </p>
* The following naming convention is used:
* <pre>
* name := <eFolder>.<eFeature>
*
* where
*
* eFolder := the name of the {@link EFeatureFolderInfo folder} which contains the {@link EFeatureInfo feature}
* eFeature := the name of the {@link EFeatureInfo feature}
* </pre>
*
* @return names of EFeature types available in this {@code DataStore}
*
* @see EFeatureUtils#toFolderName(String) - parse type name into folder name
* @see EFeatureUtils#toFeatureName(String) - parse type name into feature name
*/
@Override
protected List<Name> createTypeNames() throws IOException {
List<Name> eNames = new ArrayList<Name>();
for(String eName : ePackageInfo.getTypeNames(eTypeQuery)) {
eNames.add(new NameImpl(eName));
}
return eNames;
}
@Override
protected ContentState createContentState(ContentEntry entry) {
//
// Forward to default implementation
//
ContentState state = super.createContentState(entry);
//
// Get EFeature structure info
//
EFeatureInfo eStructure = ePackageInfo.eGetFeatureInfo(entry.getTypeName());
//
// Set SimpleFeature type definition
//
state.setFeatureType(eStructure.getFeatureType());
//
// Finished
//
return state;
}
@Override
protected ContentFeatureSource createFeatureSource(ContentEntry entry) throws IOException {
//
// Create FeatureStore containing EFeature instances matching current data store query
//
return eWritable ? new EFeatureStore(entry, eDataStoreQuery)
: new EFeatureSource(entry, eDataStoreQuery);
}
// -----------------------------------------------------
// Static helper methods
// -----------------------------------------------------
protected static Filter ensureFilter(Filter filter) {
return (filter == null ? Filter.INCLUDE : filter);
}
protected static Transaction ensureTransaction(Transaction tx) {
return (tx == null ? Transaction.AUTO_COMMIT : tx);
}
}