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

History | View | Annotate | Download (16.8 KB)

1 40559 jjdelcerro
/**
2
 * gvSIG. Desktop Geographic Information System.
3 40435 jjdelcerro
 *
4 40559 jjdelcerro
 * Copyright (C) 2007-2013 gvSIG Association.
5 40435 jjdelcerro
 *
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 40559 jjdelcerro
 * as published by the Free Software Foundation; either version 3
9 40435 jjdelcerro
 * 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 40559 jjdelcerro
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23 40435 jjdelcerro
 */
24
/*
25
 * AUTHORS (In addition to CIT):
26
 * 2008 {DiSiD Technologies}  {Create a JTable TableModel for a FeatureCollection}
27
 */
28
package org.gvsig.fmap.dal.feature.paging.impl;
29
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
    /**
81
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
82
     *
83
     * @param featureStore
84
     *            to extract data from
85
     * @throws DataException
86
     *             if there is an error initializing the helper
87
     */
88
    public FeaturePagingHelperImpl(FeatureStore featureStore)
89
        throws BaseException {
90
        this(featureStore, DEFAULT_PAGE_SIZE);
91
    }
92
93
    /**
94
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
95
     *
96
     * @param featureStore
97
     *            to extract data from
98
     * @param pageSize
99
     *            the number of elements per page data
100
     * @throws DataException
101
     *             if there is an error initializing the helper
102
     */
103
    public FeaturePagingHelperImpl(FeatureStore featureStore, int pageSize)
104
        throws BaseException {
105
        this(featureStore, null, pageSize);
106
    }
107
108
    /**
109
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
110
     *
111
     * @param featureStore
112
     *            to extract data from
113
     * @throws DataException
114
     *             if there is an error initializing the helper
115
     */
116
    public FeaturePagingHelperImpl(FeatureStore featureStore,
117
        FeatureQuery featureQuery) throws BaseException {
118
        this(featureStore, featureQuery, DEFAULT_PAGE_SIZE);
119
    }
120
121
    /**
122
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
123
     *
124
     * @param featureSet
125
     *            to extract data from
126
     * @param pageSize
127
     *            the number of elements per page data
128
     * @throws DataException
129
     *             if there is an error initializing the helper
130
     */
131
    public FeaturePagingHelperImpl(FeatureStore featureStore,
132
        FeatureQuery featureQuery, int pageSize) throws BaseException {
133
        super();
134
        FeatureQuery query = featureQuery;
135
        if (featureQuery == null) {
136
            query = featureStore.createFeatureQuery();
137
            query.setFeatureType(featureStore.getDefaultFeatureType());
138
        }
139
140
        this.featureStore = featureStore;
141
        this.query = query;
142
        this.query.setPageSize(pageSize);
143
144
        if (LOG.isDebugEnabled()) {
145
            LOG.debug("FeaturePagingHelperImpl created with {} pages, "
146
                + "and a page size of {}", new Long(getCalculator()
147
                .getNumPages()), new Integer(pageSize));
148
        }
149
150
        setDefaultCalculator(new Sizeable() {
151
            public long getSize() {
152
                    FeatureSet featureSet = getFeatureSet(false);
153
                try {
154
                                        return featureSet.getSize();
155
                } catch (BaseException e) {
156
                    LOG.error("Error getting the size of the FeatureSet: "
157
                        + featureSet, e);
158
                    return 0l;
159
                }
160
            }
161
        }, pageSize);
162
    }
163
164
    /**
165
     * @return the selectionUp status
166
     */
167
    protected boolean isSelectionUp() {
168
        return selectionUp;
169
    }
170
171
    public void setSelectionUp(boolean selectionUp) {
172
        this.selectionUp = selectionUp;
173
        try {
174
            if (selectionUp) {
175
                initialSelection =
176
                    (FeatureSelection) getFeatureStore().getFeatureSelection()
177
                        .clone();
178
                setCalculator(new OneSubsetOneSetPagingCalculator(
179
                    new FeatureSetSizeableDelegate(initialSelection),
180
                    new FeatureSetSizeableDelegate(getFeatureSet(false)),
181
                    getMaxPageSize()));
182
            } else {
183
                if (initialSelection != null) {
184
                    initialSelection.dispose();
185
                    initialSelection = null;
186
                }
187
                setDefaultCalculator(new FeatureSetSizeableDelegate(
188
                    getFeatureSet(false)), getMaxPageSize());
189
            }
190
        } catch (BaseException e) {
191
            LOG.error("Error setting the selection up setting to: "
192
                + selectionUp, e);
193
        } catch (CloneNotSupportedException e) {
194
            LOG.error("Error cloning the selection "
195
                + "while setting the selection up", e);
196
        }
197
    }
