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

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.fmap.geom.primitive.NullGeometry;
51
import org.gvsig.tools.dispose.DisposableIterator;
52
import org.gvsig.tools.dispose.DisposeUtils;
53
import org.gvsig.tools.dispose.impl.AbstractDisposable;
54
import org.gvsig.tools.exception.BaseException;
55
import org.gvsig.tools.observer.Observer;
56
import org.gvsig.tools.observer.WeakReferencingObservable;
57
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
58
import org.gvsig.tools.task.AbstractMonitorableTask;
59
import org.gvsig.tools.task.CancellableTask;
60
import org.gvsig.tools.task.MonitorableTask;
61
import org.slf4j.Logger;
62
import org.slf4j.LoggerFactory;
63

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

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

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

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

    
87
    private boolean valid = true;
88

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

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

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

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

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

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

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

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

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

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

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

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

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

    
163
        private final DefaultFeatureIndex index;
164

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

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

    
175
        private final int operation;
176

    
177
        private final FeatureSet data;
178

    
179
        private final Observer operationObserver;
180

    
181
        private final FeatureStore store;
182

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
511

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

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

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

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

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

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

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

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

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

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

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

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

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

    
584
}