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 @ 44111

History | View | Annotate | Download (19.9 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
package org.gvsig.fmap.dal.feature.impl;
26

    
27
import java.io.File;
28
import java.util.ArrayList;
29
import java.util.List;
30

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

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

    
72
    private static final Logger LOG = LoggerFactory
73
        .getLogger(DefaultFeatureIndex.class);
74

    
75
    private final FeatureStoreProviderServices featureStore;
76
    private final FeatureType featureType;
77
    private final String attributeName;
78
    private final String indexName;
79
    private final int dataType;
80
    private final FeatureIndexProvider indexProvider;
81
    private List attributeNames;
82

    
83
    private Object featureOperationTaskLock = new Object();
84
    private FeatureIndexOperationTask featureIndexTask;
85

    
86
    private boolean valid = true;
87

    
88
    private DelegateWeakReferencingObservable observable =
89
        new DelegateWeakReferencingObservable(this);
90

    
91
    public DefaultFeatureIndex(FeatureStoreProviderServices featureStore,
92
        FeatureType featureType, FeatureIndexProvider indexProvider,
93
        String attributeName, String indexName) {
94

    
95
        if (featureStore == null) {
96
            throw new IllegalArgumentException("featureStore cannot be null.");
97
        }
98
        if (featureType == null) {
99
            throw new IllegalArgumentException("featureType cannot be null.");
100
        }
101
        if (attributeName == null) {
102
            throw new IllegalArgumentException("attributeName cannot be null.");
103
        }
104
        if (indexName == null) {
105
            throw new IllegalArgumentException("indexName cannot be null.");
106
        }
107

    
108
        // FIXME Esto debe ir al provider
109
        if (featureStore.getProvider().getOIDType() != DataTypes.INT
110
            && featureStore.getProvider().getOIDType() != DataTypes.LONG) {
111
            throw new IllegalArgumentException();
112
        }
113

    
114
        FeatureAttributeDescriptor attr =
115
            featureType.getAttributeDescriptor(attributeName);
116
        if (attr == null) {
117
            throw new IllegalArgumentException("Attribute " + attributeName
118
                + " not found in FeatureType " + featureType.toString());
119
        }
120

    
121
        this.featureStore = featureStore;
122
        this.featureType = featureType;
123
        this.attributeName = attributeName;
124
        this.indexName = indexName;
125
        this.dataType = attr.getType();
126
        this.indexProvider = indexProvider;
127

    
128
        attributeNames = new ArrayList();
129
        attributeNames.add(attributeName);
130
    }
131

    
132
    public final FeatureAttributeDescriptor getFeatureAttributeDescriptor() {
133
        return featureType.getAttributeDescriptor(attributeName);
134
    }
135

    
136
    public final FeatureStoreProviderServices getFeatureStoreProviderServices() {
137
        return featureStore;
138
    }
139

    
140
    public final FeatureType getFeatureType() {
141
        return featureType;
142
    }
143

    
144
    public final String getAttributeName() {
145
        return attributeName;
146
    }
147

    
148
    public final int getDataType() {
149
        return dataType;
150
    }
151

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

    
162
        private final DefaultFeatureIndex index;
163

    
164
        public static final int OP_FILL = 0;
165
        public static final int OP_INSERT_FSET = 1;
166
        public static final int OP_DELETE_FSET = 2;
167

    
168
        private static final String[] OP_NAMES = { //
169
            Messages.getText("filling_index"), // OP_FILL
170
                Messages.getText("updating_index"), // OP_INSERT_FSET
171
                Messages.getText("updating_index"), // OP_DELETE_FSET
172
            };
173

    
174
        private final int operation;
175

    
176
        private final FeatureSet data;
177

    
178
        private final Observer operationObserver;
179

    
180
        private final FeatureStore store;
181

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

    
212
        public void run() {
213
            try {
214
                switch (operation) {
215
                case OP_FILL:
216
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
217
                    clearAndFill();
218
                    break;
219

    
220
                case OP_INSERT_FSET:
221
                    notify(FeatureStoreNotification.INDEX_FILLING_STARTED);
222
                    insert(data);
223
                    break;
224

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

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

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

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

    
329
        private void cancel() {
330
            notify(FeatureStoreNotification.INDEX_FILLING_CANCELLED);
331
            taskStatus.cancel();
332
        }
333

    
334
        public void notifyOperationObserver(DataStoreNotification notification) {
335
            if (this.operationObserver != null) {
336
                this.operationObserver.update(index, notification);
337
            }
338
        }
339

    
340
        private void notify(String notificationType) {
341
            DataStoreNotification notification =
342
                new DefaultFeatureStoreNotification(store, notificationType,
343
                    index);
344
            notifyOperationObserver(notification);
345
            index.notifyObservers(notification);
346
        }
347

    
348
        private void notify(String notificationType, Exception exception) {
349
            DataStoreNotification notification =
350
                new DefaultFeatureStoreNotification(store, notificationType,
351
                    exception);
352
            notifyOperationObserver(notification);
353
            index.notifyObservers(notification);
354
        }
355
    }
356

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

    
372
    private void removeTask(FeatureIndexOperationTask task) {
373
        synchronized (featureOperationTaskLock) {
374
            // Remove if it is not null and the same task
375
            if (this.featureIndexTask == task) {
376
                featureIndexTask = null;
377
            }
378
        }
379
    }
380

    
381
    public void fill() throws FeatureIndexException {
382
        fill(false, null);
383
    }
384

    
385
    public void fill(boolean background, Observer observer)
386
        throws FeatureIndexException {
387
        FeatureIndexOperationTask task =
388
            createIndexTask(FeatureIndexOperationTask.OP_FILL, null, observer);
389
        if (background) {
390
            task.start();
391
        } else {
392
            task.run();
393
        }
394
    }
395

    
396
    public final void insert(FeatureSet data) throws DataException {
397
        if (!isValid()) {
398
            throw new InvalidFeatureIndexException();
399
        }
400
        FeatureIndexOperationTask task =
401
            createIndexTask(FeatureIndexOperationTask.OP_INSERT_FSET, data,
402
                null);
403
        task.run();
404
    }
405

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

    
426
    public final void delete(FeatureSet data) throws FeatureIndexException {
427
        if (!isValid()) {
428
            throw new InvalidFeatureIndexException();
429
        }
430
        FeatureIndexOperationTask task =
431
            createIndexTask(FeatureIndexOperationTask.OP_DELETE_FSET, data,
432
                null);
433
        task.run();
434
    }
435

    
436
    public synchronized final void delete(Feature feat) throws DataException {
437
        getIndexProvider().delete(feat.get(getAttributeName()),
438
            (FeatureReferenceProviderServices) feat.getReference());
439
    }
440

    
441
    private synchronized void clear() throws DataException {
442
        getIndexProvider().clear();
443
    }
444

    
445
    public synchronized FeatureSet getMatchFeatureSet(Object value)
446
        throws FeatureIndexException {
447
        if (!isValid()) {
448
            throw new InvalidFeatureIndexException();
449
        }
450
        return new IndexFeatureSet(this, new DefaultLongList(
451
            indexProvider.match(value)));
452
    }
453

    
454
    public synchronized FeatureSet getRangeFeatureSet(Object value1,
455
        Object value2) throws FeatureIndexException {
456
        if (!isValid()) {
457
            throw new InvalidFeatureIndexException();
458
        }
459
        return new IndexFeatureSet(this, new DefaultLongList(
460
            indexProvider.range(value1, value2)));
461
    }
462

    
463
    public synchronized FeatureSet getNearestFeatureSet(int count, Object value)
464
        throws FeatureIndexException {
465
        if (!isValid()) {
466
            throw new InvalidFeatureIndexException();
467
        }
468
        return new IndexFeatureSet(this, new DefaultLongList(
469
            indexProvider.nearest(count, value)));
470
    }
471

    
472
    public synchronized FeatureSet getNearestFeatureSet(int count,
473
        Object value, Object tolerance) throws FeatureIndexException {
474
        if (!isValid()) {
475
            throw new InvalidFeatureIndexException();
476
        }
477
        return new IndexFeatureSet(this, new DefaultLongList(
478
            indexProvider.nearest(count, value, tolerance)));
479
    }
480

    
481
    public void initialize() throws InitializeException {
482
        indexProvider.setFeatureIndexProviderServices(this);
483
        indexProvider.initialize();
484
    }
485

    
486
    public List getAttributeNames() {
487
        return attributeNames;
488
    }
489

    
490
    public String getNewFileName(String prefix, String sufix) {
491
        int n = 0;
492
        File file = new File(prefix + getName(), sufix);
493
        while (file.exists()) {
494
            n++;
495
            file = new File(prefix + getName() + n, sufix);
496
        }
497
        return file.getAbsolutePath();
498
    }
499

    
500
    public String getFileName() {
501
        // TODO Auto-generated method stub
502
        return null;
503
    }
504

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

    
510

    
511
    public FeatureIndexProvider getFeatureIndexProvider() {
512
        return this.indexProvider;
513
    }
514

    
515
    public boolean isFilling() {
516
        synchronized (featureOperationTaskLock) {
517
            return featureIndexTask != null;
518
        }
519
    }
520

    
521
    public boolean isValid() {
522
        synchronized (featureOperationTaskLock) {
523
            return !isFilling() && valid;
524
        }
525
    }
526

    
527
    public synchronized void waitForIndex() {
528
        // Nothing to do, this is just used for anyone to block until the index
529
        // has finished being used by a FeatureIndexOperation.
530
        LOG.debug("Wait finished for index: {}", this);
531
    }
532

    
533
    public void setValid(boolean valid) {
534
        synchronized (featureOperationTaskLock) {
535
            this.valid = valid;
536
        }
537
    }
538

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

    
553
    public String toString() {
554
        return "Feature index with name" + indexName
555
            + ", for the FeatureType: " + featureType + ", and the attribute: "
556
            + attributeName;
557
    }
558

    
559
    public void notifyObservers(Object notification) {
560
        observable.notifyObservers(notification);
561
    }
562

    
563
    public String getName() {
564
        return indexName;
565
    }
566

    
567
    private FeatureIndexProvider getIndexProvider() {
568
        return indexProvider;
569
    }
570

    
571
    public void addObserver(Observer observer) {
572
        observable.addObserver(observer);
573
    }
574

    
575
    public void deleteObserver(Observer observer) {
576
        observable.deleteObserver(observer);
577
    }
578

    
579
    public void deleteObservers() {
580
        observable.deleteObservers();
581
    }
582

    
583
}