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

History | View | Annotate | Download (22.5 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
    protected boolean isSelectionUp() {
172
        return selectionUp;
173
    }
174

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
347
        this.features = values;
348
    }
349
    
350
    private void loadCurrentPageDataWithSelectionUp(final Feature[] values)
351
        throws BaseException {
352
        FeatureSelection selection = initialSelection;
353

    
354
        FeatureSet set = getFeatureSet(false);
355
        try {
356
                OneSubsetOneSetPagingCalculator twoSetsCalculator = null;
357
                if (getCalculator() instanceof OneSubsetOneSetPagingCalculator) {
358
                    twoSetsCalculator =
359
                        (OneSubsetOneSetPagingCalculator) getCalculator();
360
                } else {
361
                    twoSetsCalculator =
362
                        new OneSubsetOneSetPagingCalculator(
363
                            new FeatureSetSizeableDelegate(selection),
364
                            new FeatureSetSizeableDelegate(set),
365
                            getMaxPageSize(), getCalculator().getCurrentPage());
366
                    setCalculator(twoSetsCalculator);
367
                }
368
        
369
                // First load values from the selection, if the current page has
370
                // elements from it
371
                if (twoSetsCalculator.hasCurrentPageAnyValuesInFirstSet()) {
372
                    loadDataFromFeatureSet(values, 0, selection,
373
                        twoSetsCalculator.getFirstSetInitialIndex(),
374
                        twoSetsCalculator.getFirstSetHowMany(), null);
375
                }
376
                // Next, load values from the FeatureSet if the current page has values
377
                // from it
378
                if (twoSetsCalculator.hasCurrentPageAnyValuesInSecondSet()) {
379
                    loadDataFromFeatureSet(
380
                        values,
381
                        // The cast will work as that size will be <= maxpagesize,
382
                        // which is an int
383
                        (int) twoSetsCalculator.getFirstSetHowMany(), set,
384
                        twoSetsCalculator.getSecondSetInitialIndex(),
385
                        twoSetsCalculator.getSecondSetHowMany(), selection);
386
                }
387
        } finally {
388
            /*
389
             * This is the feature set
390
             * we dont want to lose it
391
             */
392
                // set.dispose();
393
        }
394
    }
395

    
396
    private void loadCurrentPageDataNoSelection(final Feature[] values)
397
        throws BaseException {
398

    
399
        long firstPosition = getCalculator().getInitialIndex();
400

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

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

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

    
424
        try {
425
            set.accept(new Visitor() {
426

    
427
                private int i = valuesPosition;
428

    
429
                public void visit(Object obj) throws VisitCanceledException,
430
                    BaseException {
431
                    if (i >= valuesPosition + howMany) {
432
                        throw new VisitCanceledException();
433
                    }
434
                    Feature current = (Feature) obj;
435
                    // Add the current Feature only if we don't skip selected
436
                    // features or the feature is not selected
437
                    if (selectedFeaturesToSkip == null
438
                        || !selectedFeaturesToSkip.isSelected(current)) {
439
                        values[i] = current.getCopy();
440
                        i++;
441
                    }
442
                }
443
            }, initialIndex);
444
        } catch (BaseException e) {
445
            if (e instanceof DataException) {
446
                throw ((DataException) e);
447
            } else {
448
                LOG.error("Error loading the data starting at position {}",
449
                    new Long(initialIndex), e);
450
            }
451
        }
452
    }
453

    
454
    public void delete(Feature feature) throws BaseException {
455
        featureStore.delete(feature);
456
        /*
457
         * Force re-creation of feature set
458
         */
459
        this.getFeatureSet(true);
460

    
461
        reloadCurrentPage();
462
    }
463

    
464
    public void insert(EditableFeature feature) throws BaseException {
465
            featureStore.insert(feature);
466
        /*
467
         * Force re-creation of feature set
468
         */
469
        this.getFeatureSet(true);
470

    
471
        reloadCurrentPage();
472
    }
473

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

    
481
        reloadCurrentPage();
482
    }
483

    
484
    public FeatureType getFeatureType() {
485
        
486
        FeatureType ft = null;
487
        
488
        try {
489
            ft = featureStore.getDefaultFeatureType();
490
        } catch (DataException e) {
491
            LOG.error("Error while getting feature type: " +
492
                e.getMessage(), e);
493
        }
494
        return ft;
495
        
496
        /*
497
         * 
498
        FeatureSet featureSet = getFeatureSet();
499
        try {
500
            return featureSet.getDefaultFeatureType();
501
        } finally {
502
            featureSet.dispose();
503
        }
504
        */
505

    
506
        
507
    }
508

    
509
    protected void doDispose() throws BaseException {
510
        initialSelection.dispose();
511
        if (featSet != null) {
512
            try {
513
                featSet.dispose();
514
            } catch (Exception ex) {
515
                LOG.info("Error while disposing featset.", ex);
516
            }
517
        }
518
    }
519

    
520
    public DynObject[] getCurrentPageDynObjects() {
521
        Feature[] features = getCurrentPageFeatures();
522
        DynObject[] dynobjects = new DynObject[features.length];
523
        for (int i = 0; i < dynobjects.length; i++) {
524
            dynobjects[i] = new DynObjectFeatureFacade(features[i]);
525
        }
526
        return dynobjects;
527
    }
528

    
529
    public DynObject getDynObjectAt(long index) throws BaseException {
530
        return new DynObjectFeatureFacade(getFeatureAt(index));
531
    }
532

    
533
    public List asList() {
534
        return new FeaturePagingHelperList();
535
    }
536
    
537
    public List asListOfDynObjects() {
538
        return new DynObjectPagingHelperList();
539
    }    
540
    
541
    private class FeaturePagingHelperList extends PagingHelperList {
542
        public Object get(int i) {
543
            try {
544
                return getFeatureAt(i);
545
            } catch (BaseException ex) {
546
                throw  new RuntimeException(ex);
547
            }
548
        }
549
    }
550
    
551
    private class DynObjectPagingHelperList extends PagingHelperList {
552
        public Object get(int i) {
553
            try {
554
                return getDynObjectAt(i);
555
            } catch (BaseException ex) {
556
                throw  new RuntimeException(ex);
557
            }
558
        }
559
    }
560
    
561
    private abstract class PagingHelperList implements List {
562

    
563
        public int size() {
564
            try {
565
                return (int) getFeatureSet(false).getSize();
566
            } catch (DataException ex) {
567
                throw  new RuntimeException(ex);
568
            }
569
        }
570

    
571
        public boolean isEmpty() {
572
            try {
573
                return getFeatureSet(false).isEmpty();
574
            } catch (DataException ex) {
575
                throw  new RuntimeException(ex);
576
            }
577
        }
578

    
579
        public Iterator iterator() {
580
            try {
581
                return getFeatureSet(false).fastIterator();
582
            } catch (DataException ex) {
583
                throw  new RuntimeException(ex);
584
            }
585
        }
586

    
587
        public boolean contains(Object o) {
588
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
589
        }
590

    
591
        public Object[] toArray() {
592
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
593
        }
594

    
595
        public Object[] toArray(Object[] ts) {
596
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
597
        }
598

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

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

    
607
        public boolean containsAll(Collection clctn) {
608
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
609
        }
610

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

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

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

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

    
627
        public void clear() {
628
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
629
        }
630

    
631
        public Object set(int i, Object e) {
632
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
633
        }
634

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

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

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

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

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

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

    
659
        public List subList(int i, int i1) {
660
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
661
        }
662
        
663
    }
664
}