Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.impl / src / main / java / org / gvsig / fmap / dal / feature / impl / DefaultFeatureIndex.java @ 40559

History | View | Annotate | Download (20 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
/*
25
 * AUTHORS (In addition to CIT):
26
 * 2008 {{Company}}   {{Task}}
27
 */
28

    
29
package org.gvsig.fmap.dal.feature.impl;
30

    
31
import java.io.File;
32
import java.util.ArrayList;
33
import java.util.List;
34

    
35
import org.cresques.Messages;
36
import org.gvsig.fmap.dal.DataStoreNotification;
37
import org.gvsig.fmap.dal.DataTypes;
38
import org.gvsig.fmap.dal.exception.DataException;
39
import org.gvsig.fmap.dal.exception.InitializeException;
40
import org.gvsig.fmap.dal.feature.Feature;
41
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
42
import org.gvsig.fmap.dal.feature.FeatureSet;
43
import org.gvsig.fmap.dal.feature.FeatureStore;
44
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
45
import org.gvsig.fmap.dal.feature.FeatureType;
46
import org.gvsig.fmap.dal.feature.exception.FeatureIndexException;
47
import org.gvsig.fmap.dal.feature.exception.FeatureIndexOperationException;
48
import org.gvsig.fmap.dal.feature.exception.InvalidFeatureIndexException;
49
import org.gvsig.fmap.dal.feature.spi.DefaultLongList;
50
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
51
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
52
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProvider;
53
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProviderServices;
54
import org.gvsig.fmap.geom.primitive.NullGeometry;
55
import org.gvsig.tools.dispose.DisposableIterator;
56
import org.gvsig.tools.dispose.DisposeUtils;
57
import org.gvsig.tools.dispose.impl.AbstractDisposable;
58
import org.gvsig.tools.exception.BaseException;
59
import org.gvsig.tools.observer.Observer;
60
import org.gvsig.tools.observer.WeakReferencingObservable;
61
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
62
import org.gvsig.tools.task.AbstractMonitorableTask;
63
import org.gvsig.tools.task.CancellableTask;
64
import org.gvsig.tools.task.MonitorableTask;
65
import org.slf4j.Logger;
66
import org.slf4j.LoggerFactory;
67

    
68
/**
69
 * Default feature index provider services.
70
 * 
71
 * @author gvSIG team
72
 */
73
public class DefaultFeatureIndex extends AbstractDisposable implements
74
    FeatureIndexProviderServices,
75
    WeakReferencingObservable {
76

    
77
    private static final Logger LOG = LoggerFactory
78
        .getLogger(DefaultFeatureIndex.class);
79

    
80
    private final FeatureStoreProviderServices featureStore;
81
    private final FeatureType featureType;
82
    private final String attributeName;
83
    private final String indexName;
84
    private final int dataType;
85
    private final FeatureIndexProvider indexProvider;
86
    private List attributeNames;
87

    
88
    private Object featureOperationTaskLock = new Object();
89
    private FeatureIndexOperationTask featureIndexTask;
90

    
91
    private boolean valid = true;
92

    
93
    private DelegateWeakReferencingObservable observable =
94
        new DelegateWeakReferencingObservable(this);
95

    
96
    public DefaultFeatureIndex(FeatureStoreProviderServices featureStore,
97
        FeatureType featureType, FeatureIndexProvider indexProvider,
98
        String attributeName, String indexName) {
99

    
100
        if (featureStore == null) {
101
            throw new IllegalArgumentException("featureStore cannot be null.");
102
        }
103
        if (featureType == null) {
104
            throw new IllegalArgumentException("featureType cannot be null.");
105
        }
106
        if (attributeName == null) {
107
            throw new IllegalArgumentException("attributeName cannot be null.");
108
        }
109
        if (indexName == null) {
110
            throw new IllegalArgumentException("indexName cannot be null.");
111
        }
112

    
113
        // FIXME Esto debe ir al provider
114
        if (featureStore.getProvider().getOIDType() != DataTypes.INT
115
            && featureStore.getProvider().getOIDType() != DataTypes.LONG) {
116
            throw new IllegalArgumentException();
117
        }
118

    
119
        FeatureAttributeDescriptor attr =
120
            featureType.getAttributeDescriptor(attributeName);
121
        if (attr == null) {
122
            throw new IllegalArgumentException("Attribute " + attributeName
123
                + " not found in FeatureType " + featureType.toString());
124
        }
125

    
126
        this.featureStore = featureStore;
127
        this.featureType = featureType;
128
        this.attributeName = attributeName;
129
        this.indexName = indexName;
130
        this.dataType = attr.getType();
131
        this.indexProvider = indexProvider;
132

    
133
        attributeNames = new ArrayList();
134
        attributeNames.add(attributeName);
135
    }
136

    
137
    public final FeatureAttributeDescriptor getFeatureAttributeDescriptor() {
138
        return featureType.getAttributeDescriptor(attributeName);
139
    }
140

    
141
    public final FeatureStoreProviderServices getFeatureStoreProviderServices() {
142
        return featureStore;
143
    }
144

    
145
    public final FeatureType getFeatureType() {
146
        return featureType;
147
    }
148

    
149
    public final String getAttributeName() {
150
        return attributeName;
151
    }
152

    
153
    public final int getDataType() {
154
        return dataType;
155
    }
156

    
157
    /**
158
     * {@link MonitorableTask} and {@link CancellableTask} to perform long
159
     * operations on the index: filling and inserting or deleting a feature set.
160
     * 
161
     * @author gvSIG Team
162
     * @version $Id$
163
     */
164
    private static class FeatureIndexOperationTask extends
165
        AbstractMonitorableTask {
166

    
167
        private final DefaultFeatureIndex index;
168

    
169
        public static final int OP_FILL = 0;
170
        public static final int OP_INSERT_FSET = 1;
171
        public static final int OP_DELETE_FSET = 2;
172

    
173
        private static final String[] OP_NAMES = { //
174
            Messages.getText("filling_index"), // OP_FILL
175
                Messages.getText("updating_index"), // OP_INSERT_FSET
176
                Messages.getText("updating_index"), // OP_DELETE_FSET
177
            };
178

    
179
        private final int operation;
180

    
181
        private final FeatureSet data;
182

    
183
        private final Observer operationObserver;
184

    
185
        private final FeatureStore store;
186

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

    
217
        public void run() {
218
            try {
219
                switch (operation) {
220
                case OP_FILL:
221
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
222
                    clearAndFill();
223
                    break;
224

    
225
                case OP_INSERT_FSET:
226
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
227
                    insert(data);
228
                    break;
229

    
230
                case OP_DELETE_FSET:
231
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
232
                    delete(data);
233
                    break;
234
                }
235
            } catch (Exception e) {
236
                Exception fioex =
237
                    new FeatureIndexOperationException(index,
238
                        OP_NAMES[operation], e);
239
                notify(FeatureStoreNotification.INDEX_FILLING_ERROR, fioex);
240
                throw new RuntimeException(fioex);
241
            } finally {
242
                index.removeTask(this);
243
            }
244
        }
245

    
246
        /**
247
         * Clears the index data and fills it again.
248
         */
249
        private void clearAndFill() throws DataException {
250
            FeatureSet set = null;
251
            try {
252
                synchronized (index) {
253
                    set = store.getFeatureSet();
254
                    if (isCancellationRequested()) {
255
                        cancel();
256
                        return;
257
                    }
258
                    index.clear();
259
                    if (isCancellationRequested()) {
260
                        cancel();
261
                        return;
262
                    }
263
                    insert(set);
264
                    index.setValid(true);
265
                }
266
            } catch (IllegalStateException e) {
267
                    // The feature store has entered in editing or 
268
                    // append mode again, cancel indexing.
269
                    cancel();
270
            } finally {
271
                DisposeUtils.dispose(set);
272
            }
273
        }
274

    
275
        private void insert(FeatureSet data) throws DataException {
276
            DisposableIterator it = null;
277
            long counter = 0;
278
            try {
279
                it = data.fastIterator();
280
                synchronized (index) {
281
                    taskStatus.setRangeOfValues(0, data.getSize());
282
                    taskStatus.add();
283
                    while (it.hasNext()) {
284
                        if (isCancellationRequested()) {
285
                            index.clear();
286
                            cancel();
287
                            return;
288
                        }
289
                        Feature feat = (Feature) it.next();
290
                        index.insert(feat);
291
                        taskStatus.setCurValue(counter++);
292
                    }
293
                    notify(FeatureStoreNotification.INDEX_FILLING_SUCCESS);
294
                }
295
                taskStatus.terminate();
296
            } catch (IllegalStateException e) {
297
                    // The feature store has entered in editing or 
298
                    // append mode again, cancel indexing.
299
                    taskStatus.cancel();
300
            } catch (RuntimeException e) {
301
                taskStatus.abort();
302
                throw e;
303
            } catch (DataException e) {
304
                taskStatus.abort();
305
                throw e;
306
            } finally {
307
                DisposeUtils.dispose(it);
308
                taskStatus.remove();
309
            }
310
        }
311

    
312
        private void delete(FeatureSet data) throws FeatureIndexException {
313
            DisposableIterator it = null;
314
            try {
315
                it = data.fastIterator();
316
                synchronized (index) {
317
                    while (it.hasNext()) {
318
                        if (isCancellationRequested()) {
319
                            cancel();
320
                            return;
321
                        }
322
                        Feature feat = (Feature) it.next();
323
                        index.delete(feat);
324
                    }
325
                    notify(FeatureStoreNotification.INDEX_FILLING_SUCCESS);
326
                }
327
            } catch (DataException e) {
328
                throw new FeatureIndexException(e);
329
            } finally {
330
                DisposeUtils.dispose(it);
331
            }
332
        }
333

    
334
        private void cancel() {
335
            notify(FeatureStoreNotification.INDEX_FILLING_CANCELLED);
336
            taskStatus.cancel();
337
        }
338

    
339
        public void notifyOperationObserver(DataStoreNotification notification) {
340
            if (this.operationObserver != null) {
341
                this.operationObserver.update(index, notification);
342
            }
343
        }
344

    
345
        private void notify(String notificationType) {
346
            DataStoreNotification notification =
347
                new DefaultFeatureStoreNotification(store, notificationType,
348
                    index);
349
            notifyOperationObserver(notification);
350
            index.notifyObservers(notification);
351
        }
352

    
353
        private void notify(String notificationType, Exception exception) {
354
            DataStoreNotification notification =
355
                new DefaultFeatureStoreNotification(store, notificationType,
356
                    exception);
357
            notifyOperationObserver(notification);
358
            index.notifyObservers(notification);
359
        }
360
    }
361

    
362
    private FeatureIndexOperationTask createIndexTask(int operation,
363
        FeatureSet data, Observer observer) {
364
        synchronized (featureOperationTaskLock) {
365
            if (featureIndexTask != null) {
366
                this.featureIndexTask.cancelRequest();
367
                removeTask(this.featureIndexTask);
368
            }
369
            FeatureIndexOperationTask fillingTask =
370
                new FeatureIndexOperationTask(this,
371
                    featureStore.getFeatureStore(), operation, data, observer);
372
            this.featureIndexTask = fillingTask;
373
            return fillingTask;
374
        }
375
    }
376

    
377
    private void removeTask(FeatureIndexOperationTask task) {
378
        synchronized (featureOperationTaskLock) {
379
            // Remove if it is not null and the same task
380
            if (this.featureIndexTask == task) {
381
                featureIndexTask = null;
382
            }
383
        }
384
    }
385

    
386
    public void fill() throws FeatureIndexException {
387
        fill(false, null);
388
    }
389

    
390
    public void fill(boolean background, Observer observer)
391
        throws FeatureIndexException {
392
        FeatureIndexOperationTask task =
393
            createIndexTask(FeatureIndexOperationTask.OP_FILL, null, observer);
394
        if (background) {
395
            task.start();
396
        } else {
397
            task.run();
398
        }
399
    }
400

    
401
    public final void insert(FeatureSet data) throws DataException {
402
        if (!isValid()) {
403
            throw new InvalidFeatureIndexException();
404
        }
405
        FeatureIndexOperationTask task =
406
            createIndexTask(FeatureIndexOperationTask.OP_INSERT_FSET, data,
407
                null);
408
        task.run();
409
    }
410

    
411
    public synchronized final void insert(Feature feat) throws DataException {
412
        try {
413
                FeatureIndexProvider prov = getIndexProvider();
414
                Object value = feat.get(getAttributeName());
415
                if(prov.allowNulls() || !(value instanceof NullGeometry)) {
416
                        prov.insert(value,
417
                                        (FeatureReferenceProviderServices) feat.getReference());
418
                }
419
        } catch (NullPointerException e) {
420
            throw new IllegalArgumentException("Could not add Feature: " + feat
421
                + " to index " + this
422
                + ". It does not contain a column with name "
423
                + getAttributeName());
424
        } catch (ClassCastException e) {
425
            throw new IllegalArgumentException("Could not add Feature: " + feat
426
                + " to index " + this + ". Attribute " + getAttributeName()
427
                + " data type is not valid.");
428
        }
429
    }
430

    
431
    public final void delete(FeatureSet data) throws FeatureIndexException {
432
        if (!isValid()) {
433
            throw new InvalidFeatureIndexException();
434
        }
435
        FeatureIndexOperationTask task =
436
            createIndexTask(FeatureIndexOperationTask.OP_DELETE_FSET, data,
437
                null);
438
        task.run();
439
    }
440

    
441
    public synchronized final void delete(Feature feat) throws DataException {
442
        getIndexProvider().delete(feat.get(getAttributeName()),
443
            (FeatureReferenceProviderServices) feat.getReference());
444
    }
445

    
446
    private synchronized void clear() throws DataException {
447
        getIndexProvider().clear();
448
    }
449

    
450
    public synchronized FeatureSet getMatchFeatureSet(Object value)
451
        throws FeatureIndexException {
452
        if (!isValid()) {
453
            throw new InvalidFeatureIndexException();
454
        }
455
        return new IndexFeatureSet(this, new DefaultLongList(
456
            indexProvider.match(value)));
457
    }
458

    
459
    public synchronized FeatureSet getRangeFeatureSet(Object value1,
460
        Object value2) throws FeatureIndexException {
461
        if (!isValid()) {
462
            throw new InvalidFeatureIndexException();
463
        }
464
        return new IndexFeatureSet(this, new DefaultLongList(
465
            indexProvider.range(value1, value2)));
466
    }
467

    
468
    public synchronized FeatureSet getNearestFeatureSet(int count, Object value)
469
        throws FeatureIndexException {
470
        if (!isValid()) {
471
            throw new InvalidFeatureIndexException();
472
        }
473
        return new IndexFeatureSet(this, new DefaultLongList(
474
            indexProvider.nearest(count, value)));
475
    }
476

    
477
    public synchronized FeatureSet getNearestFeatureSet(int count,
478
        Object value, Object tolerance) throws FeatureIndexException {
479
        if (!isValid()) {
480
            throw new InvalidFeatureIndexException();
481
        }
482
        return new IndexFeatureSet(this, new DefaultLongList(
483
            indexProvider.nearest(count, value, tolerance)));
484
    }
485

    
486
    public void initialize() throws InitializeException {
487
        indexProvider.setFeatureIndexProviderServices(this);
488
        indexProvider.initialize();
489
    }
490

    
491
    public List getAttributeNames() {
492
        return attributeNames;
493
    }
494

    
495
    public String getNewFileName(String prefix, String sufix) {
496
        int n = 0;
497
        File file = new File(prefix + getName(), sufix);
498
        while (file.exists()) {
499
            n++;
500
            file = new File(prefix + getName() + n, sufix);
501
        }
502
        return file.getAbsolutePath();
503
    }
504

    
505
    public String getFileName() {
506
        // TODO Auto-generated method stub
507
        return null;
508
    }
509

    
510
    public String getTemporaryFileName() {
511
        // TODO Auto-generated method stub
512
        return null;
513
    }
514

    
515

    
516
    public FeatureIndexProvider getFeatureIndexProvider() {
517
        return this.indexProvider;
518
    }
519

    
520
    public boolean isFilling() {
521
        synchronized (featureOperationTaskLock) {
522
            return featureIndexTask != null;
523
        }
524
    }
525

    
526
    public boolean isValid() {
527
        synchronized (featureOperationTaskLock) {
528
            return !isFilling() && valid;
529
        }
530
    }
531

    
532
    public synchronized void waitForIndex() {
533
        // Nothing to do, this is just used for anyone to block until the index
534
        // has finished being used by a FeatureIndexOperation.
535
        LOG.debug("Wait finished for index: {}", this);
536
    }
537

    
538
    public void setValid(boolean valid) {
539
        synchronized (featureOperationTaskLock) {
540
            this.valid = valid;
541
        }
542
    }
543

    
544
    protected void doDispose() throws BaseException {
545
        synchronized (featureOperationTaskLock) {
546
            setValid(false);
547
            if (this.featureIndexTask != null) {
548
                this.featureIndexTask.cancelRequest();
549
                this.featureIndexTask = null;
550
            }
551
        }
552
        // Wait for any task until it finishes running
553
        synchronized (this) {
554
            return;
555
        }
556
    }
557

    
558
    public String toString() {
559
        return "Feature index with name" + indexName
560
            + ", for the FeatureType: " + featureType + ", and the attribute: "
561
            + attributeName;
562
    }
563

    
564
    public void notifyObservers(Object notification) {
565
        observable.notifyObservers(notification);
566
    }
567

    
568
    public String getName() {
569
        return indexName;
570
    }
571

    
572
    private FeatureIndexProvider getIndexProvider() {
573
        return indexProvider;
574
    }
575

    
576
    public void addObserver(Observer observer) {
577
        observable.addObserver(observer);
578
    }
579

    
580
    public void deleteObserver(Observer observer) {
581
        observable.deleteObserver(observer);
582
    }
583

    
584
    public void deleteObservers() {
585
        observable.deleteObservers();
586
    }
587

    
588
}