org.geotools.data.store.ContentFeatureStore Maven / Gradle / Ivy
Show all versions of gt-main Show documentation
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2015, Open Source Geospatial Foundation (OSGeo)
*
* 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.geotools.data.store;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureWriter;
import org.geotools.data.FilteringFeatureWriter;
import org.geotools.data.InProcessLockingManager;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureLocking;
import org.geotools.data.simple.SimpleFeatureStore;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.NameImpl;
import org.geotools.filter.identity.FeatureIdImpl;
import org.geotools.util.factory.Hints;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.identity.FeatureId;
/**
* Abstract implementation of FeatureStore.
*
* List its base class {@link ContentFeatureSource}, this feature store works off of operations
* provided by {@link FeatureCollection}.
*
*
The {@link #addFeatures(SimpleFeatureCollection)} method is used to add features to the
* feature store. The method should return the "persistent" feature id's which are generated after
* the feature has been added to persistent storage. Often the persistent fid is different from the
* fid specified by the actual feature being inserted. For this reason {@link
* SimpleFeature#getUserData()} is used to report back persistent fids. It is up to the implementor
* of the feature collection to report this value back after a feature has been inserted. As an
* example, consider an implementation of {@link FeatureCollection#add(Object)}.
*
*
* boolean add( Object o ) {
* SimpleFeature feature = (SimpleFeature) o;
*
* //1.add the feature to storage
* ...
*
* //2. derive the persistent fid
* String fid = ...;
*
* //3. set the user data
* feature.getUserData().put( "fid", fid );
*
* }
*
*
* @author Justin Deoliveira, The Open Planning Project
*/
public abstract class ContentFeatureStore extends ContentFeatureSource
implements SimpleFeatureStore, SimpleFeatureLocking {
/** Flag writer for adding new content */
protected final int WRITER_ADD = ContentDataStore.WRITER_ADD;
/** Flag writer for updating content in place */
protected final int WRITER_UPDATE = ContentDataStore.WRITER_UPDATE;
/** Flag writer for commit (AUTO_COMMIT with no events) */
protected final int WRITER_COMMIT = ContentDataStore.WRITER_COMMIT;
public static final String ORIGINAL_FEATURE_KEY = "_original_";
/**
* Creates the content feature store.
*
* @param entry The entry for the feature store.
* @param query The defining query.
*/
public ContentFeatureStore(ContentEntry entry, Query query) {
super(entry, query);
}
/**
* Returns a writer over features specified by a filter.
*
* @param filter The filter
*/
public final FeatureWriter getWriter(Filter filter)
throws IOException {
return getWriter(filter, WRITER_ADD | WRITER_UPDATE);
}
/**
* Returns a writer over features specified by a filter.
*
* @param filter The filter
* @param flags flags specifying writing mode
*/
public final FeatureWriter getWriter(Filter filter, int flags)
throws IOException {
return getWriter(new Query(getSchema().getTypeName(), filter), flags);
}
/**
* Returns a writer over features specified by a query.
*
* @param query The query
*/
public final FeatureWriter getWriter(Query query)
throws IOException {
return getWriter(query, WRITER_ADD | WRITER_UPDATE);
}
/**
* Returns a writer over features specified by a query.
*
* @param query The query
* @param flags flags specifying writing mode
*/
public final FeatureWriter getWriter(Query query, int flags)
throws IOException {
query = joinQuery(query);
query = resolvePropertyNames(query);
FeatureWriter writer;
if (!canTransact() && transaction != null && transaction != Transaction.AUTO_COMMIT) {
if ((flags | WRITER_COMMIT) == WRITER_COMMIT) {
// Simple simple writer with no events or locking
writer = getWriterInternal(query, flags);
// filtering may not be needed
if (!canFilter()) {
if (query.getFilter() != null && query.getFilter() != Filter.INCLUDE) {
writer = new FilteringFeatureWriter(writer, query.getFilter());
}
}
} else {
DiffTransactionState state =
(DiffTransactionState) getTransaction().getState(getEntry());
// reader will take care of filtering
// DiffContentWriter takes care of events
@SuppressWarnings("PMD.CloseResource") // wrapped and returned
FeatureReader reader = getReader(query);
writer = state.diffWriter(this, reader);
}
} else {
writer = getWriterInternal(query, flags);
// events
if (!canEvent()) {
writer = new EventContentFeatureWriter(this, writer);
}
// filtering
if (!canFilter()) {
if (query.getFilter() != null && query.getFilter() != Filter.INCLUDE) {
writer = new FilteringFeatureWriter(writer, query.getFilter());
}
}
}
// Use InProcessLockingManager to assert write locks?
if (!canLock()) {
InProcessLockingManager lockingManager =
(InProcessLockingManager) getDataStore().getLockingManager();
writer = lockingManager.checkedWriter(writer, transaction);
}
// Finished
return writer;
}
/**
* Subclass method for returning a native writer from the datastore.
*
* It is important to note that if the native writer intends to handle any of the following
* natively:
*
*
* - reprojection
*
- filtering
*
- events
*
- max feature limiting
*
- sorting
*
- locking
*
*
* Then it *must* set the corresponding flags to true
:
*
*
* - {@link #canReproject()}
*
- {@link #canFilter()}
*
- {@link #canEvent()}
*
- {@link #canLimit()}
*
- {@link #canSort()}
*
- {@link #canLock()}
*
*
* @param query Query
* @param flags See {@link #WRITER_ADD} and {@link #WRITER_UPDATE}
*/
protected abstract FeatureWriter getWriterInternal(
Query query, int flags) throws IOException;
/**
* Adds a collection of features to the store.
*
* This method operates by getting an appending feature writer and writing all the features
* in collection to it. Directly after a feature is written its id is obtained and
* added to the returned set.
*/
public List addFeatures(Collection collection) throws IOException {
// gather up id's
List ids = new ArrayList(collection.size());
FeatureWriter writer = getWriterAppend();
try {
for (Iterator f = collection.iterator(); f.hasNext(); ) {
FeatureId id = addFeature((SimpleFeature) f.next(), writer);
ids.add(id);
}
} finally {
writer.close();
}
return ids;
}
/** Adds a collection of features to the store. */
public List addFeatures(
FeatureCollection featureCollection)
throws IOException {
// gather up id's
List ids = new ArrayList();
FeatureWriter writer = getWriterAppend();
FeatureIterator f = featureCollection.features();
try {
while (f.hasNext()) {
SimpleFeature feature = f.next();
FeatureId id = addFeature(feature, writer);
ids.add(id);
}
} finally {
writer.close();
f.close();
}
return ids;
}
/** Utility method that ensures we are going to write only in append mode */
private FeatureWriter getWriterAppend() throws IOException {
FeatureWriter writer =
getWriter(Filter.INCLUDE, WRITER_ADD);
while (writer.hasNext()) {
writer.next();
}
return writer;
}
FeatureId addFeature(
final SimpleFeature feature, FeatureWriter writer)
throws IOException {
// grab next feature and populate it
// JD: worth a note on how we do this... we take a "pull" approach
// because the raw schema we are inserting into may not match the
// schema of the features we are inserting
final SimpleFeature toWrite = writer.next();
for (int i = 0; i < toWrite.getType().getAttributeCount(); i++) {
String name = toWrite.getType().getDescriptor(i).getLocalName();
toWrite.setAttribute(name, feature.getAttribute(name));
}
// copy over the user data
toWrite.getUserData().putAll(feature.getUserData());
// pass through the fid if the user asked so
boolean useExisting =
Boolean.TRUE.equals(feature.getUserData().get(Hints.USE_PROVIDED_FID));
if (getQueryCapabilities().isUseProvidedFIDSupported() && useExisting) {
((FeatureIdImpl) toWrite.getIdentifier()).setID(feature.getID());
}
// Need to save a link to the original feature in order to be able to set the ID once it
// is actually saved (see JDBCInsertFeatureWriter)
toWrite.getUserData().put(ORIGINAL_FEATURE_KEY, feature);
// perform the write
writer.write();
// copy any metadata from the feature that was actually written (not always effective, see
// JDBCInsertFeatureWriter)
feature.getUserData().putAll(toWrite.getUserData());
feature.getUserData().remove(ORIGINAL_FEATURE_KEY);
return toWrite.getIdentifier();
}
/**
* Sets the feature of the source.
*
* This method operates by first clearing the contents of the feature store ({@link
* #removeFeatures(Filter)}), and then obtaining an appending feature writer and writing all
* features from reader to it.
*/
public final void setFeatures(FeatureReader reader)
throws IOException {
// remove features
removeFeatures(Filter.INCLUDE);
// grab a feature writer for insert
FeatureWriter writer =
getWriter(Filter.INCLUDE, WRITER_ADD);
try {
while (reader.hasNext()) {
SimpleFeature feature = reader.next();
// grab next feature and populate it
// JD: worth a note on how we do this... we take a "pull" approach
// because the raw schema we are inserting into may not match the
// schema of the features we are inserting
SimpleFeature toWrite = writer.next();
for (int i = 0; i < toWrite.getType().getAttributeCount(); i++) {
String name = toWrite.getType().getDescriptor(i).getLocalName();
toWrite.setAttribute(name, feature.getAttribute(name));
}
// perform the write
writer.write();
}
} finally {
writer.close();
}
}
public void modifyFeatures(AttributeDescriptor[] type, Object[] value, Filter filter)
throws IOException {
Name attributeNames[] = new Name[type.length];
for (int i = 0; i < type.length; i++) {
attributeNames[i] = type[i].getName();
}
modifyFeatures(attributeNames, value, filter);
}
/**
* Modifies/updates the features of the store which match the specified filter.
*
* This method operates by obtaining an updating feature writer based on the specified
* filter and writing the updated values to it.
*
*
The filter must not be null
, in this case this method will throw an
* {@link IllegalArgumentException}.
*/
public void modifyFeatures(Name[] type, Object[] value, Filter filter) throws IOException {
if (filter == null) {
String msg = "Must specify a filter, must not be null.";
throw new IllegalArgumentException(msg);
}
filter = resolvePropertyNames(filter);
// grab a feature writer
FeatureWriter writer = getWriter(filter, WRITER_UPDATE);
try {
while (writer.hasNext()) {
SimpleFeature toWrite = writer.next();
for (int i = 0; i < type.length; i++) {
toWrite.setAttribute(type[i], value[i]);
}
writer.write();
}
} finally {
writer.close();
}
}
public final void modifyFeatures(String name, Object attributeValue, Filter filter)
throws IOException {
modifyFeatures(
new Name[] {
new NameImpl(name),
},
new Object[] {
attributeValue,
},
filter);
}
public final void modifyFeatures(String[] names, Object[] values, Filter filter)
throws IOException {
Name attributeNames[] = new Name[names.length];
for (int i = 0; i < names.length; i++) {
attributeNames[i] = new NameImpl(names[i]);
}
modifyFeatures(attributeNames, values, filter);
}
/** Calls through to {@link #modifyFeatures(Name[], Object[], Filter)}. */
public final void modifyFeatures(AttributeDescriptor type, Object value, Filter filter)
throws IOException {
modifyFeatures(new Name[] {type.getName()}, new Object[] {value}, filter);
}
/** Calls through to {@link #modifyFeatures(Name[], Object[], Filter)}. */
public final void modifyFeatures(Name name, Object value, Filter filter) throws IOException {
modifyFeatures(new Name[] {name}, new Object[] {value}, filter);
}
/**
* Removes the features from the store which match the specified filter.
*
* This method operates by obtaining an updating feature writer based on the specified
* filter and removing every feature from it.
*
*
The filter must not be null
, in this case this method will throw an
* {@link IllegalArgumentException}.
*/
public void removeFeatures(Filter filter) throws IOException {
if (filter == null) {
String msg = "Must specify a filter, must not be null.";
throw new IllegalArgumentException(msg);
}
filter = resolvePropertyNames(filter);
// grab a feature writer
FeatureWriter writer = getWriter(filter, WRITER_UPDATE);
try {
// remove everything
while (writer.hasNext()) {
writer.next();
writer.remove();
}
} finally {
writer.close();
}
}
}