Revision 36235 branches/v2_0_0_prep/libraries/libFMap_dal/src/org/gvsig/fmap/dal/feature/impl/DefaultFeatureIndex.java

View differences:

DefaultFeatureIndex.java
31 31
import java.util.ArrayList;
32 32
import java.util.List;
33 33

  
34
import org.slf4j.Logger;
35
import org.slf4j.LoggerFactory;
34
import org.cresques.Messages;
36 35

  
36
import org.gvsig.fmap.dal.DataStoreNotification;
37 37
import org.gvsig.fmap.dal.DataTypes;
38 38
import org.gvsig.fmap.dal.exception.DataException;
39 39
import org.gvsig.fmap.dal.exception.InitializeException;
40 40
import org.gvsig.fmap.dal.feature.Feature;
41 41
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
42 42
import org.gvsig.fmap.dal.feature.FeatureSet;
43
import org.gvsig.fmap.dal.feature.FeatureStore;
43 44
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
44 45
import org.gvsig.fmap.dal.feature.FeatureType;
45 46
import org.gvsig.fmap.dal.feature.exception.FeatureIndexException;
47
import org.gvsig.fmap.dal.feature.exception.FeatureIndexOperationException;
46 48
import org.gvsig.fmap.dal.feature.exception.InvalidFeatureIndexException;
47 49
import org.gvsig.fmap.dal.feature.spi.DefaultLongList;
48 50
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
49 51
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
50 52
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProvider;
51 53
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProviderServices;
52
import org.gvsig.i18n.Messages;
53
import org.gvsig.tools.ToolsLocator;
54 54
import org.gvsig.tools.dispose.DisposableIterator;
55 55
import org.gvsig.tools.dispose.DisposeUtils;
56
import org.gvsig.tools.observer.impl.BaseWeakReferencingObservable;
57
import org.gvsig.tools.task.SimpleTaskStatus;
58
import org.gvsig.tools.task.TaskStatusManager;
56
import org.gvsig.tools.observer.Observer;
57
import org.gvsig.tools.observer.WeakReferencingObservable;
58
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
59
import org.gvsig.tools.task.AbstractMonitorableTask;
60
import org.gvsig.tools.task.CancellableTask;
61
import org.gvsig.tools.task.MonitorableTask;
59 62

  
60 63
/**
61 64
 * Default feature index provider services.
62 65
 * 
63
 * @author jyarza
64
 * 
66
 * @author gvSIG team
65 67
 */
