All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotools.data.store.ContentState Maven / Gradle / Ivy

Go to download

The main module contains the GeoTools public interfaces that are used by other GeoTools modules (and GeoTools applications). Where possible we make use industry standard terms as provided by OGC and ISO standards. The formal GeoTools public api consists of gt-metadata, jts and the gt-main module. The main module contains the default implementations that are available provided to other GeoTools modules using our factory system. Factories are obtained from an appropriate FactoryFinder, giving applications a chance configure the factory used using the Factory Hints facilities. FilterFactory ff = CommonFactoryFinder.getFilterFactory(); Expression expr = ff.add( expression1, expression2 ); If you find yourself using implementation specific classes chances are you doing it wrong: Expression expr = new AddImpl( expression1, expressiom2 );

The newest version!
/*
 *    GeoTools - The Open Source Java GIS Toolkit
 *    http://geotools.org
 *
 *    (C) 2006-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.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import org.geotools.data.BatchFeatureEvent;
import org.geotools.data.Diff;
import org.geotools.data.FeatureEvent;
import org.geotools.data.FeatureEvent.Type;
import org.geotools.data.FeatureListener;
import org.geotools.data.FeatureSource;
import org.geotools.data.Transaction;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.opengis.filter.identity.FeatureId;

/**
 * The state of an entry in a DataStore, maintained on a per-transaction basis. For information
 * maintained on a typeName basis see {@link ContentEntry}.
 *
 * 

Data Cache Synchronization Required

* *

The default ContentState implementation maintains cached values on a per transaction basis: * *

    *
  • feature type ({@link #getFeatureType()} *
  • number of features ({@link #getCount()} *
  • spatial extent ({@link #getBounds()} *
* * Other types of state depend on the data format and must be handled by a subclass. * *

This class is a "data object" used to store values and is not thread safe. It is up to clients * of this class to ensure that values are set in a thread-safe / synchronized manner. For example: * *

 *   
 *   ContentState state = ...;
 *
 *   //get the count
 *   int count = state.getCount();
 *   if ( count == -1 ) {
 *     synchronized ( state ) {
 *       count = calculateCount();
 *       state.setCount( count );
 *     }
 *   }
* *

Event Notification

* * The list of listeners interested in event notification as features are modified and edited is * stored as part of ContentState. Each notification is considered to be broadcast from a specific * FeatureSource. Since several ContentFeatureStores can be on the same transaction (and thus share * a ContentState) the fire methods listed here require you pass in the FeatureSource making the * change; this requires that your individual FeatureWriters keep a pointer to the * ContentFeatureStore which created them. * *

You may also make use of {@link ContentFeatureSource#canEvent} value of {@code false} allowing * the base ContentFeatureStore class to take responsibility for sending event notifications. * *

Transaction Independence

* *

The default ContentState implementation also supports the handling of {@link * ContentFeatureSource#canTransaction} value of {@code false}. The implementation asks ContentState * to store a {@link Diff} which is used to record any modifications made until commit is called. * *

Internally a {@link Transaction.State} is used to notify the implementation of {{@link * Transaction#commit} and {@link Transaction#rollback()}. * *

Extension

* * This class may be extended if your implementaiton wishes to capture additional information per * transaction. A database implementation using a JDBCContentState to store a JDBC Connection * remains a good example. * *

Subclasses may extend (not override) the following methods: * *

    *
  • {@link #flush()} - remember to call super.flush() *
  • {@link #close()} - remember to call super.close() *
* * Subclasses should also override {@link #copy()} to ensure any additional state they are keeping * is correctly accounted for. * * @author Jody Garnett (LISASoft) * @author Justin Deoliveira, The Open Planning Project * @version 8.0 * @since 2.6 */ public class ContentState { /** Transaction the state works from. */ protected Transaction tx; /** entry maintaining the state */ protected ContentEntry entry; // CACHE /** cached feature type */ protected SimpleFeatureType featureType; /** cached number of features */ protected int count = -1; /** cached bounds of features */ protected ReferencedEnvelope bounds; // EVENT NOTIFICATION SUPPORT /** * Even used for batch notification; used to collect the bounds and feature ids generated over * the course of a transaction. */ protected BatchFeatureEvent batchFeatureEvent; /** observers */ protected List listeners = Collections.synchronizedList(new ArrayList()); // TRANSACTION SUPPORT /** * Callback used to issue batch feature events when commit/rollback issued on the transaction. */ protected DiffTransactionState transactionState = new DiffTransactionState(this); /** * Creates a new state. * * @param entry The entry for the state. */ public ContentState(ContentEntry entry) { this.entry = entry; } /** * Creates a new state from a previous one. * *

All state from the specified state is copied. Therefore subclasses extending this * constructor should clone all mutable objects. * * @param state The existing state. */ protected ContentState(ContentState state) { this(state.getEntry()); featureType = state.featureType; count = state.count; bounds = state.bounds == null ? null : ReferencedEnvelope.reference(state.bounds); batchFeatureEvent = null; } /** The entry which maintains the state. */ public ContentEntry getEntry() { return entry; } /** The transaction associated with the state. */ public Transaction getTransaction() { return tx; } /** Sets the transaction associated with the state. */ public void setTransaction(Transaction tx) { this.tx = tx; if (tx != Transaction.AUTO_COMMIT) { tx.putState(this.entry, transactionState); } } /** The cached feature type. */ public final SimpleFeatureType getFeatureType() { return featureType; } /** Sets the cached feature type. */ public final void setFeatureType(SimpleFeatureType featureType) { this.featureType = featureType; } /** The cached number of features. */ public final int getCount() { return count; } /** Sets the cached number of features. */ public final void setCount(int count) { this.count = count; } /** The cached spatial extent. */ public final ReferencedEnvelope getBounds() { return bounds; } /** Sets the cached spatial extent. */ public final void setBounds(ReferencedEnvelope bounds) { this.bounds = bounds; } /** * Adds a listener for collection events. * * @param listener The listener to add */ public final void addListener(FeatureListener listener) { listeners.add(listener); } /** * Removes a listener for collection events. * * @param listener The listener to remove */ public final void removeListener(FeatureListener listener) { listeners.remove(listener); } public BatchFeatureEvent getBatchFeatureEvent() { return batchFeatureEvent; } /** Used to quickly test if any listeners are available. */ public final boolean hasListener() { if (!listeners.isEmpty()) { return true; } if (this.tx == Transaction.AUTO_COMMIT && this.entry.state.size() > 1) { // We are the auto commit state; and there is at least one other thread to notify return true; } return false; } /** * Creates a FeatureEvent indicating that the provided feature has been changed. * *

This method is provided to simplify event notification for implementors by constructing a * FeatureEvent internally (and then only if listeners are interested). You may find it easier * to construct your own FeatureEvent using {{@link #hasListener()} to check if you need to fire * events at all. * * @param source FeatureSource responsible for the change * @param feature The updated feature * @param before the bounds of the feature before the change */ public void fireFeatureUpdated( FeatureSource source, Feature feature, ReferencedEnvelope before) { if (feature == null) { return; // nothing changed } if (listeners.isEmpty() && tx != Transaction.AUTO_COMMIT) return; // nobody is listenting Filter filter = idFilter(feature); ReferencedEnvelope bounds = ReferencedEnvelope.reference(feature.getBounds()); if (bounds != null) { bounds.expandToInclude(before); } FeatureEvent event = new FeatureEvent(source, Type.CHANGED, bounds, filter); fireFeatureEvent(event); } /** Used to issue a Type.ADDED FeatureEvent indicating a new feature being created */ public final void fireFeatureAdded(FeatureSource source, Feature feature) { if (listeners.isEmpty() && tx != Transaction.AUTO_COMMIT) return; Filter filter = idFilter(feature); ReferencedEnvelope bounds = ReferencedEnvelope.reference(feature.getBounds()); FeatureEvent event = new FeatureEvent(source, Type.ADDED, bounds, filter); fireFeatureEvent(event); } public void fireFeatureRemoved(FeatureSource source, Feature feature) { if (listeners.isEmpty() && tx != Transaction.AUTO_COMMIT) return; Filter filter = idFilter(feature); ReferencedEnvelope bounds = ReferencedEnvelope.reference(feature.getBounds()); FeatureEvent event = new FeatureEvent(source, Type.REMOVED, bounds, filter); fireFeatureEvent(event); } /** Helper method or building fid filters. */ Filter idFilter(Feature feature) { FilterFactory ff = this.entry.dataStore.getFilterFactory(); Set fids = new HashSet(); fids.add(feature.getIdentifier()); return ff.id(fids); } /** * Used to issue a single FeatureEvent. * *

If this content state is used for Transaction.AUTO_COMMIT the notification will be passed * to all interested parties. * *

If not this event will be recored as part of a BatchFeatureEvent that will to be issued * using issueBatchFeatureEvent() */ public final void fireFeatureEvent(FeatureEvent event) { if (this.tx == Transaction.AUTO_COMMIT) { this.entry.notifiyFeatureEvent(this, event); } else { // we are not in auto-commit mode so we need to batch // up the changes for when the commit goes out if (batchFeatureEvent == null) { batchFeatureEvent = new BatchFeatureEvent(event.getFeatureSource()); } batchFeatureEvent.add(event); } if (listeners.isEmpty()) { return; } for (FeatureListener listener : listeners) { try { listener.changed(event); } catch (Throwable t) { this.entry.dataStore.LOGGER.log( Level.WARNING, "Problem issuing batch feature event " + event, t); } } } /** * Notifies all waiting listeners that a commit has been issued; this notification is also sent * to our */ public final void fireBatchFeatureEvent(boolean isCommit) { if (batchFeatureEvent == null) { return; } if (isCommit) { batchFeatureEvent.setType(Type.COMMIT); // This state already knows about the changes, let others know a modifications was made this.entry.notifiyFeatureEvent(this, batchFeatureEvent); } else { batchFeatureEvent.setType(Type.ROLLBACK); // Notify this state about the rollback, other transactions see no change for (FeatureListener listener : listeners) { try { listener.changed(batchFeatureEvent); } catch (Throwable t) { this.entry.dataStore.LOGGER.log( Level.WARNING, "Problem issuing batch feature event " + batchFeatureEvent, t); } } } batchFeatureEvent = null; } /** * Clears cached state. * *

This method does not affect any non-cached state. This method may be extended by * subclasses, but not overiden. */ public void flush() { featureType = null; count = -1; bounds = null; } /** * Clears all state. * *

Any resources that the state holds onto (like a database connection) should be closed or * disposes when this method is called. This method may be extended by subclasses, but not * overiden. */ public void close() { featureType = null; if (listeners != null) { listeners.clear(); listeners = null; } } /** * Copies the state. * *

Subclasses should override this method. Any mutable state objects should be cloned. * * @return A copy of the state. */ public ContentState copy() { return new ContentState(this); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy