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

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
        this.indexProvider.setFeatureIndexProviderServices(this);
132
    }
133

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

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

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

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

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

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

    
164
        private final DefaultFeatureIndex index;
165

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

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

    
176
        private final int operation;
177

    
178
        private final FeatureSet data;
179

    
180
        private final Observer operationObserver;
181

    
182
        private final FeatureStore store;
183

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
512

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

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

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

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

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

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

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

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

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

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

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

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

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

    
585
}