PageRenderTime 106ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/projects/geotools-9.2/modules/library/data/src/main/java/org/geotools/data/store/ContentState.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 467 lines | 184 code | 45 blank | 238 comment | 35 complexity | 91e2b18cc0c2d794ddc75b4518786b53 MD5 | raw file
  1. /*
  2. * GeoTools - The Open Source Java GIS Toolkit
  3. * http://geotools.org
  4. *
  5. * (C) 2006-2008, Open Source Geospatial Foundation (OSGeo)
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation;
  10. * version 2.1 of the License.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. package org.geotools.data.store;
  18. import java.util.ArrayList;
  19. import java.util.Collections;
  20. import java.util.HashSet;
  21. import java.util.List;
  22. import java.util.Set;
  23. import java.util.logging.Level;
  24. import org.geotools.data.BatchFeatureEvent;
  25. import org.geotools.data.Diff;
  26. import org.geotools.data.FeatureEvent;
  27. import org.geotools.data.FeatureEvent.Type;
  28. import org.geotools.data.FeatureListener;
  29. import org.geotools.data.FeatureSource;
  30. import org.geotools.data.Transaction;
  31. import org.geotools.geometry.jts.ReferencedEnvelope;
  32. import org.opengis.feature.Feature;
  33. import org.opengis.feature.simple.SimpleFeatureType;
  34. import org.opengis.filter.Filter;
  35. import org.opengis.filter.FilterFactory;
  36. import org.opengis.filter.identity.FeatureId;
  37. /**
  38. * The state of an entry in a datastore, maintained on a per-transaction basis. For information
  39. * maintained on a typeName basis see {@link ContentEntry}.
  40. *
  41. * <h3>Data Cache Synchronization Required</h2>
  42. * <p>
  43. * The default ContentState implementation maintains cached values on a per transaction
  44. * basis:
  45. * <ul>
  46. * <li>feature type ({@link #getFeatureType()}
  47. * <li>number of features ({@link #getCount()}
  48. * <li>spatial extent ({@link #getBounds()}
  49. * </ul>
  50. * Other types of state depend on the data format and must be handled by a subclass.
  51. * <p>
  52. * This class is a "data object" used to store values and is not thread safe. It is up to
  53. * clients of this class to ensure that values are set in a thread-safe / synchronized
  54. * manner. For example:
  55. * <pre>
  56. * <code>
  57. * ContentState state = ...;
  58. *
  59. * //get the count
  60. * int count = state.getCount();
  61. * if ( count == -1 ) {
  62. * synchronized ( state ) {
  63. * count = calculateCount();
  64. * state.setCount( count );
  65. * }
  66. * }</code></pre>
  67. * </p>
  68. * <h3>Event Notification</h3>
  69. * The list of listeners interested in event notification as features are modified and edited
  70. * is stored as part of ContentState. Each notification is considered to be broadcast from a
  71. * specific FeatureSource. Since several ContentFeatureStores can be on the same transaction
  72. * (and thus share a ContentState) the fire methods listed here require you pass in the
  73. * FeatureSource making the change; this requires that your individual FeatureWriters keep a
  74. * pointer to the ContentFeatureStore which created them.
  75. * <p>
  76. * You may also make use of {@link ContentFeatureSource#canEvent} value of {@code false} allowing
  77. * the base ContentFeatureStore class to take responsibility for sending event notifications.
  78. *
  79. * <h3>Transaction Independence</h3>
  80. * <p>
  81. * The default ContentState implementation also supports the handling of
  82. * {@link ContentFeatureSource#canTransaction} value of {@code false}. The implementation asks
  83. * ContentState to store a {@link Diff} which is used to record any modifications made until
  84. * commit is called.
  85. * <p>
  86. * Internally a {@link Transaction.State} is used to notify the implementation of
  87. * {{@link Transaction#commit} and {@link Transaction#rollback()}.
  88. *
  89. * <h3>Extension</h3>
  90. * This class may be extended if your implementaiton wishes to capture additional information
  91. * per transaction. A database implementation using a JDBCContentState to store a JDBC Connection
  92. * remains a good example.
  93. * <p>
  94. * Subclasses may extend (not override) the following methods:
  95. * <ul>
  96. * <li>{@link #flush()} - remember to call super.flush()
  97. * <li>{@link #close()} - remember to call super.close()
  98. * </ul>
  99. * Subclasses should also override {@link #copy()} to ensure any additional state they are keeping
  100. * is correctly accounted for.
  101. * </p>
  102. *
  103. * @author Jody Garnett (LISASoft)
  104. * @author Justin Deoliveira, The Open Planning Project
  105. *
  106. * @version 8.0
  107. * @since 2.6
  108. * @source $URL$
  109. */
  110. public class ContentState {
  111. /**
  112. * Transaction the state works from.
  113. */
  114. protected Transaction tx;
  115. /**
  116. * entry maintaining the state
  117. */
  118. protected ContentEntry entry;
  119. // CACHE
  120. /**
  121. * cached feature type
  122. */
  123. protected SimpleFeatureType featureType;
  124. /**
  125. * cached number of features
  126. */
  127. protected int count = -1;
  128. /**
  129. * cached bounds of features
  130. */
  131. protected ReferencedEnvelope bounds;
  132. // EVENT NOTIFICATION SUPPORT
  133. /**
  134. * Even used for batch notification; used to collect the bounds and feature ids generated
  135. * over the course of a transaction.
  136. */
  137. protected BatchFeatureEvent batchFeatureEvent;
  138. /**
  139. * observers
  140. */
  141. protected List<FeatureListener> listeners = Collections.synchronizedList(new ArrayList<FeatureListener>());
  142. // TRANSACTION SUPPORT
  143. /**
  144. * Callback used to issue batch feature events when commit/rollback issued
  145. * on the transaction.
  146. */
  147. protected DiffTransactionState transactionState = new DiffTransactionState(this);
  148. /**
  149. * Creates a new state.
  150. *
  151. * @param entry The entry for the state.
  152. */
  153. public ContentState(ContentEntry entry) {
  154. this.entry = entry;
  155. }
  156. /**
  157. * Creates a new state from a previous one.
  158. * <p>
  159. * All state from the specified <tt>state</tt> is copied. Therefore subclasses
  160. * extending this constructor should clone all mutable objects.
  161. * </p>
  162. *
  163. * @param state The existing state.
  164. */
  165. protected ContentState(ContentState state) {
  166. this(state.getEntry());
  167. featureType = state.featureType;
  168. count = state.count;
  169. bounds = state.bounds == null ? null : new ReferencedEnvelope( state.bounds );
  170. batchFeatureEvent = null;
  171. }
  172. /**
  173. * The entry which maintains the state.
  174. */
  175. public ContentEntry getEntry() {
  176. return entry;
  177. }
  178. /**
  179. * The transaction associated with the state.
  180. */
  181. public Transaction getTransaction() {
  182. return tx;
  183. }
  184. /**
  185. * Sets the transaction associated with the state.
  186. */
  187. public void setTransaction(Transaction tx) {
  188. this.tx = tx;
  189. if (tx != Transaction.AUTO_COMMIT) {
  190. tx.putState(this.entry, transactionState);
  191. }
  192. }
  193. /**
  194. * The cached feature type.
  195. */
  196. public final SimpleFeatureType getFeatureType() {
  197. return featureType;
  198. }
  199. /**
  200. * Sets the cached feature type.
  201. */
  202. public final void setFeatureType(SimpleFeatureType featureType) {
  203. this.featureType = featureType;
  204. }
  205. /**
  206. * The cached number of features.
  207. *
  208. */
  209. public final int getCount() {
  210. return count;
  211. }
  212. /**
  213. * Sets the cached number of features.
  214. */
  215. public final void setCount(int count) {
  216. this.count = count;
  217. }
  218. /**
  219. * The cached spatial extent.
  220. */
  221. public final ReferencedEnvelope getBounds() {
  222. return bounds;
  223. }
  224. /**
  225. * Sets the cached spatial extent.
  226. */
  227. public final void setBounds(ReferencedEnvelope bounds) {
  228. this.bounds = bounds;
  229. }
  230. /**
  231. * Adds a listener for collection events.
  232. *
  233. * @param listener The listener to add
  234. */
  235. public final void addListener(FeatureListener listener) {
  236. listeners.add(listener);
  237. }
  238. /**
  239. * Removes a listener for collection events.
  240. *
  241. * @param listener The listener to remove
  242. */
  243. public final void removeListener(FeatureListener listener) {
  244. listeners.remove(listener);
  245. }
  246. public BatchFeatureEvent getBatchFeatureEvent() {
  247. return batchFeatureEvent;
  248. }
  249. /**
  250. * Used to quickly test if any listeners are available.
  251. *
  252. * @return
  253. */
  254. public final boolean hasListener() {
  255. if (!listeners.isEmpty()) {
  256. return true;
  257. }
  258. if (this.tx == Transaction.AUTO_COMMIT && this.entry.state.size() > 1) {
  259. // We are the auto commit state; and there is at least one other thread to notify
  260. return true;
  261. }
  262. return false;
  263. }
  264. /**
  265. * Creates a FeatureEvent indicating that the provided feature has been changed.
  266. * <p>
  267. * This method is provided to simplify event notification for implementors by constructing
  268. * a FeatureEvent internally (and then only if listeners are interested). You may find it
  269. * easier to construct your own FeatureEvent using {{@link #hasListener()} to check if
  270. * you need to fire events at all.
  271. *
  272. * @param source FeatureSource responsible for the change
  273. * @param feature The updated feature
  274. * @param before the bounds of the feature before the change
  275. */
  276. public void fireFeatureUpdated(FeatureSource<?, ?> source, Feature feature,
  277. ReferencedEnvelope before) {
  278. if( feature == null){
  279. return; // nothing changed
  280. }
  281. if (listeners.isEmpty() && tx != Transaction.AUTO_COMMIT)
  282. return; // nobody is listenting
  283. Filter filter = idFilter(feature);
  284. ReferencedEnvelope bounds = new ReferencedEnvelope(feature.getBounds());
  285. if( bounds != null ){
  286. bounds.expandToInclude(before);
  287. }
  288. FeatureEvent event = new FeatureEvent(source, Type.CHANGED, bounds, filter);
  289. fireFeatureEvent(event);
  290. }
  291. /**
  292. * Used to issue a Type.ADDED FeatureEvent indicating a new feature being created
  293. *
  294. * @param source
  295. * @param feature
  296. */
  297. public final void fireFeatureAdded(FeatureSource<?, ?> source, Feature feature) {
  298. if (listeners.isEmpty() && tx != Transaction.AUTO_COMMIT)
  299. return;
  300. Filter filter = idFilter(feature);
  301. ReferencedEnvelope bounds = new ReferencedEnvelope(feature.getBounds());
  302. FeatureEvent event = new FeatureEvent(source, Type.ADDED, bounds, filter);
  303. fireFeatureEvent(event);
  304. }
  305. public void fireFeatureRemoved(FeatureSource<?, ?> source, Feature feature) {
  306. if (listeners.isEmpty() && tx != Transaction.AUTO_COMMIT)
  307. return;
  308. Filter filter = idFilter(feature);
  309. ReferencedEnvelope bounds = new ReferencedEnvelope(feature.getBounds());
  310. FeatureEvent event = new FeatureEvent(source, Type.REMOVED, bounds, filter);
  311. fireFeatureEvent(event);
  312. }
  313. /**
  314. * Helper method or building fid filters.
  315. */
  316. Filter idFilter(Feature feature) {
  317. FilterFactory ff = this.entry.dataStore.getFilterFactory();
  318. Set<FeatureId> fids = new HashSet<FeatureId>();
  319. fids.add(feature.getIdentifier());
  320. return ff.id(fids);
  321. }
  322. /**
  323. * Used to issue a single FeatureEvent.
  324. * <p>
  325. * If this content state is used for Transaction.AUTO_COMMIT the notification will be passed to
  326. * all interested parties.
  327. * <p>
  328. * If not this event will be recored as part of a BatchFeatureEvent that will to be issued using
  329. * issueBatchFeatureEvent()
  330. *
  331. * @param event
  332. */
  333. public final void fireFeatureEvent(FeatureEvent event) {
  334. if (this.tx == Transaction.AUTO_COMMIT) {
  335. this.entry.notifiyFeatureEvent(this, event);
  336. } else {
  337. // we are not in auto-commit mode so we need to batch
  338. // up the changes for when the commit goes out
  339. if (batchFeatureEvent == null) {
  340. batchFeatureEvent = new BatchFeatureEvent(event.getFeatureSource());
  341. }
  342. batchFeatureEvent.add(event);
  343. }
  344. if (listeners.isEmpty()) {
  345. return;
  346. }
  347. for (FeatureListener listener : listeners) {
  348. try {
  349. listener.changed(event);
  350. } catch (Throwable t) {
  351. this.entry.dataStore.LOGGER.log(Level.WARNING,
  352. "Problem issuing batch feature event " + event, t);
  353. }
  354. }
  355. }
  356. /**
  357. * Notifies all waiting listeners that a commit has been issued; this notification is also sent
  358. * to our
  359. */
  360. public final void fireBatchFeatureEvent(boolean isCommit) {
  361. if (batchFeatureEvent == null) {
  362. return;
  363. }
  364. if (listeners.isEmpty()) {
  365. return;
  366. }
  367. if (isCommit) {
  368. batchFeatureEvent.setType(Type.COMMIT);
  369. } else {
  370. batchFeatureEvent.setType(Type.ROLLBACK);
  371. }
  372. for (FeatureListener listener : listeners) {
  373. try {
  374. listener.changed(batchFeatureEvent);
  375. } catch (Throwable t) {
  376. this.entry.dataStore.LOGGER.log(Level.WARNING,
  377. "Problem issuing batch feature event " + batchFeatureEvent, t);
  378. }
  379. }
  380. // Let others know a modifications was made
  381. this.entry.notifiyFeatureEvent(this, batchFeatureEvent);
  382. batchFeatureEvent = null;
  383. }
  384. /**
  385. * Clears cached state.
  386. * <p>
  387. * This method does not affect any non-cached state. This method may be extended by subclasses,
  388. * but not overiden.
  389. * </p>
  390. */
  391. public void flush() {
  392. featureType = null;
  393. count = -1;
  394. bounds = null;
  395. }
  396. /**
  397. * Clears all state.
  398. * <p>
  399. * Any resources that the state holds onto (like a database connection) should be closed or
  400. * disposes when this method is called. This method may be extended by subclasses, but not
  401. * overiden.
  402. * </p>
  403. */
  404. public void close() {
  405. featureType = null;
  406. if (listeners != null) {
  407. listeners.clear();
  408. listeners = null;
  409. }
  410. }
  411. /**
  412. * Copies the state.
  413. * <p>
  414. * Subclasses shold override this method. Any mutable state objects should be cloned.
  415. * </p>
  416. *
  417. * @return A copy of the state.
  418. */
  419. public ContentState copy() {
  420. return new ContentState(this);
  421. }
  422. }