/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2004-2008, 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.Iterator;
import org.geotools.data.DataSourceException;
import org.geotools.data.Diff;
import org.geotools.data.DiffFeatureReader;
import org.geotools.data.DiffFeatureWriter;
import org.geotools.data.FeatureEvent;
import org.geotools.data.FeatureReader;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Query;
import org.geotools.data.Transaction;
import org.geotools.data.TransactionStateDiff;
import org.geotools.data.Transaction.State;
import org.geotools.feature.IllegalAttributeException;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
/**
* A Transaction.State that keeps a difference table.
* <p>
* This implementation is backed by memory, please feel free to make a scalable
* implementation backed by a temporary file.
* </p>
*
* @author Jody Garnett, Refractions Research
* @source $URL$
*/
public class TypeDiffState implements State {
/** Tranasction this State is opperating against. */
Transaction transaction;
/**
* Map of differences by typeName.
*
* <p>
* Differences are stored as a Map of Feature by fid, and are reset during
* a commit() or rollback().
* </p>
*/
Diff diffMap = new Diff();
private ActiveTypeEntry entry;
public TypeDiffState(ActiveTypeEntry typeEntry) {
entry = typeEntry;
}
public synchronized void setTransaction(Transaction transaction) {
if (transaction != null) {
// configure
this.transaction = transaction;
} else {
this.transaction = null;
if (diffMap != null) {
diffMap.clear();
}
entry = null;
}
}
public Diff diff() throws IOException {
return diffMap;
}
/**
* @see org.geotools.data.Transaction.State#addAuthorization(java.lang.String)
*/
public synchronized void addAuthorization(String AuthID)
throws IOException {
// not required for TransactionStateDiff
}
/**
* Will apply differences to store.
*
* @see org.geotools.data.Transaction.State#commit()
*/
public synchronized void commit() throws IOException {
applyDiff( diffMap );
}
/**
* Called by commit() to apply one set of diff
*
* <p>
* diff will be modified as the differneces are applied, If the opperations
* is successful diff will be empty at the end of this process.
* </p>
*
* <p>
* diff can be used to represent the following operations:
* </p>
*
* <ul>
* <li>
* fid|null: represents a fid being removed
* </li>
* <li>
* fid|feature: where fid exists, represents feature modification
* </li>
* <li>
* fid|feature: where fid does not exist, represents feature being modified
* </li>
* </ul>
*
*
* @param typeName typeName being updated
* @param diff differences to apply to FeatureWriter
*
* @throws IOException If the entire diff cannot be writen out
* @throws DataSourceException If the entire diff cannot be writen out
*/
void applyDiff(Diff diff) throws IOException {
if (diff.isEmpty()) {
return;
}
FeatureWriter<SimpleFeatureType, SimpleFeature> writer = entry.createWriter();
SimpleFeature feature;
SimpleFeature update;
String fid;
try {
while (writer.hasNext()) {
feature = (SimpleFeature)writer.next();
fid = feature.getID();
if (diff.modified2.containsKey(fid)) {
update = (SimpleFeature) diff.modified2.get(fid);
if (update == TransactionStateDiff.NULL) {
writer.remove();
// notify
entry.listenerManager.fireFeaturesChanged( entry.getTypeName(), transaction, ReferencedEnvelope.reference(feature.getBounds()), true);
} else {
try {
feature.setAttributes(update.getAttributes());
writer.write();
// notify
ReferencedEnvelope bounds = new ReferencedEnvelope();
bounds.include(feature.getBounds());
bounds.include(update.getBounds());
entry.listenerManager.fireFeaturesChanged( entry.getTypeName(),
transaction, bounds, true);
} catch (IllegalAttributeException e) {
throw new DataSourceException("Could update " + fid,
e);
}
}
}
}
SimpleFeature addedFeature;
SimpleFeature nextFeature;
for (Iterator i = diff.added.values().iterator(); i.hasNext();) {
addedFeature = (SimpleFeature) i.next();
fid = addedFeature.getID();
nextFeature = (SimpleFeature)writer.next();
if (nextFeature == null) {
throw new DataSourceException("Could not add " + fid);
} else {
try {
nextFeature.setAttributes(addedFeature.getAttributes());
writer.write();
// notify
entry.listenerManager.fireFeaturesAdded( entry.getTypeName(),transaction, ReferencedEnvelope.reference(nextFeature.getBounds()), true);
} catch (IllegalAttributeException e) {
throw new DataSourceException("Could update " + fid,
e);
}
}
}
} finally {
writer.close();
diff.clear();
entry.listenerManager.fireChanged( entry.getTypeName(), transaction, true);
}
}
/**
* @see org.geotools.data.Transaction.State#rollback()
*/
public synchronized void rollback() throws IOException {
diffMap.clear(); // rollback differences
entry.listenerManager.fireChanged( entry.getTypeName(), transaction, false);
}
/**
* Convience Method for a Transaction based FeatureReader.
*
* <p>
* Constructs a DiffFeatureReader that works against this Transaction.
* </p>
*
* @return FeatureReader<SimpleFeatureType, SimpleFeature> the mask orgional contents with against the
* current Differences recorded by the Tansasction State
*
* @throws IOException If typeName is not Manged by this Tansaction State
*/
public synchronized FeatureReader<SimpleFeatureType, SimpleFeature> reader() throws IOException {
return new DiffFeatureReader<SimpleFeatureType, SimpleFeature>( entry.reader( Query.ALL, transaction ), diffMap );
}
/**
* Convience Method for a Transaction based FeatureWriter
*
* <p>
* Constructs a DiffFeatureWriter that works against this Transaction.
* </p>
*
* @return A FeatureWriter that records Differences against a FeatureReader
*
* @throws IOException If a FeatureRader could not be constucted to record
* differences against
*/
public synchronized FeatureWriter<SimpleFeatureType, SimpleFeature> writer()
throws IOException {
Diff diff = new Diff();
FeatureReader<SimpleFeatureType, SimpleFeature> reader = entry.createReader();
return new DiffFeatureWriter(reader, diff) {
protected void fireNotification(int eventType, ReferencedEnvelope bounds) {
switch (eventType) {
case FeatureEvent.FEATURES_ADDED:
entry.listenerManager.fireFeaturesAdded( entry.getTypeName(),
transaction, bounds, false);
break;
case FeatureEvent.FEATURES_CHANGED:
entry.listenerManager.fireFeaturesChanged(entry.getTypeName(),
transaction, bounds, false);
break;
case FeatureEvent.FEATURES_REMOVED:
entry.listenerManager.fireFeaturesRemoved(entry.getTypeName(),
transaction, bounds, false);
break;
}
}
};
}
}