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

History | View | Annotate | Download (16.7 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
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 2
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
 */
22

    
23
/*
24
 * AUTHORS (In addition to CIT):
25
 * 2008 {DiSiD Technologies}  {Create a JTable TableModel for a FeatureCollection}
26
 */
27
package org.gvsig.fmap.dal.feature.paging.impl;
28

    
29
import org.slf4j.Logger;
30
import org.slf4j.LoggerFactory;
31

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

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

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

    
67
    private FeatureQuery query;
68

    
69
    private FeatureStore featureStore;
70

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

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

    
77
    private Feature[] features = null;
78

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

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

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

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

    
139
        this.featureStore = featureStore;
140
        this.query = query;
141
        this.query.setPageSize(pageSize);
142

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
406
                private int i = valuesPosition;
407

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

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

    
440
        reloadCurrentPage();
441
    }
442

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

    
450
        reloadCurrentPage();
451
    }
452

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

    
460
        reloadCurrentPage();
461
    }
462

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

    
485
        
486
    }
487

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

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

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

    
512
}