198
199
    public Feature getFeatureAt(long index) throws BaseException {
200
        // Check if we have currently loaded the viewed page data,
201
        // or we need to load a new one
202
        long pageForIndex = (long) Math.floor(index / getMaxPageSize());
203
204
        if (pageForIndex != getCurrentPage()) {
205
            setCurrentPage(pageForIndex);
206
        }
207
208
        long positionForIndex = index - (getCurrentPage() * getMaxPageSize());
209
210
        if (positionForIndex >= features.length) {
211
            throw new FeatureIndexException(
212
                new IndexOutOfBoundsException("positionForIndex too big: "
213
                    + positionForIndex));
214
        } else {
215
            return features[(int) positionForIndex];
216
        }
217
218
    }
219
220
    public Feature[] getCurrentPageFeatures() {
221
        return features;
222
    }
223
224
    /**
225
     * Gets the feature set.
226
     * The boolean tells whether we must create the featureset
227
     * again (for example perhaps we need it after a feature
228
     * has been added/removed)
229
     */
230
    private FeatureSet getFeatureSet(boolean reset) {
231
232
        if (featSet == null || reset) {
233
234
            if (featSet != null) {
235
                try {
236
                    featSet.dispose();
237
                } catch (Exception ex) {
238
                    LOG.info("Error while disposing featset.", ex);
239
                }
240
            }
241
242
            try {
243
                FeatureStore featureStore = getFeatureStore();
244
                synchronized (featureStore) {
245
                    featSet = featureStore.getFeatureSet(getFeatureQuery());
246
                }
247
            } catch (DataException e) {
248
                throw new RuntimeException("Error getting a feature set with the query " + getFeatureQuery());
249
            }
250
        }
251
        return featSet;
252
    }
253
254
    public DynObjectSet getDynObjectSet() {
255
            return getFeatureSet(false).getDynObjectSet();
256
    }
257
258
    public void reloadCurrentPage() throws BaseException {
259
260
        boolean sel_up = this.isSelectionUp();
261
262
        setSelectionUp(false);
263
        if (getCalculator().getCurrentPage() > -1) {
264
            loadCurrentPageData();
265
        }
266
267
        if (sel_up) {
268
            setSelectionUp(true);
269
        }
270
    }
271
272
    public void reload() throws BaseException {
273
274
        /*
275
         * Force re-creation of feature set
276
         */
277
        this.getFeatureSet(true);
278
279
280
        setDefaultCalculator(new Sizeable() {
281
            public long getSize() {
282
                    FeatureSet featureSet = getFeatureSet(false);
283
                try {
284
                                        return featureSet.getSize();
285
                } catch (BaseException e) {
286
                    LOG.error("Error getting the size of the FeatureSet: "
287
                        + featureSet, e);
288
                    return 0l;
289
                }
290
            }
291
        }, getCalculator().getMaxPageSize());
292
        reloadCurrentPage();
293
    }
294
295
    public FeatureStore getFeatureStore() {
296
        return featureStore;
297
    }
298
299
    public FeatureQuery getFeatureQuery() {
300
        return query;
301
    }
302
303
    /**
304
     * Loads all the Features of the current page.
305
     */
306
    protected void loadCurrentPageData() throws BaseException {
307
        final int currentPageSize = getCalculator().getCurrentPageSize();
308
        final Feature[] values = new Feature[currentPageSize];
309
310
        long t1 = 0;
311
        if (LOG.isTraceEnabled()) {
312
            t1 = System.currentTimeMillis();
313
        }
314
315
        if (selectionUp) {
316
            loadCurrentPageDataWithSelectionUp(values);
317
        } else {
318
            loadCurrentPageDataNoSelection(values);
319
        }
320
321
        if (LOG.isTraceEnabled()) {
322
            long t2 = System.currentTimeMillis();
323
            LOG.trace("Time to load {} features: {} ms", new Integer(
324
                currentPageSize), new Long(t2 - t1));
325
        }
326
327
        this.features = values;
328
    }
329
330
    private void loadCurrentPageDataWithSelectionUp(final Feature[] values)
331
        throws BaseException {
332
        FeatureSelection selection = initialSelection;
333
334
        FeatureSet set = getFeatureSet(false);
335
        try {
336
                OneSubsetOneSetPagingCalculator twoSetsCalculator = null;
337
                if (getCalculator() instanceof OneSubsetOneSetPagingCalculator) {
338
                    twoSetsCalculator =
339
                        (OneSubsetOneSetPagingCalculator) getCalculator();
340
                } else {
341
                    twoSetsCalculator =
342
                        new OneSubsetOneSetPagingCalculator(
343
                            new FeatureSetSizeableDelegate(selection),
344
                            new FeatureSetSizeableDelegate(set),
345
                            getMaxPageSize(), getCalculator().getCurrentPage());
346
                    setCalculator(twoSetsCalculator);
347
                }
348
349
                // First load values from the selection, if the current page has
350
                // elements from it
351
                if (twoSetsCalculator.hasCurrentPageAnyValuesInFirstSet()) {
352
                    loadDataFromFeatureSet(values, 0, selection,
353
                        twoSetsCalculator.getFirstSetInitialIndex(),
354
                        twoSetsCalculator.getFirstSetHowMany(), null);
355
                }
356
                // Next, load values from the FeatureSet if the current page has values
357
                // from it
358
                if (twoSetsCalculator.hasCurrentPageAnyValuesInSecondSet()) {
359
                    loadDataFromFeatureSet(
360
                        values,
361
                        // The cast will work as that size will be <= maxpagesize,
362
                        // which is an int
363
                        (int) twoSetsCalculator.getFirstSetHowMany(), set,
364
                        twoSetsCalculator.getSecondSetInitialIndex(),
365
                        twoSetsCalculator.getSecondSetHowMany(), selection);
366
                }
367
        } finally {
368
            /*
369
             * This is the feature set
370
             * we dont want to lose it
371
             */
372
                // set.dispose();
373
        }
374
    }
