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 / paging / impl / FeaturePagingHelperImpl.java @ 41630

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

    
26
import java.util.Collection;
27
import java.util.Iterator;
28
import java.util.List;
29
import java.util.ListIterator;
30
import org.slf4j.Logger;
31
import org.slf4j.LoggerFactory;
32

    
33
import org.gvsig.fmap.dal.exception.DataException;
34
import org.gvsig.fmap.dal.feature.EditableFeature;
35
import org.gvsig.fmap.dal.feature.Feature;
36
import org.gvsig.fmap.dal.feature.FeatureQuery;
37
import org.gvsig.fmap.dal.feature.FeatureSelection;
38
import org.gvsig.fmap.dal.feature.FeatureSet;
39
import org.gvsig.fmap.dal.feature.FeatureStore;
40
import org.gvsig.fmap.dal.feature.FeatureType;
41
import org.gvsig.fmap.dal.feature.exception.FeatureIndexException;
42
import org.gvsig.fmap.dal.feature.impl.featureset.DynObjectFeatureFacade;
43
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
44
import org.gvsig.tools.dynobject.DynObject;
45
import org.gvsig.tools.dynobject.DynObjectSet;
46
import org.gvsig.tools.dynobject.impl.DefaultDynObjectPagingHelper;
47
import org.gvsig.tools.exception.BaseException;
48
import org.gvsig.tools.visitor.VisitCanceledException;
49
import org.gvsig.tools.visitor.Visitor;
50

    
51
/**
52
 * Helper class to access the values of a FeatureCollection by position. Handles
53
 * pagination automatically to avoid filling the memory in case of big
54
 * collections.
55
 * 
56
 * TODO: evaluate if its more convenient to read values in the background when
57
 * the returned value is near the end of the page, instead of loading a page on
58
 * demand.
59
 * 
60
 * @author gvSIG Team
61
 */
