/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2011, 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.geoserver.data.versioning.decorator;
import static org.geotools.data.Transaction.AUTO_COMMIT;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.geogit.api.GeoGIT;
import org.geogit.api.Ref;
import org.geogit.api.RevTree;
import org.geogit.repository.Repository;
import org.geoserver.data.geogit.VersioningTransactionState;
import org.geoserver.data.versioning.VersioningFeatureStore;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureStore;
import org.geotools.data.Transaction;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.filter.identity.ResourceIdImpl;
import org.opengis.feature.Feature;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory2;
import org.opengis.filter.Id;
import org.opengis.filter.identity.FeatureId;
import org.opengis.filter.identity.Identifier;
import org.opengis.filter.identity.ResourceId;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
@SuppressWarnings("rawtypes")
public class FeatureStoreDecorator<T extends FeatureType, F extends Feature>
extends FeatureSourceDecorator<T, F> implements
VersioningFeatureStore<T, F> {
public FeatureStoreDecorator(final FeatureStore unversioned,
final Repository repo) {
super(unversioned, repo);
}
@Override
public RevTree getCurrentVersion() {
final Transaction transaction = getTransaction();
if (null == transaction || Transaction.AUTO_COMMIT.equals(transaction)) {
return super.getCurrentVersion();
}
final Name name = getName();
RevTree headVersion = repository.getWorkingTree()
.getStagedVersion(name);
return headVersion;
}
/**
* @see org.geotools.data.FeatureStore#getTransaction()
*/
@Override
public Transaction getTransaction() {
return ((FeatureStore) unversioned).getTransaction();
}
/**
* @see org.geotools.data.FeatureStore#setTransaction(org.geotools.data.Transaction)
*/
@Override
public void setTransaction(final Transaction transaction) {
((FeatureStore) unversioned).setTransaction(transaction);
}
private FeatureStore<T, F> getUnversionedStore() {
return ((FeatureStore<T, F>) unversioned);
}
/**
* @see org.geotools.data.FeatureStore#addFeatures(org.geotools.feature.FeatureCollection)
*/
@Override
public List<FeatureId> addFeatures(FeatureCollection<T, F> collection)
throws IOException {
if (isVersioned()) {
checkTransaction();
}
final FeatureStore<T, F> unversioned = getUnversionedStore();
List<FeatureId> unversionedIds = unversioned.addFeatures(collection);
if (isVersioned()) {
try {
final Name typeName = getSchema().getName();
VersioningTransactionState versioningState = getVersioningState();
FilterFactory2 ff = CommonFactoryFinder.getFilterFactory2(null);
Id id = ff.id(new HashSet<Identifier>(unversionedIds));
FeatureCollection<T, F> inserted = unversioned.getFeatures(id);
List<FeatureId> versionedIds;
versionedIds = versioningState.stageInsert(typeName, inserted,
true);
return versionedIds;
} catch (Exception e) {
Throwables.propagate(e);
}
}
return unversionedIds;
}
/**
* @see org.geotools.data.FeatureStore#removeFeatures(org.opengis.filter.Filter)
*/
@Override
public void removeFeatures(Filter filter) throws IOException {
final FeatureStore<T, F> unversioned = getUnversionedStore();
if (isVersioned()) {
checkTransaction();
FeatureCollection<T, F> removed = unversioned.getFeatures(filter);
try {
Name typeName = getSchema().getName();
getVersioningState().stageDelete(typeName, filter, removed);
} catch (Exception e) {
Throwables.propagate(e);
}
}
unversioned.removeFeatures(filter);
}
/**
* @see org.geotools.data.FeatureStore#modifyFeatures(org.opengis.feature.type.Name[],
* java.lang.Object[], org.opengis.filter.Filter)
*/
@Override
public void modifyFeatures(final Name[] attributeNames,
final Object[] attributeValues, final Filter filter)
throws IOException {
final FeatureStore<T, F> unversioned = getUnversionedStore();
final boolean versioned = isVersioned();
Id affectedFeaturesFitler = null;
Filter unversionedFilter = filter;
if (versioned) {
checkTransaction();
// throws exception if filter has a resourceid that doesn't match
// the current version
checkEditFilterMatchesCurrentVersion(filter);
unversionedFilter = VersionFilters.getUnversioningFilter(filter);
if (unversionedFilter instanceof Id) {
affectedFeaturesFitler = (Id) unversionedFilter;
} else {
FeatureCollection<T, F> affectedFeatures;
affectedFeatures = unversioned.getFeatures(unversionedFilter);
FeatureIterator<F> iterator = affectedFeatures.features();
Set<Identifier> affectedIds = new HashSet<Identifier>();
try {
while (iterator.hasNext()) {
affectedIds.add(iterator.next().getIdentifier());
}
} finally {
iterator.close();
}
final FilterFactory2 ff = CommonFactoryFinder
.getFilterFactory2(null);
affectedFeaturesFitler = ff.id(affectedIds);
}
}
unversioned.modifyFeatures(attributeNames, attributeValues,
unversionedFilter);
if (versioned && affectedFeaturesFitler != null
&& affectedFeaturesFitler.getIdentifiers().size() > 0) {
try {
FeatureCollection newValues = unversioned
.getFeatures(affectedFeaturesFitler);
getVersioningState().stageUpdate(getSchema().getName(),
newValues);
} catch (Exception e) {
Throwables.propagate(e);
}
}
}
/**
* Throws an IllegalArgumentException if {@code filter} contains a resource
* filter that doesn't match the current version of a feature
*
* @param filter original upate filter
*/
private void checkEditFilterMatchesCurrentVersion(final Filter filter) {
final Id versionFilter = VersionFilters.getVersioningFilter(filter);
if (versionFilter == null) {
return;
}
// don't allow non current versions
GeoGIT ggit = new GeoGIT(repository);
VersionQuery query = new VersionQuery(ggit, getSchema().getName());
for (Identifier id : versionFilter.getIdentifiers()) {
ResourceId rid = (ResourceId) id;
List<Ref> requested;
List<Ref> current;
try {
requested = Lists.newArrayList(query.get(rid));
current = Lists.newArrayList(query.get(new ResourceIdImpl(rid
.getID(), null)));
} catch (Exception e) {
throw new RuntimeException(e);
}
if (!current.equals(requested)) {
throw new IllegalArgumentException(
"Requested resource id filter doesn't match curent version for feature "
+ rid.getID());
}
}
}
/**
* @see #modifyFeatures(Name[], Object[], Filter)
*/
@Override
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);
}
/**
* @see #modifyFeatures(Name[], Object[], Filter)
*/
@Override
public void modifyFeatures(Name attributeName, Object attributeValue,
Filter filter) throws IOException {
modifyFeatures(new Name[] { attributeName },
new Object[] { attributeValue }, filter);
}
/**
* @see #modifyFeatures(Name[], Object[], Filter)
*/
@Override
public void modifyFeatures(AttributeDescriptor type, Object value,
Filter filter) throws IOException {
modifyFeatures(new Name[] { type.getName() }, new Object[] { value },
filter);
}
/**
* @see org.geotools.data.FeatureStore#setFeatures(org.geotools.data.FeatureReader)
*/
@Override
public void setFeatures(FeatureReader<T, F> reader) throws IOException {
final FeatureStore<T, F> unversioned = getUnversionedStore();
unversioned.setFeatures(reader);
if (isVersioned()) {
checkTransaction();
throw new UnsupportedOperationException("do versioning!");
}
}
private void checkTransaction() {
if (Transaction.AUTO_COMMIT.equals(getTransaction())) {
throw new UnsupportedOperationException(
"AUTO_COMMIT is not supported for versioned Feature Types");
}
}
protected VersioningTransactionState getVersioningState() {
Transaction transaction = getTransaction();
if (AUTO_COMMIT.equals(transaction)) {
return VersioningTransactionState.VOID;
}
Object key = "WHAT_WOULD_BE_A_GOOD_KEY?";
VersioningTransactionState state = (VersioningTransactionState) transaction
.getState(key);
if (state == null) {
state = new VersioningTransactionState(new GeoGIT(repository));
transaction.putState(key, state);
}
return state;
}
}