Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_dal / src / org / gvsig / fmap / dal / feature / impl / DefaultFeatureIndex.java @ 36359

History | View | Annotate | Download (19.4 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.slf4j.Logger;
36
import org.slf4j.LoggerFactory;
37

    
38
import org.gvsig.fmap.dal.DataStoreNotification;
39
import org.gvsig.fmap.dal.DataTypes;
40
import org.gvsig.fmap.dal.exception.DataException;
41
import org.gvsig.fmap.dal.exception.InitializeException;
42
import org.gvsig.fmap.dal.feature.Feature;
43
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
44
import org.gvsig.fmap.dal.feature.FeatureSet;
45
import org.gvsig.fmap.dal.feature.FeatureStore;
46
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
47
import org.gvsig.fmap.dal.feature.FeatureType;
48
import org.gvsig.fmap.dal.feature.exception.FeatureIndexException;
49
import org.gvsig.fmap.dal.feature.exception.FeatureIndexOperationException;
50
import org.gvsig.fmap.dal.feature.exception.InvalidFeatureIndexException;
51
import org.gvsig.fmap.dal.feature.spi.DefaultLongList;
52
import org.gvsig.fmap.dal.feature.spi.FeatureReferenceProviderServices;
53
import org.gvsig.fmap.dal.feature.spi.FeatureStoreProviderServices;
54
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProvider;
55
import org.gvsig.fmap.dal.feature.spi.index.FeatureIndexProviderServices;
56
import org.gvsig.tools.dispose.DisposableIterator;
57
import org.gvsig.tools.dispose.DisposeUtils;
58
import org.gvsig.tools.dispose.impl.AbstractDisposable;
59
import org.gvsig.tools.exception.BaseException;
60
import org.gvsig.tools.observer.Observer;
61
import org.gvsig.tools.observer.WeakReferencingObservable;
62
import org.gvsig.tools.observer.impl.DelegateWeakReferencingObservable;
63
import org.gvsig.tools.task.AbstractMonitorableTask;
64
import org.gvsig.tools.task.CancellableTask;
65
import org.gvsig.tools.task.MonitorableTask;
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
            } 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 (RuntimeException e) {
292
                taskStatus.abort();
293
                throw e;
294
            } catch (DataException e) {
295
                taskStatus.abort();
296
                throw e;
297
            } finally {
298
                DisposeUtils.dispose(it);
299
                taskStatus.remove();
300
            }
301
        }
302

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

    
325
        private void cancel() {
326
            notify(FeatureStoreNotification.INDEX_FILLING_CANCELLED);
327
            taskStatus.cancel();
328
        }
329

    
330
        public void notifyOperationObserver(DataStoreNotification notification) {
331
            if (this.operationObserver != null) {
332
                this.operationObserver.update(index, notification);
333
            }
334
        }
335

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

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

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

    
368
    private void removeTask(FeatureIndexOperationTask task) {
369
        synchronized (featureOperationTaskLock) {
370
            // Remove if it is not null and the same task
371
            if (this.featureIndexTask == task) {
372
                featureIndexTask = null;
373
            }
374
        }
375
    }
376

    
377
    public void fill() throws FeatureIndexException {
378
        fill(false, null);
379
    }
380

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

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

    
402
    public synchronized final void insert(Feature feat) throws DataException {
403
        try {
404
            getIndexProvider().insert(feat.get(getAttributeName()),
405
                (FeatureReferenceProviderServices) feat.getReference());
406
        } catch (NullPointerException e) {
407
            throw new IllegalArgumentException("Could not add Feature: " + feat
408
                + " to index " + this
409
                + ". It does not contain a column with name "
410
                + getAttributeName(), e);
411
        } catch (ClassCastException e) {
412
            throw new IllegalArgumentException("Could not add Feature: " + feat
413
                + " to index " + this + ". Attribute " + getAttributeName()
414
                + " data type is not valid.", e);
415
        }
416
    }
417

    
418
    public final void delete(FeatureSet data) throws FeatureIndexException {
419
        if (!isValid()) {
420
            throw new InvalidFeatureIndexException();
421
        }
422
        FeatureIndexOperationTask task =
423
            createIndexTask(FeatureIndexOperationTask.OP_DELETE_FSET, data,
424
                null);
425
        task.run();
426
    }
