/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 * 
 *    (C) 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.geotools.data.store;

import java.io.IOException;

import org.geotools.data.DataSourceException;
import org.geotools.data.Diff;
import org.geotools.data.FeatureWriter;
import org.geotools.data.Transaction;
import org.geotools.data.TransactionStateDiff;
import org.geotools.factory.Hints;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.IllegalAttributeException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;

/**
 * Transaction state responsible for holding an in memory {@link Diff} of any modifications.
 */
class DiffTransactionState implements Transaction.State {
    Diff diff;
    
    /** The transaction (ie session) associated with this state */
    Transaction transaction;

    /**
     * ContentState for this transaction used to hold information for
     * FeatureReader implementations
     */
    ContentState state;

    /**
     * Transaction state responsible for holding an in memory {@link Diff}.
     * 
     * @param state ContentState for the transaction
     */
    public DiffTransactionState(ContentState state) {
        this.state = state;
        this.diff = new Diff();
    }
    /**
     * Access the in memory Diff.
     * @return in memory diff.
     */
    public Diff getDiff() {
        return this.diff;
    }

    @Override
    
    /**
     * We are already holding onto our transaction from
     * ContentState; however this method does check that
     * the transaction is correct.
     */
    public synchronized void setTransaction(Transaction transaction) {
        this.transaction = transaction;
    }

    @Override
    
    /**
     * Will apply differences to store.
     * 
     * <p>
     * The provided diff will be modified as the differences are applied,
     * If the operations are all 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
     * @t
     * @see org.geotools.data.Transaction.State#commit()
     */
    public synchronized void commit() throws IOException {
        if (diff.isEmpty()) {
            return; // nothing to do
        }
        FeatureWriter<SimpleFeatureType, SimpleFeature> writer;
        ContentFeatureStore store;
        ContentEntry entry = state.getEntry();
        Name name = entry.getName();
        ContentDataStore dataStore = entry.getDataStore();
        ContentFeatureSource source = (ContentFeatureSource) dataStore.getFeatureSource(name);
        if (source instanceof ContentFeatureStore) {
            store = (ContentFeatureStore) dataStore.getFeatureSource(name);
            writer = store.getWriter(Filter.INCLUDE);
        } else {
            throw new UnsupportedOperationException("not writable");
        }
        SimpleFeature feature;
        SimpleFeature update;

        Throwable cause = null;
        try {
            while (writer.hasNext()) {
                feature = (SimpleFeature) writer.next();
                String fid = feature.getID();

                if (diff.getModified().containsKey(fid)) {
                    update = (SimpleFeature) diff.getModified().get(fid);

                    if (update == TransactionStateDiff.NULL) {
                        writer.remove();

                        // notify
                        state.fireFeatureRemoved(store, feature);
                    } else {
                        try {
                            feature.setAttributes(update.getAttributes());
                            writer.write();

                            // notify
                            ReferencedEnvelope bounds = ReferencedEnvelope.reference(feature
                                    .getBounds());
                            state.fireFeatureUpdated(store, update, bounds);
                        } catch (IllegalAttributeException e) {
                            throw new DataSourceException("Could update " + fid, e);
                        }
                    }
                }
            }

            SimpleFeature addedFeature;
            SimpleFeature nextFeature;

            synchronized (diff) {
                for (String fid : diff.getAddedOrder()) {
                    addedFeature = diff.getAdded().get(fid);

                    nextFeature = (SimpleFeature) writer.next();

                    if (nextFeature == null) {
                        throw new DataSourceException("Could not add " + fid);
                    } else {
                        try {
                            nextFeature.setAttributes(addedFeature.getAttributes());
                            // if( Boolean.TRUE.equals(
                            // addedFeature.getUserData().get(Hints.USE_PROVIDED_FID)) ){
                            nextFeature.getUserData().put(Hints.USE_PROVIDED_FID, true);
                            if (addedFeature.getUserData().containsKey(Hints.PROVIDED_FID)) {
                                String providedFid = (String) addedFeature.getUserData().get(
                                        Hints.PROVIDED_FID);
                                nextFeature.getUserData().put(Hints.PROVIDED_FID, providedFid);
                            } else {
                                nextFeature.getUserData().put(Hints.PROVIDED_FID,
                                        addedFeature.getID());
                            }
                            // }
                            writer.write();

                            // notify
                            state.fireFeatureAdded(store, nextFeature);
                        } catch (IllegalAttributeException e) {
                            throw new DataSourceException("Could update " + fid, e);
                        }
                    }
                }
            }
        } catch (IOException e) {
            cause = e;
            throw e;
        } catch (RuntimeException e) {
            cause = e;
            throw e;
        } finally {
            try {
                writer.close();
                state.fireBatchFeatureEvent(true);
                diff.clear();
            } catch (IOException e) {
                if (cause != null) {
                    e.initCause(cause);
                }
                throw e;
            } catch (RuntimeException e) {
                if (cause != null) {
                    e.initCause(cause);
                }
                throw e;
            }
        }
    }

    @Override
    /**
     * @see org.geotools.data.Transaction.State#rollback()
     */
    public synchronized void rollback() throws IOException {
        diff.clear(); // rollback differences
        state.fireBatchFeatureEvent(false);
    }

    @Override
    
    /**
     * @see org.geotools.data.Transaction.State#addAuthorization(java.lang.String)
     */
    public synchronized void addAuthorization(String AuthID) throws IOException {
        // not required for TransactionStateDiff
    }
}
@@ -1323,16 +1323,23 @@
 ns.%0A */%0A
+public 
 class Di
@@ -1392,16 +1392,26 @@
 e %7B%0A    
+protected 
 Diff dif
@@ -1489,16 +1489,26 @@
  */%0A    
+protected 
 Transact
@@ -1648,20 +1648,30 @@
      */%0A
-
     
+protected 
 ContentS

allow DiffTransactionState to be extended