66
public class DefaultFeatureIndex extends BaseWeakReferencingObservable
67
    implements FeatureIndexProviderServices {
68
public class DefaultFeatureIndex implements FeatureIndexProviderServices,
69
    WeakReferencingObservable {
68 70

  
69
    private static final Logger LOG = LoggerFactory
70
        .getLogger(DefaultFeatureIndex.class);
71

  
72 71
    private final FeatureStoreProviderServices featureStore;
73 72
    private final FeatureType featureType;
74 73
    private final String attributeName;
......
77 76
    private final FeatureIndexProvider indexProvider;
78 77
    private List attributeNames;
79 78

  
80
    private boolean filling = false;
79
    private Object featureOperationTaskLock = new Object();
80
    private FeatureIndexOperationTask featureIndexTask;
81 81

  
82 82
    private boolean valid = true;
83 83

  
84
    private DelegateWeakReferencingObservable observable =
85
        new DelegateWeakReferencingObservable(this);
86

  
84 87
    public DefaultFeatureIndex(FeatureStoreProviderServices featureStore,
85 88
        FeatureType featureType, FeatureIndexProvider indexProvider,
86 89
        String attributeName, String indexName) {
90

  
87 91
        if (featureStore == null) {
88 92
            throw new IllegalArgumentException("featureStore cannot be null.");
89 93
        }
......
133 137
        return featureType;
134 138
    }
135 139

  
136
    public final String getName() {
137
        return indexName;
138
    }
139

  
140 140
    public final String getAttributeName() {
141 141
        return attributeName;
142 142
    }
......
145 145
        return dataType;
146 146
    }
147 147

  
148
    public final void fill() throws FeatureIndexException {
149
        fill(false);
150
    }
148
    /**
149
     * {@link MonitorableTask} and {@link CancellableTask} to perform long
150
     * operations on the index: filling and inserting or deleting a feature set.
151
     * 
152
     * @author gvSIG Team
153
     * @version $Id$
154
     */
155
    private static class FeatureIndexOperationTask extends
156
        AbstractMonitorableTask {
151 157

  
152
    public void fill(boolean background)
153
        throws FeatureIndexException {
154
        if (background) {
155
            filling = true;
158
        private final DefaultFeatureIndex index;
156 159

  
157
            Runnable task = new Runnable() {
160
        public static final int OP_FILL = 0;
161
        public static final int OP_INSERT_FSET = 1;
162
        public static final int OP_DELETE_FSET = 2;
158 163

  
159
                public void run() {
160
                    try {
161
                        clearAndFill();
162
                    } catch (DataException e) {
163
                        LOG.error("Error filling index: " + this, e);
164
        private static final String[] OP_NAMES = { //
165
            Messages.getText("filling_index"), // OP_FILL
166
                Messages.getText("updating_index"), // OP_INSERT_FSET
167
                Messages.getText("updating_index"), // OP_DELETE_FSET
168
            };
169

  
170
        private final int operation;
171

  
172
        private final FeatureSet data;
173

  
174
        private final Observer operationObserver;
175

  
176
        private final FeatureStore store;
177

  
178
        /**
179
         * Creates a new {@link FeatureIndexOperationTask}
180
         * 
181
         * @param index
182
         *            to operate on
183
         * @param store
184
         *            to index data from
185
         * @param operation
186
         *            to perform: {@link #OP_FILL}, {@link #OP_INSERT_FSET} or
187
         *            {@link #OP_DELETE_FSET}
188
         * @param data
189
         *            feature set to insert or delete in the insert or delete
190
         *            operations
191
         * @param operationObserver
192
         *            to be notified when the operation starts, finishes, is
193
         *            cancelled or has finished with errors
194
         */
195
        protected FeatureIndexOperationTask(DefaultFeatureIndex index,
196
            FeatureStore store, int operation, FeatureSet data,
197
            Observer operationObserver) {
198
            super(OP_NAMES[operation]);
199
            this.index = index;
200
            this.store = store;
201
            this.operation = operation;
202
            this.data = data;
203
            this.operationObserver = operationObserver;
204
            setDaemon(true);
205
            setPriority(MIN_PRIORITY);
206
        }
207

  
208
        public void run() {
209
            try {
210
                switch (operation) {
211
                case OP_FILL:
212
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
213
                    clearAndFill();
214
                    break;
215

  
216
                case OP_INSERT_FSET:
217
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
218
                    insert(data);
219
                    break;
220

  
221
                case OP_DELETE_FSET:
222
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
223
                    delete(data);
224
                    break;
225
                }
226
            } catch (Exception e) {
227
                Exception fioex =
228
                    new FeatureIndexOperationException(index,
229
                        OP_NAMES[operation], e);
230
                notify(FeatureStoreNotification.INDEX_FILLING_ERROR, fioex);
231
                throw new RuntimeException(fioex);
232
            } finally {
233
                index.removeTask(this);
234
            }
235
        }
236

  
237
        /**
238
         * Clears the index data and fills it again.
239
         */
240
        private void clearAndFill() throws DataException {
241
            FeatureSet set = null;
242
            try {
243
                set = store.getFeatureSet();
244
                synchronized (index) {
245
                    if (isCancellationRequested()) {
246
                        cancel();
247
                        return;
164 248
                    }
249
                    index.clear();
250
                    if (isCancellationRequested()) {
251
                        cancel();
252
                        return;
253
                    }
254
                    insert(set);
255
                    index.setValid(true);
165 256
                }
166
            };
257
            } finally {
258
                DisposeUtils.dispose(set);
259
            }
260
        }
167 261

  
168
            Thread thread = new Thread(task, "Fill index data");
169
            thread.setDaemon(true);
170
            // thread.setPriority(-1);
171
            thread.start();
172
        } else {
262
        private void insert(FeatureSet data) throws DataException {
263
            DisposableIterator it = null;
264
            taskStatus.setRangeOfValues(0, data.getSize());
265
            taskStatus.add();
266
            long counter = 0;
173 267
            try {
174
                clearAndFill();
268
                it = data.fastIterator();
269
                synchronized (index) {
270
                    while (it.hasNext()) {
271
                        if (isCancellationRequested()) {
272
                            index.clear();
273
                            cancel();
274
                            return;
275
                        }
276
                        Feature feat = (Feature) it.next();
277
                        index.insert(feat);
278
                        taskStatus.setCurValue(counter++);
279
                    }
280
                    notify(FeatureStoreNotification.INDEX_FILLING_SUCCESS);
281
                }
282
                taskStatus.terminate();
283
            } catch (RuntimeException e) {
284
                taskStatus.abort();
285
                throw e;
175 286
            } catch (DataException e) {
287
                taskStatus.abort();
288
                throw e;
289
            } finally {
290
                DisposeUtils.dispose(it);
291
                taskStatus.remove();
292
            }
293
        }
294

  
295
        private void delete(FeatureSet data) throws FeatureIndexException {
296
            DisposableIterator it = null;
297
            try {
298
                it = data.fastIterator();
299
                synchronized (index) {
300
                    while (it.hasNext()) {
301
                        if (isCancellationRequested()) {
302
                            cancel();
303
                            return;
304
                        }
305
                        Feature feat = (Feature) it.next();
306
                        index.delete(feat);
307
                    }
308
                    notify(FeatureStoreNotification.INDEX_FILLING_SUCCESS);
309
                }
310
            } catch (DataException e) {
176 311
                throw new FeatureIndexException(e);
312
            } finally {
313
                DisposeUtils.dispose(it);
177 314
            }
178 315
        }
179
    }
180 316

  
181
    /**
182
     * Clears the index data and fills it again.
183
     */
184
    private synchronized void clearAndFill() throws DataException {
185
        FeatureSet set = null;
186
        try {
187
            filling = true;
188
            set =
189
                getFeatureStoreProviderServices().getFeatureStore()
190
                    .getFeatureSet();
191
            clear();
192
            doInsert(set);
193
            filling = false;
194
            notify(this, new DefaultFeatureStoreNotification(
195
                getFeatureStoreProviderServices().getFeatureStore(),
196
                FeatureStoreNotification.INDEX_FILLED_SUCCESSFULLY, this));
197
        } catch (DataException e) {
198
            filling = false;
199
            setValid(false);
200
            notify(this, new DefaultFeatureStoreNotification(
201
                getFeatureStoreProviderServices().getFeatureStore(),
202
                FeatureStoreNotification.INDEX_FILLING_ERROR, e));
203
            throw new FeatureIndexException(e);
204
        } finally {
205
            DisposeUtils.dispose(set);
317
        private void cancel() {
318
            notify(FeatureStoreNotification.INDEX_FILLING_CANCELLED);
319
            taskStatus.cancel();
206 320
        }
207
    }
208 321

  
209
    public synchronized final void insert(FeatureSet data) throws DataException {
210
        if (!isValid()) {
211
            throw new InvalidFeatureIndexException();
322
        public void notifyOperationObserver(DataStoreNotification notification) {
323
            if (this.operationObserver != null) {
324
                this.operationObserver.update(index, notification);
325
            }
212 326
        }
213
        try {
214
            filling = true;
215
            doInsert(data);
216
        } finally {
217
            filling = false;
327

  
328
        private void notify(String notificationType) {
329
            DataStoreNotification notification =
330
                new DefaultFeatureStoreNotification(store, notificationType,
331
                    index);
332
            notifyOperationObserver(notification);
333
            index.notifyObservers(notification);
218 334
        }
335

  
336
        private void notify(String notificationType, Exception exception) {
337
            DataStoreNotification notification =
338
                new DefaultFeatureStoreNotification(store, notificationType,
339
                    exception);
340
            notifyOperationObserver(notification);
341
            index.notifyObservers(notification);
342
        }
219 343
    }
220 344

  
221
    private void doInsert(FeatureSet data) throws DataException {
222
        DisposableIterator it = null;
223
        TaskStatusManager statusManager = ToolsLocator.getTaskStatusManager();
224
        SimpleTaskStatus status =
225
            statusManager.createDefaultSimpleTaskStatus(Messages
226
                .getText("filling_index"));
227
        status.setCancellable(false);
228
        status.setRangeOfValues(0, data.getSize());
229
        status.add();
230
        long counter = 0;
231
        try {
232
            it = data.fastIterator();
233
            while (it.hasNext()) {
234
                Feature feat = (Feature) it.next();
235
                doInsert(feat);
236
                status.setCurValue(counter++);
345
    private FeatureIndexOperationTask createIndexTask(int operation,
346
        FeatureSet data, Observer observer) {
347
        synchronized (featureOperationTaskLock) {
348
            if (featureIndexTask != null) {
349
                this.featureIndexTask.cancelRequest();
350
                removeTask(this.featureIndexTask);
237 351
            }
238
        } catch (RuntimeException e) {
239
            status.abort();
240
            throw e;
241
        } finally {
242
            DisposeUtils.dispose(it);
243
            status.remove();
352
            FeatureIndexOperationTask fillingTask =
353
                new FeatureIndexOperationTask(this,
354
                    featureStore.getFeatureStore(), operation, data, observer);
355
            this.featureIndexTask = fillingTask;
356
            return fillingTask;
244 357
        }
245 358
    }
246 359

  
247
    public synchronized final void insert(Feature feat) {
248
        if (!isValid()) {
249
            throw new RuntimeException(new InvalidFeatureIndexException());
360
    private void removeTask(FeatureIndexOperationTask task) {
361
        synchronized (featureOperationTaskLock) {
362
            // Remove if it is not null and the same task
363
            if (this.featureIndexTask == task) {
364
                featureIndexTask = null;
365
            }
250 366
        }
251
        try {
252
            filling = true;
253
            doInsert(feat);
254
        } finally {
255
            filling = false;
367
    }
368

  
369
    public void fill() throws FeatureIndexException {
370
        fill(false, null);
371
    }
372

  
373
    public void fill(boolean background, Observer observer)
374
        throws FeatureIndexException {
375
        FeatureIndexOperationTask task =
376
            createIndexTask(FeatureIndexOperationTask.OP_FILL, null, observer);
377
        if (background) {
378
            task.start();
379
        } else {
380
            task.run();
256 381
        }
257 382
    }
258 383

  
259
    private void doInsert(Feature feat) {
384
    public final void insert(FeatureSet data) throws DataException {
385
        if (!isValid()) {
386
            throw new InvalidFeatureIndexException();
387
        }
388
        FeatureIndexOperationTask task =
389
            createIndexTask(FeatureIndexOperationTask.OP_INSERT_FSET, data,
390
                null);
391
        task.run();
392
    }
393

  
394
    public synchronized final void insert(Feature feat) throws DataException {
260 395
        try {
261
            indexProvider.insert(feat.get(attributeName),
396
            getIndexProvider().insert(feat.get(getAttributeName()),
262 397
                (FeatureReferenceProviderServices) feat.getReference());
263 398
        } catch (NullPointerException e) {
264
            throw new IllegalArgumentException(
265
                "Feature does not contain a column with name " + attributeName);
399
            throw new IllegalArgumentException("Could not add Feature: " + feat
400
                + " to index " + this
401
                + ". It does not contain a column with name "
402
                + getAttributeName(), e);
266 403
        } catch (ClassCastException e) {
267
            throw new IllegalArgumentException(
268
                "Attribute data type is not valid.");
404
            throw new IllegalArgumentException("Could not add Feature: " + feat
405
                + " to index " + this + ". Attribute " + getAttributeName()
406
                + " data type is not valid.", e);
269 407
        }
270 408
    }
271 409

  
272
    public FeatureSet getMatchFeatureSet(Object value)
410
    public final void delete(FeatureSet data) throws FeatureIndexException {
411
        if (!isValid()) {
412
            throw new InvalidFeatureIndexException();
413
        }
414
        FeatureIndexOperationTask task =
415
            createIndexTask(FeatureIndexOperationTask.OP_DELETE_FSET, data,
416
                null);
417
        task.run();
418
    }
419

  
420
    public synchronized final void delete(Feature feat) throws DataException {
421
        getIndexProvider().delete(feat.get(getAttributeName()),
422
            (FeatureReferenceProviderServices) feat.getReference());
423
    }
424

  
425
    private synchronized void clear() throws DataException {
426
        getIndexProvider().clear();
427
    }
428

  
429
    public synchronized FeatureSet getMatchFeatureSet(Object value)
273 430
        throws FeatureIndexException {
274 431
        if (!isValid()) {
275 432
            throw new InvalidFeatureIndexException();
......
278 435
            indexProvider.match(value)));
279 436
    }
280 437

  
281
    public FeatureSet getRangeFeatureSet(Object value1, Object value2)
282
        throws FeatureIndexException {
438
    public synchronized FeatureSet getRangeFeatureSet(Object value1,
439
        Object value2) throws FeatureIndexException {
283 440
        if (!isValid()) {
284 441
            throw new InvalidFeatureIndexException();
285 442
        }
......
287 444
            indexProvider.range(value1, value2)));
288 445
    }
289 446

  
290
    public FeatureSet getNearestFeatureSet(int count, Object value)
447
    public synchronized FeatureSet getNearestFeatureSet(int count, Object value)
291 448
        throws FeatureIndexException {
292 449
        if (!isValid()) {
293 450
            throw new InvalidFeatureIndexException();
......
296 453
            indexProvider.nearest(count, value)));
297 454
    }
298 455

  
299
    public FeatureSet getNearestFeatureSet(int count, Object value,
300
        Object tolerance) throws FeatureIndexException {
456
    public synchronized FeatureSet getNearestFeatureSet(int count,
457
        Object value, Object tolerance) throws FeatureIndexException {
301 458
        if (!isValid()) {
302 459
            throw new InvalidFeatureIndexException();
303 460
        }
......
310 467
        indexProvider.initialize();
311 468
    }
312 469

  
313
    public synchronized final void delete(Feature feat) {
314
        if (!isValid()) {
315
            throw new RuntimeException(new InvalidFeatureIndexException());
316
        }
317
        try {
318
            filling = true;
319
            doDelete(feat);
320
        } finally {
321
            filling = false;
322
        }
323
    }
324

  
325
    private void doDelete(Feature feat) {
326
        indexProvider.delete(feat.get(this.attributeName),
327
            (FeatureReferenceProviderServices) feat.getReference());
328
    }
329

  
330
    public synchronized final void delete(FeatureSet data)
331
        throws FeatureIndexException {
332
        if (!isValid()) {
333
            throw new InvalidFeatureIndexException();
334
        }
335
        DisposableIterator it = null;
336
        try {
337
            filling = true;
338
            it = data.fastIterator();
339
            while (it.hasNext()) {
340
                Feature feat = (Feature) it.next();
341
                doDelete(feat);
342
            }
343
        } catch (DataException e) {
344
            throw new FeatureIndexException(e);
345
        } finally {
346
            filling = false;
347
            DisposeUtils.dispose(it);
348
        }
349
    }
350

  
351
    private void clear() throws DataException {
352
        indexProvider.clear();
353
    }
354

  
355 470
    public List getAttributeNames() {
356 471
        return attributeNames;
357 472
    }
......
381 496
    }
382 497

  
383 498
    public boolean isFilling() {
384
        return filling;
499
        synchronized (featureOperationTaskLock) {
500
            return featureIndexTask != null;
501
        }
385 502
    }
386 503

  
387 504
    public boolean isValid() {
388
        return !isFilling() && valid;
505
        synchronized (featureOperationTaskLock) {
506
            return !isFilling() && valid;
507
        }
389 508
    }
390 509

  
391 510
    public void setValid(boolean valid) {
392
        this.valid = valid;
511
        synchronized (featureOperationTaskLock) {
512
            this.valid = valid;
513
        }
393 514
    }
394 515

  
395 516
    public String toString() {
......
397 518
            + ", for the FeatureType: " + featureType + ", and the attribute: "
398 519
            + attributeName;
399 520
    }
521

  
522
    public void notifyObservers(Object notification) {
523
        observable.notifyObservers(notification);
524
    }
525

  
526
    public String getName() {
527
        return indexName;
528
    }
529

  
530
    private FeatureIndexProvider getIndexProvider() {
531
        return indexProvider;
532
    }
533

  
534
    public void addObserver(Observer observer) {
535
        observable.addObserver(observer);
536
    }
537

  
538
    public void deleteObserver(Observer observer) {
539
        observable.deleteObserver(observer);
540
    }
541

  
542
    public void deleteObservers() {
543
        observable.deleteObservers();
544
    }
545

  
400 546
}

Also available in: Unified diff