427

    
428
    public synchronized final void delete(Feature feat) throws DataException {
429
        getIndexProvider().delete(feat.get(getAttributeName()),
430
            (FeatureReferenceProviderServices) feat.getReference());
431
    }
432

    
433
    private synchronized void clear() throws DataException {
434
        getIndexProvider().clear();
435
    }
436

    
437
    public synchronized FeatureSet getMatchFeatureSet(Object value)
438
        throws FeatureIndexException {
439
        if (!isValid()) {
440
            throw new InvalidFeatureIndexException();
441
        }
442
        return new IndexFeatureSet(this, new DefaultLongList(
443
            indexProvider.match(value)));
444
    }
445

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

    
455
    public synchronized FeatureSet getNearestFeatureSet(int count, Object value)
456
        throws FeatureIndexException {
457
        if (!isValid()) {
458
            throw new InvalidFeatureIndexException();
459
        }
460
        return new IndexFeatureSet(this, new DefaultLongList(
461
            indexProvider.nearest(count, value)));
462
    }
463

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

    
473
    public void initialize() throws InitializeException {
474
        indexProvider.setFeatureIndexProviderServices(this);
475
        indexProvider.initialize();
476
    }
477

    
478
    public List getAttributeNames() {
479
        return attributeNames;
480
    }
481

    
482
    public String getNewFileName(String prefix, String sufix) {
483
        int n = 0;
484
        File file = new File(prefix + getName(), sufix);
485
        while (file.exists()) {
486
            n++;
487
            file = new File(prefix + getName() + n, sufix);
488
        }
489
        return file.getAbsolutePath();
490
    }
491

    
492
    public String getFileName() {
493
        // TODO Auto-generated method stub
494
        return null;
495
    }
496

    
497
    public String getTemporaryFileName() {
498
        // TODO Auto-generated method stub
499
        return null;
500
    }
501

    
502

    
503
    public FeatureIndexProvider getFeatureIndexProvider() {
504
        return this.indexProvider;
505
    }
506

    
507
    public boolean isFilling() {
508
        synchronized (featureOperationTaskLock) {
509
            return featureIndexTask != null;
510
        }
511
    }
512

    
513
    public boolean isValid() {
514
        synchronized (featureOperationTaskLock) {
515
            return !isFilling() && valid;
516
        }
517
    }
518

    
519
    public synchronized void waitForIndex() {
520
        // Nothing to do, this is just used for anyone to block until the index
521
        // has finished being used by a FeatureIndexOperation.
522
        LOG.debug("Wait finished for index: {}", this);
523
    }
524

    
525
    public void setValid(boolean valid) {
526
        synchronized (featureOperationTaskLock) {
527
            this.valid = valid;
528
        }
529
    }
530

    
531
    protected void doDispose() throws BaseException {
532
        synchronized (featureOperationTaskLock) {
533
            setValid(false);
534
            if (this.featureIndexTask != null) {
535
                this.featureIndexTask.cancelRequest();
536
                this.featureIndexTask = null;
537
            }
538
        }
539
        // Wait for any task until it finishes running
540
        synchronized (this) {
541
            return;
542
        }
543
    }
544

    
545
    public String toString() {
546
        return "Feature index with name" + indexName
547
            + ", for the FeatureType: " + featureType + ", and the attribute: "
548
            + attributeName;
549
    }
550

    
551
    public void notifyObservers(Object notification) {
552
        observable.notifyObservers(notification);
553
    }
554

    
555
    public String getName() {
556
        return indexName;
557
    }
558

    
559
    private FeatureIndexProvider getIndexProvider() {
560
        return indexProvider;
561
    }
562

    
563
    public void addObserver(Observer observer) {
564
        observable.addObserver(observer);
565
    }
566

    
567
    public void deleteObserver(Observer observer) {
568
        observable.deleteObserver(observer);
569
    }
570

    
571
    public void deleteObservers() {
572
        observable.deleteObservers();
573
    }
574

    
575
}