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

History | View | Annotate | Download (16.7 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 org.slf4j.Logger;
27
import org.slf4j.LoggerFactory;
28

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

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

    
61
    private static final Logger LOG = LoggerFactory
62
        .getLogger(FeaturePagingHelperImpl.class);
63

    
64
    private FeatureQuery query;
65

    
66
    private FeatureStore featureStore;
67

    
68
    /** If the selected Features must be returned as the first ones. **/
69
    private boolean selectionUp = false;
70

    
71
    private FeatureSet featSet = null;
72
    private FeatureSelection initialSelection = null;
73

    
74
    private Feature[] features = null;
75

    
76
    /**
77
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
78
     * 
79
     * @param featureStore
80
     *            to extract data from
81
     * @throws DataException
82
     *             if there is an error initializing the helper
83
     */
84
    public FeaturePagingHelperImpl(FeatureStore featureStore)
85
        throws BaseException {
86
        this(featureStore, DEFAULT_PAGE_SIZE);
87
    }
88

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

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

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

    
136
        this.featureStore = featureStore;
137
        this.query = query;
138
        this.query.setPageSize(pageSize);
139

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

    
162
    /**
163
     * @return the selectionUp status
164
     */
165
    protected boolean isSelectionUp() {
166
        return selectionUp;
167
    }
168

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

    
197
    public Feature getFeatureAt(long index) throws BaseException {
198
        // Check if we have currently loaded the viewed page data,
199
        // or we need to load a new one
200
        long pageForIndex = (long) Math.floor(index / getMaxPageSize());
201

    
202
        if (pageForIndex != getCurrentPage()) {
203
            setCurrentPage(pageForIndex);
204
        }
205

    
206
        long positionForIndex = index - (getCurrentPage() * getMaxPageSize());
207

    
208
        if (positionForIndex >= features.length) {
209
            throw new FeatureIndexException(
210
                new IndexOutOfBoundsException("positionForIndex too big: "
211
                    + positionForIndex));
212
        } else {
213
            return features[(int) positionForIndex];
214
        }
215
        
216
    }
217

    
218
    public Feature[] getCurrentPageFeatures() {
219
        return features;
220
    }
221

    
222
    /**
223
     * Gets the feature set.
224
     * The boolean tells whether we must create the featureset
225
     * again (for example perhaps we need it after a feature
226
     * has been added/removed)
227
     */
228
    private FeatureSet getFeatureSet(boolean reset) {
229
        
230
        if (featSet == null || reset) {
231
            
232
            if (featSet != null) {
233
                try {
234
                    featSet.dispose();
235
                } catch (Exception ex) {
236
                    LOG.info("Error while disposing featset.", ex);
237
                }
238
            }
239
            
240
            try {
241
                FeatureStore featureStore = getFeatureStore();          
242
                synchronized (featureStore) {
243
                    featSet = featureStore.getFeatureSet(getFeatureQuery());               
244
                }
245
            } catch (DataException e) {
246
                throw new RuntimeException("Error getting a feature set with the query " + getFeatureQuery());
247
            }
248
        }
249
        return featSet;
250
    }
251
    
252
    public DynObjectSet getDynObjectSet() {
253
            return getFeatureSet(false).getDynObjectSet();
254
    }
255

    
256
    public void reloadCurrentPage() throws BaseException {
257
        
258
        boolean sel_up = this.isSelectionUp();
259

    
260
        setSelectionUp(false);
261
        if (getCalculator().getCurrentPage() > -1) {
262
            loadCurrentPageData();
263
        }
264
        
265
        if (sel_up) {
266
            setSelectionUp(true);
267
        }
268
    }
269

    
270
    public void reload() throws BaseException {
271
        
272
        /*
273
         * Force re-creation of feature set
274
         */
275
        this.getFeatureSet(true);
276

    
277
        
278
        setDefaultCalculator(new Sizeable() {
279
            public long getSize() {
280
                    FeatureSet featureSet = getFeatureSet(false);
281
                try {
282
                                        return featureSet.getSize();
283
                } catch (BaseException e) {
284
                    LOG.error("Error getting the size of the FeatureSet: "
285
                        + featureSet, e);
286
                    return 0l;
287
                }
288
            }
289
        }, getCalculator().getMaxPageSize());
290
        reloadCurrentPage();
291
    }
292

    
293
    public FeatureStore getFeatureStore() {
294
        return featureStore;
295
    }
296

    
297
    public FeatureQuery getFeatureQuery() {
298
        return query;
299
    }
300

    
301
    /**
302
     * Loads all the Features of the current page.
303
     */
304
    protected void loadCurrentPageData() throws BaseException {
305
        final int currentPageSize = getCalculator().getCurrentPageSize();
306
        final Feature[] values = new Feature[currentPageSize];
307

    
308
        long t1 = 0;
309
        if (LOG.isTraceEnabled()) {
310
            t1 = System.currentTimeMillis();
311
        }
312

    
313
        if (selectionUp) {
314
            loadCurrentPageDataWithSelectionUp(values);
315
        } else {
316
            loadCurrentPageDataNoSelection(values);
317
        }
318

    
319
        if (LOG.isTraceEnabled()) {
320
            long t2 = System.currentTimeMillis();
321
            LOG.trace("Time to load {} features: {} ms", new Integer(
322
                currentPageSize), new Long(t2 - t1));
323
        }
324

    
325
        this.features = values;
326
    }
327
    
328
    private void loadCurrentPageDataWithSelectionUp(final Feature[] values)
329
        throws BaseException {
330
        FeatureSelection selection = initialSelection;
331

    
332
        FeatureSet set = getFeatureSet(false);
333
        try {
334
                OneSubsetOneSetPagingCalculator twoSetsCalculator = null;
335
                if (getCalculator() instanceof OneSubsetOneSetPagingCalculator) {
336
                    twoSetsCalculator =
337
                        (OneSubsetOneSetPagingCalculator) getCalculator();
338
                } else {
339
                    twoSetsCalculator =
340
                        new OneSubsetOneSetPagingCalculator(
341
                            new FeatureSetSizeableDelegate(selection),
342
                            new FeatureSetSizeableDelegate(set),
343
                            getMaxPageSize(), getCalculator().getCurrentPage());
344
                    setCalculator(twoSetsCalculator);
345
                }
346
        
347
                // First load values from the selection, if the current page has
348
                // elements from it
349
                if (twoSetsCalculator.hasCurrentPageAnyValuesInFirstSet()) {
350
                    loadDataFromFeatureSet(values, 0, selection,
351
                        twoSetsCalculator.getFirstSetInitialIndex(),
352
                        twoSetsCalculator.getFirstSetHowMany(), null);
353
                }
354
                // Next, load values from the FeatureSet if the current page has values
355
                // from it
356
                if (twoSetsCalculator.hasCurrentPageAnyValuesInSecondSet()) {
357
                    loadDataFromFeatureSet(
358
                        values,
359
                        // The cast will work as that size will be <= maxpagesize,
360
                        // which is an int
361
                        (int) twoSetsCalculator.getFirstSetHowMany(), set,
362
                        twoSetsCalculator.getSecondSetInitialIndex(),
363
                        twoSetsCalculator.getSecondSetHowMany(), selection);
364
                }
365
        } finally {
366
            /*
367
             * This is the feature set
368
             * we dont want to lose it
369
             */
370
                // set.dispose();
371
        }
372
    }
373

    
374
    private void loadCurrentPageDataNoSelection(final Feature[] values)
375
        throws BaseException {
376

    
377
        long firstPosition = getCalculator().getInitialIndex();
378

    
379
        if (LOG.isDebugEnabled()) {
380
            LOG.debug("Loading {} Features starting at position {}",
381
                new Integer(getCalculator().getCurrentPageSize()), new Long(
382
                    firstPosition));
383
        }
384

    
385
        FeatureSet featureSet = getFeatureSet(false);
386
        try {
387
                loadDataFromFeatureSet(values, 0, featureSet, firstPosition,
388
                                getCalculator().getCurrentPageSize(), null);
389
        } catch(DataException ex) {
390
            throw ex;
391
            // } finally {
392
                // featureSet.dispose();
393
        }
394
        
395
    }
396

    
397
    private void loadDataFromFeatureSet(final Feature[] values,
398
        final int valuesPosition, FeatureSet set, long initialIndex,
399
        final long howMany, final FeatureSelection selectedFeaturesToSkip)
400
        throws DataException {
401

    
402
        try {
403
            set.accept(new Visitor() {
404

    
405
                private int i = valuesPosition;
406

    
407
                public void visit(Object obj) throws VisitCanceledException,
408
                    BaseException {
409
                    if (i >= valuesPosition + howMany) {
410
                        throw new VisitCanceledException();
411
                    }
412
                    Feature current = (Feature) obj;
413
                    // Add the current Feature only if we don't skip selected
414
                    // features or the feature is not selected
415
                    if (selectedFeaturesToSkip == null
416
                        || !selectedFeaturesToSkip.isSelected(current)) {
417
                        values[i] = current.getCopy();
418
                        i++;
419
                    }
420
                }
421
            }, initialIndex);
422
        } catch (BaseException e) {
423
            if (e instanceof DataException) {
424
                throw ((DataException) e);
425
            } else {
426
                LOG.error("Error loading the data starting at position {}",
427
                    new Long(initialIndex), e);
428
            }
429
        }
430
    }
431

    
432
    public void delete(Feature feature) throws BaseException {
433
        featureStore.delete(feature);
434
        /*
435
         * Force re-creation of feature set
436
         */
437
        this.getFeatureSet(true);
438

    
439
        reloadCurrentPage();
440
    }
441

    
442
    public void insert(EditableFeature feature) throws BaseException {
443
            featureStore.insert(feature);
444
        /*
445
         * Force re-creation of feature set
446
         */
447
        this.getFeatureSet(true);
448

    
449
        reloadCurrentPage();
450
    }
451

    
452
    public void update(EditableFeature feature) throws BaseException {
453
            featureStore.update(feature);
454
        /*
455
         * Force re-creation of feature set
456
         */
457
        this.getFeatureSet(true);
458

    
459
        reloadCurrentPage();
460
    }
461

    
462
    public FeatureType getFeatureType() {
463
        
464
        FeatureType ft = null;
465
        
466
        try {
467
            ft = featureStore.getDefaultFeatureType();
468
        } catch (DataException e) {
469
            LOG.error("Error while getting feature type: " +
470
                e.getMessage(), e);
471
        }
472
        return ft;
473
        
474
        /*
475
         * 
476
        FeatureSet featureSet = getFeatureSet();
477
        try {
478
            return featureSet.getDefaultFeatureType();
479
        } finally {
480
            featureSet.dispose();
481
        }
482
        */
483

    
484
        
485
    }
486

    
487
    protected void doDispose() throws BaseException {
488
        initialSelection.dispose();
489
        if (featSet != null) {
490
            try {
491
                featSet.dispose();
492
            } catch (Exception ex) {
493
                LOG.info("Error while disposing featset.", ex);
494
            }
495
        }
496
    }
497

    
498
    public DynObject[] getCurrentPageDynObjects() {
499
        Feature[] features = getCurrentPageFeatures();
500
        DynObject[] dynobjects = new DynObject[features.length];
501
        for (int i = 0; i < dynobjects.length; i++) {
502
            dynobjects[i] = new DynObjectFeatureFacade(features[i]);
503
        }
504
        return dynobjects;
505
    }
506

    
507
    public DynObject getDynObjectAt(long index) throws BaseException {
508
        return new DynObjectFeatureFacade(getFeatureAt(index));
509
    }
510

    
511
}