62
public class FeaturePagingHelperImpl extends DefaultDynObjectPagingHelper
63
    implements FeaturePagingHelper {
64

    
65
    private static final Logger LOG = LoggerFactory
66
        .getLogger(FeaturePagingHelperImpl.class);
67

    
68
    private FeatureQuery query;
69

    
70
    private FeatureStore featureStore;
71

    
72
    /** If the selected Features must be returned as the first ones. **/
73
    private boolean selectionUp = false;
74

    
75
    private FeatureSet featSet = null;
76
    private FeatureSelection initialSelection = null;
77

    
78
    private Feature[] features = null;
79

    
80
    private boolean initialization_completed = false;
81
    /**
82
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
83
     * 
84
     * @param featureStore
85
     *            to extract data from
86
     * @throws DataException
87
     *             if there is an error initializing the helper
88
     */
89
    public FeaturePagingHelperImpl(FeatureStore featureStore)
90
        throws BaseException {
91
        this(featureStore, DEFAULT_PAGE_SIZE);
92
    }
93

    
94
    /**
95
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
96
     * 
97
     * @param featureStore
98
     *            to extract data from
99
     * @param pageSize
100
     *            the number of elements per page data
101
     * @throws DataException
102
     *             if there is an error initializing the helper
103
     */
104
    public FeaturePagingHelperImpl(FeatureStore featureStore, int pageSize)
105
        throws BaseException {
106
        this(featureStore, null, pageSize);
107
    }
108

    
109
    /**
110
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
111
     * 
112
     * @param featureStore
113
     *            to extract data from
114
     * @throws DataException
115
     *             if there is an error initializing the helper
116
     */
117
    public FeaturePagingHelperImpl(FeatureStore featureStore,
118
        FeatureQuery featureQuery) throws BaseException {
119
        this(featureStore, featureQuery, DEFAULT_PAGE_SIZE);
120
    }
121

    
122
    /**
123
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
124
     * 
125
     * @param featureSet
126
     *            to extract data from
127
     * @param pageSize
128
     *            the number of elements per page data
129
     * @throws DataException
130
     *             if there is an error initializing the helper
131
     */
132
    public FeaturePagingHelperImpl(FeatureStore featureStore,
133
        FeatureQuery featureQuery, int pageSize) throws BaseException {
134
        super();
135
        FeatureQuery query = featureQuery;
136
        if (featureQuery == null) {
137
            query = featureStore.createFeatureQuery();
138
            query.setFeatureType(featureStore.getDefaultFeatureType());
139
        }
140

    
141
        this.featureStore = featureStore;
142
        this.query = query;
143
        this.query.setPageSize(pageSize);
144

    
145
        setDefaultCalculator(new Sizeable() {
146
            public long getSize() {
147
                    FeatureSet featureSet = getFeatureSet(false);
148
                try {
149
                                        return featureSet.getSize();
150
                } catch (BaseException e) {
151
                    LOG.error("Error getting the size of the FeatureSet: "
152
                        + featureSet, e);
153
                    return 0l;
154
                }
155
            }
156
        }, pageSize);
157
        
158
        
159
        if (LOG.isDebugEnabled()) {
160
                
161
            LOG.debug("FeaturePagingHelperImpl created with {} pages, "
162
                + "and a page size of {}", new Long(getCalculator()
163
                .getNumPages()), new Integer(pageSize));
164
        }
165
        this.initialization_completed = true;
166
    }
167

    
168
    /**
169
     * @return the selectionUp status
170
     */
171
    public boolean isSelectionUp() {
172
        return selectionUp;
173
    }
174

    
175
    public void setSelectionUp(boolean selectionUp) {
176
        this.selectionUp = selectionUp;
177
        try {
178
            FeatureSelection currentSelection = getFeatureStore().getFeatureSelection();
179
            if (selectionUp && !currentSelection.isEmpty()) {
180
                initialSelection =(FeatureSelection) currentSelection.clone();
181
                setCalculator(new OneSubsetOneSetPagingCalculator(
182
                    new FeatureSetSizeableDelegate(initialSelection),
183
                    new FeatureSetSizeableDelegate(getFeatureSet(false)),
184
                    getMaxPageSize()));
185
            } else {
186
                if (initialSelection != null) {
187
                    initialSelection.dispose();
188
                    initialSelection = null;
189
                }
190
                setDefaultCalculator(new FeatureSetSizeableDelegate(
191
                    getFeatureSet(false)), getMaxPageSize());
192
            }
193
        } catch (BaseException e) {
194
            LOG.error("Error setting the selection up setting to: "
195
                + selectionUp, e);
196
        } catch (CloneNotSupportedException e) {
197
            LOG.error("Error cloning the selection "
198
                + "while setting the selection up", e);
199
        }
200
    }
201

    
202
    public Feature getFeatureAt(long index) throws BaseException {
203
        // Check if we have currently loaded the viewed page data,
204
        // or we need to load a new one
205
        long pageForIndex = (long) Math.floor(index / getMaxPageSize());
206

    
207
        if (pageForIndex != getCurrentPage()) {
208
            setCurrentPage(pageForIndex);
209
        }
210

    
211
        long positionForIndex = index - (getCurrentPage() * getMaxPageSize());
212

    
213
        if (positionForIndex >= getCurrentPageFeatures().length) {
214
            throw new FeatureIndexException(
215
                new IndexOutOfBoundsException("positionForIndex too big: "
216
                    + positionForIndex));
217
        } else {
218
            Feature feature = getCurrentPageFeatures()[(int) positionForIndex];
219
            return feature;
220
        }
221
        
222
    }
223

    
224
    public Feature[] getCurrentPageFeatures() {
225
        if( this.features==null ) {
226
            try {
227
                this.loadCurrentPageData();
228
            } catch (BaseException ex) {
229
                // Do nothing
230
            }
231
            if( this.features == null ) {
232
                String msg = "Can't retrieve the features from current page.";
233
                LOG.warn(msg);
234
                throw new RuntimeException(msg);
235
            }
236
        }
237
        return features;
238
    }
239

    
240
    /**
241
     * Gets the feature set.
242
     * The boolean tells whether we must create the featureset
243
     * again (for example perhaps we need it after a feature
244
     * has been added/removed)
245
     */
246
    private FeatureSet getFeatureSet(boolean reset) {
247
        
248
        if (featSet == null || reset) {
249
            
250
            if (featSet != null) {
251
                try {
252
                    featSet.dispose();
253
                } catch (Exception ex) {
254
                    LOG.info("Error while disposing featset.", ex);
255
                }
256
            }
257
            
258
            try {
259
                FeatureStore featureStore = getFeatureStore();          
260
                synchronized (featureStore) {
261
                    featSet = featureStore.getFeatureSet(getFeatureQuery());               
262
                }
263
            } catch (DataException e) {
264
                throw new RuntimeException("Error getting a feature set with the query " + getFeatureQuery());
265
            }
266
        }
267
        return featSet;
268
    }
269
    
270
    public DynObjectSet getDynObjectSet() {
271
            return getFeatureSet(false).getDynObjectSet();
272
    }
273

    
274
    public void reloadCurrentPage() throws BaseException {
275
        
276
        boolean sel_up = this.isSelectionUp();
277

    
278
        setSelectionUp(false);
279
        if (getCalculator().getCurrentPage() > -1) {
280
            loadCurrentPageData();
281
        }
282
        
283
        if (sel_up) {
284
            setSelectionUp(true);
285
        }
286
    }
287

    
288
    public void reload() throws BaseException {
289
        
290
        /*
291
         * Force re-creation of feature set
292
         */
293
        this.getFeatureSet(true);
294

    
295
        
296
        setDefaultCalculator(new Sizeable() {
297
            public long getSize() {
298
                    FeatureSet featureSet = getFeatureSet(false);
299
                try {
300
                                        return featureSet.getSize();
301
                } catch (BaseException e) {
302
                    LOG.error("Error getting the size of the FeatureSet: "
303
                        + featureSet, e);
304
                    return 0l;
305
                }
306
            }
307
        }, getCalculator().getMaxPageSize());
308
        reloadCurrentPage();
309
    }
310

    
311
    public FeatureStore getFeatureStore() {
312
        return featureStore;
313
    }
314

    
315
    public FeatureQuery getFeatureQuery() {
316
        return query;
317
    }
318

    
319
    /**
320
     * Loads all the Features of the current page.
321
     */
322
    protected void loadCurrentPageData() throws BaseException {
323
        if( !initialization_completed ) {
324
            return;
325
        }
326
        final int currentPageSize = getCalculator().getCurrentPageSize();
327
        final Feature[] values = new Feature[currentPageSize];
328

    
329
        long t1 = 0;
330
        if (LOG.isTraceEnabled()) {
331
            t1 = System.currentTimeMillis();
332
        }
333

    
334
        if (selectionUp) {
335
            loadCurrentPageDataWithSelectionUp(values);
336
        } else {
337
            loadCurrentPageDataNoSelection(values);
338
        }
339

    
340
        if (LOG.isTraceEnabled()) {
341
            long t2 = System.currentTimeMillis();
342
            LOG.trace("Time to load {} features: {} ms", new Integer(
343
                currentPageSize), new Long(t2 - t1));
344
        }
345

    
346
        this.features = values;
347
    }
348
    
349
    private void loadCurrentPageDataWithSelectionUp(final Feature[] values)
350
            throws BaseException {
351
        FeatureSelection selection = initialSelection;
352
        if (selection == null) {
353
            loadCurrentPageDataNoSelection(values);
354
        } else {
355
            FeatureSet set = getFeatureSet(false);
356
            try {
357
                OneSubsetOneSetPagingCalculator twoSetsCalculator = null;
358
                if (getCalculator() instanceof OneSubsetOneSetPagingCalculator) {
359
                    twoSetsCalculator
360
                            = (OneSubsetOneSetPagingCalculator) getCalculator();
361
                } else {
362
                    twoSetsCalculator
363
                            = new OneSubsetOneSetPagingCalculator(
364
                                    new FeatureSetSizeableDelegate(selection),
365
                                    new FeatureSetSizeableDelegate(set),
366
                                    getMaxPageSize(), getCalculator().getCurrentPage());
367
                    setCalculator(twoSetsCalculator);
368
                }
369

    
370
                // First load values from the selection, if the current page has
371
                // elements from it
372
                if (twoSetsCalculator.hasCurrentPageAnyValuesInFirstSet()) {
373
                    loadDataFromFeatureSet(values, 0, selection,
374
                            twoSetsCalculator.getFirstSetInitialIndex(),
375
                            twoSetsCalculator.getFirstSetHowMany(), null);
376
                }
377
                // Next, load values from the FeatureSet if the current page has values
378
                // from it
379
                if (twoSetsCalculator.hasCurrentPageAnyValuesInSecondSet()) {
380
                    loadDataFromFeatureSet(
381
                            values,
382
                            // The cast will work as that size will be <= maxpagesize,
383
                            // which is an int
384
                            (int) twoSetsCalculator.getFirstSetHowMany(), set,
385
                            twoSetsCalculator.getSecondSetInitialIndex(),
386
                            twoSetsCalculator.getSecondSetHowMany(), selection);
387
                }
388
            } finally {
389
                /*
390
                 * This is the feature set
391
                 * we dont want to lose it
392
                 */
393
                // set.dispose();
394
            }
395
        }
396
    }
397

    
398
    private void loadCurrentPageDataNoSelection(final Feature[] values)
399
        throws BaseException {
400

    
401
        long firstPosition = getCalculator().getInitialIndex();
402

    
403
        if (LOG.isDebugEnabled()) {
404
            LOG.debug("Loading {} Features starting at position {}",
405
                new Integer(getCalculator().getCurrentPageSize()), new Long(
406
                    firstPosition));
407
        }
408

    
409
        FeatureSet featureSet = getFeatureSet(false);
410
        try {
411
                loadDataFromFeatureSet(values, 0, featureSet, firstPosition,
412
                                getCalculator().getCurrentPageSize(), null);
413
        } catch(DataException ex) {
414
            throw ex;
415
            // } finally {
416
                // featureSet.dispose();
417
        }
418
        
419
    }
420

    
421
    private void loadDataFromFeatureSet(final Feature[] values,
422
        final int valuesPosition, FeatureSet set, long initialIndex,
423
        final long howMany, final FeatureSelection selectedFeaturesToSkip)
424
        throws DataException {
425

    
426
        try {
427
            set.accept(new Visitor() {
428

    
429
                private int i = valuesPosition;
430

    
431
                public void visit(Object obj) throws VisitCanceledException,
432
                    BaseException {
433
                    if (i >= valuesPosition + howMany) {
434
                        throw new VisitCanceledException();
435
                    }
436
                    Feature current = (Feature) obj;
437
                    // Add the current Feature only if we don't skip selected
438
                    // features or the feature is not selected
439
                    if (selectedFeaturesToSkip == null
440
                        || !selectedFeaturesToSkip.isSelected(current)) {
441
                        try {
442
                            values[i] = current.getCopy();
443
                            i++;
444
                        } catch(Exception ex) {
445
                            // Aqui no deberia petar, pero...
446
                            // me he encontrado un caso que tenia una referencia a
447
                            // una feature seleccionada que ya no existia. No se como 
448
                            // habia pasado, se habia quedado de antes guardada en el
449
                            // proyecto pero la feature ya no existia, y eso hacia que
450
                            // petase al intentar leer de disco la feature a partir
451
                            // de una referencia no valida.
452
                        }
453
                    }
454
                }
455
            }, initialIndex);
456
        } catch (BaseException e) {
457
            if (e instanceof DataException) {
458
                throw ((DataException) e);
459
            } else {
460
                LOG.error("Error loading the data starting at position {}",
461
                    new Long(initialIndex), e);
462
            }
463
        }
464
    }
465

    
466
    public void delete(Feature feature) throws BaseException {
467
        featureStore.delete(feature);
468
        /*
469
         * Force re-creation of feature set
470
         */
471
        this.getFeatureSet(true);
472

    
473
        reloadCurrentPage();
474
    }
475

    
476
    public void insert(EditableFeature feature) throws BaseException {
477
            featureStore.insert(feature);
478
        /*
479
         * Force re-creation of feature set
480
         */
481
        this.getFeatureSet(true);
482

    
483
        reloadCurrentPage();
484
    }
485

    
486
    public void update(EditableFeature feature) throws BaseException {
487
            featureStore.update(feature);
488
        /*
489
         * Force re-creation of feature set
490
         */
491
        this.getFeatureSet(true);
492

    
493
        reloadCurrentPage();
494
    }
495

    
496
    public FeatureType getFeatureType() {
497
        
498
        FeatureType ft = null;
499
        
500
        try {
501
            ft = featureStore.getDefaultFeatureType();
502
        } catch (DataException e) {
503
            LOG.error("Error while getting feature type: " +
504
                e.getMessage(), e);
505
        }
506
        return ft;
507
        
508
        /*
509
         * 
510
        FeatureSet featureSet = getFeatureSet();
511
        try {
512
            return featureSet.getDefaultFeatureType();
513
        } finally {
514
            featureSet.dispose();
515
        }
516
        */
517

    
518
        
519
    }
520

    
521
    protected void doDispose() throws BaseException {
522
        initialSelection.dispose();
523
        if (featSet != null) {
524
            try {
525
                featSet.dispose();
526
            } catch (Exception ex) {
527
                LOG.info("Error while disposing featset.", ex);
528
            }
529
        }
530
    }
531

    
532
    public DynObject[] getCurrentPageDynObjects() {
533
        Feature[] features = getCurrentPageFeatures();
534
        DynObject[] dynobjects = new DynObject[features.length];
535
        for (int i = 0; i < dynobjects.length; i++) {
536
            dynobjects[i] = new DynObjectFeatureFacade(features[i]);
537
        }
538
        return dynobjects;
539
    }
540

    
541
    public DynObject getDynObjectAt(long index) throws BaseException {
542
        return new DynObjectFeatureFacade(getFeatureAt(index));
543
    }
544

    
545
    public List asList() {
546
        return new FeaturePagingHelperList();
547
    }
548
    
549
    public List asListOfDynObjects() {
550
        return new DynObjectPagingHelperList();
551
    }    
552
    
553
    private class FeaturePagingHelperList extends PagingHelperList {
554
        public Object get(int i) {
555
            try {
556
                return getFeatureAt(i);
557
            } catch (BaseException ex) {
558
                throw  new RuntimeException(ex);
559
            }
560
        }
561
    }
562
    
563
    private class DynObjectPagingHelperList extends PagingHelperList {
564
        public Object get(int i) {
565
            try {
566
                return getDynObjectAt(i);
567
            } catch (BaseException ex) {
568
                throw  new RuntimeException(ex);
569
            }
570
        }
571
    }
572
    
573
    private abstract class PagingHelperList implements List {
574

    
575
        public int size() {
576
            try {
577
                return (int) getFeatureSet(false).getSize();
578
            } catch (DataException ex) {
579
                throw  new RuntimeException(ex);
580
            }
581
        }
582

    
583
        public boolean isEmpty() {
584
            try {
585
                return getFeatureSet(false).isEmpty();
586
            } catch (DataException ex) {
587
                throw  new RuntimeException(ex);
588
            }
589
        }
590

    
591
        public Iterator iterator() {
592
            try {
593
                return getFeatureSet(false).fastIterator();
594
            } catch (DataException ex) {
595
                throw  new RuntimeException(ex);
596
            }
597
        }
598

    
599
        public boolean contains(Object o) {
600
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
601
        }
602

    
603
        public Object[] toArray() {
604
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
605
        }
606

    
607
        public Object[] toArray(Object[] ts) {
608
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
609
        }
610

    
611
        public boolean add(Object e) {
612
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
613
        }
614

    
615
        public boolean remove(Object o) {
616
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
617
        }
618

    
619
        public boolean containsAll(Collection clctn) {
620
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
621
        }
622

    
623
        public boolean addAll(Collection clctn) {
624
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
625
        }
626

    
627
        public boolean addAll(int i, Collection clctn) {
628
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
629
        }
630

    
631
        public boolean removeAll(Collection clctn) {
632
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
633
        }
634

    
635
        public boolean retainAll(Collection clctn) {
636
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
637
        }
638

    
639
        public void clear() {
640
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
641
        }
642

    
643
        public Object set(int i, Object e) {
644
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
645
        }
646

    
647
        public void add(int i, Object e) {
648
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
649
        }
650

    
651
        public Object remove(int i) {
652
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
653
        }
654

    
655
        public int indexOf(Object o) {
656
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
657
        }
658

    
659
        public int lastIndexOf(Object o) {
660
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
661
        }
662

    
663
        public ListIterator listIterator() {
664
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
665
        }
666

    
667
        public ListIterator listIterator(int i) {
668
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
669
        }
670

    
671
        public List subList(int i, int i1) {
672
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
673
        }
674
        
675
    }
676
}