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

History | View | Annotate | Download (19.9 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
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 2
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
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {{Company}}   {{Task}}
26
 */
27

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

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

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

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

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

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

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

    
90
    private boolean valid = true;
91

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

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

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

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

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

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

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

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

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

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

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

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

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

    
166
        private final DefaultFeatureIndex index;
167

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

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

    
178
        private final int operation;
179

    
180
        private final FeatureSet data;
181

    
182
        private final Observer operationObserver;
183

    
184
        private final FeatureStore store;
185

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
514

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

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

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

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

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

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

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

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

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

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

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

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

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

    
587
}