375
376
    private void loadCurrentPageDataNoSelection(final Feature[] values)
377
        throws BaseException {
378
379
        long firstPosition = getCalculator().getInitialIndex();
380
381
        if (LOG.isDebugEnabled()) {
382
            LOG.debug("Loading {} Features starting at position {}",
383
                new Integer(getCalculator().getCurrentPageSize()), new Long(
384
                    firstPosition));
385
        }
386
387
        FeatureSet featureSet = getFeatureSet(false);
388
        try {
389
                loadDataFromFeatureSet(values, 0, featureSet, firstPosition,
390
                                getCalculator().getCurrentPageSize(), null);
391
        } catch(DataException ex) {
392
            throw ex;
393
            // } finally {
394
                // featureSet.dispose();
395
        }
396
397
    }
398
399
    private void loadDataFromFeatureSet(final Feature[] values,
400
        final int valuesPosition, FeatureSet set, long initialIndex,
401
        final long howMany, final FeatureSelection selectedFeaturesToSkip)
402
        throws DataException {
403
404
        try {
405
            set.accept(new Visitor() {
406
407
                private int i = valuesPosition;
408
409
                public void visit(Object obj) throws VisitCanceledException,
410
                    BaseException {
411
                    if (i >= valuesPosition + howMany) {
412
                        throw new VisitCanceledException();
413
                    }
414
                    Feature current = (Feature) obj;
415
                    // Add the current Feature only if we don't skip selected
416
                    // features or the feature is not selected
417
                    if (selectedFeaturesToSkip == null
418
                        || !selectedFeaturesToSkip.isSelected(current)) {
419
                        values[i] = current.getCopy();
420
                        i++;
421
                    }
422
                }
423
            }, initialIndex);
424
        } catch (BaseException e) {
425
            if (e instanceof DataException) {
426
                throw ((DataException) e);
427
            } else {
428
                LOG.error("Error loading the data starting at position {}",
429
                    new Long(initialIndex), e);
430
            }
431
        }
432
    }
433
434
    public void delete(Feature feature) throws BaseException {
435
        featureStore.delete(feature);
436
        /*
437
         * Force re-creation of feature set
438
         */
439
        this.getFeatureSet(true);
440
441
        reloadCurrentPage();
442
    }
443
444
    public void insert(EditableFeature feature) throws BaseException {
445
            featureStore.insert(feature);
446
        /*
447
         * Force re-creation of feature set
448
         */
449
        this.getFeatureSet(true);
450
451
        reloadCurrentPage();
452
    }
453
454
    public void update(EditableFeature feature) throws BaseException {
455
            featureStore.update(feature);
456
        /*
457
         * Force re-creation of feature set
458
         */
459
        this.getFeatureSet(true);
460
461
        reloadCurrentPage();
462
    }
463
464
    public FeatureType getFeatureType() {
465
466
        FeatureType ft = null;
467
468
        try {
469
            ft = featureStore.getDefaultFeatureType();
470
        } catch (DataException e) {
471
            LOG.error("Error while getting feature type: " +
472
                e.getMessage(), e);
473
        }
474
        return ft;
475
476
        /*
477
         *
478
        FeatureSet featureSet = getFeatureSet();
479
        try {
480
            return featureSet.getDefaultFeatureType();
481
        } finally {
482
            featureSet.dispose();
483
        }
484
        */
485
486
487
    }
488
489
    protected void doDispose() throws BaseException {
490
        initialSelection.dispose();
491
        if (featSet != null) {
492
            try {
493
                featSet.dispose();
494
            } catch (Exception ex) {
495
                LOG.info("Error while disposing featset.", ex);
496
            }
497
        }
498
    }
499
500
    public DynObject[] getCurrentPageDynObjects() {
501
        Feature[] features = getCurrentPageFeatures();
502
        DynObject[] dynobjects = new DynObject[features.length];
503
        for (int i = 0; i < dynobjects.length; i++) {
504
            dynobjects[i] = new DynObjectFeatureFacade(features[i]);
505
        }
506
        return dynobjects;
507
    }
508
509
    public DynObject getDynObjectAt(long index) throws BaseException {
510
        return new DynObjectFeatureFacade(getFeatureAt(index));
511
    }
512
513
}