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

History | View | Annotate | Download (37 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 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.ArrayList;
27
import java.util.Collection;
28
import java.util.Date;
29
import java.util.Iterator;
30
import java.util.List;
31
import java.util.ListIterator;
32
import java.util.logging.Level;
33
import org.apache.commons.lang3.builder.ToStringBuilder;
34
import org.apache.commons.lang3.mutable.MutableBoolean;
35
import org.slf4j.Logger;
36
import org.slf4j.LoggerFactory;
37

    
38
import org.gvsig.fmap.dal.exception.DataException;
39
import org.gvsig.fmap.dal.feature.EditableFeature;
40
import org.gvsig.fmap.dal.feature.Feature;
41
import org.gvsig.fmap.dal.feature.FeatureQuery;
42
import org.gvsig.fmap.dal.feature.FeatureSelection;
43
import org.gvsig.fmap.dal.feature.FeatureSet;
44
import org.gvsig.fmap.dal.feature.FeatureStore;
45
import org.gvsig.fmap.dal.feature.FeatureType;
46
import org.gvsig.fmap.dal.feature.exception.ConcurrentDataModificationException;
47
import org.gvsig.fmap.dal.feature.exception.FeatureIndexException;
48
import org.gvsig.fmap.dal.feature.impl.dynobjectutils.DynObjectFeatureFacade;
49
import org.gvsig.fmap.dal.feature.paging.FacadeOfAFeaturePagingHelper;
50
import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper;
51
import org.gvsig.tools.dispose.Disposable;
52
import org.gvsig.tools.dispose.DisposeUtils;
53
import org.gvsig.tools.dynobject.DynObject;
54
import org.gvsig.tools.dynobject.DynObjectSet;
55
import org.gvsig.tools.dynobject.impl.DefaultDynObjectPagingHelper;
56
import org.gvsig.tools.exception.BaseException;
57
import org.gvsig.tools.util.UnmodifiableBasicList;
58
import org.gvsig.tools.util.UnmodifiableBasicList64;
59
import org.gvsig.tools.visitor.VisitCanceledException;
60
import org.gvsig.tools.visitor.Visitor;
61

    
62
/**
63
 * Helper class to access the values of a FeatureCollection by position. Handles
64
 * pagination automatically to avoid filling the memory in case of big
65
 * collections.
66
 *
67
 * TODO: evaluate if its more convenient to read values in the background when
68
 * the returned value is near the end of the page, instead of loading a page on
69
 * demand.
70
 *
71
 * @author gvSIG Team
72
 */
73
@SuppressWarnings("UseSpecificCatch")
74
public class FeaturePagingHelperImpl extends DefaultDynObjectPagingHelper
75
        implements FeaturePagingHelper {
76

    
77
    private static final Logger LOG = LoggerFactory.getLogger(FeaturePagingHelperImpl.class);
78

    
79
    private static class Page {
80

    
81
        private Feature[] features;
82
        private final long number;
83
        private final int size;
84
        private long lastaccess;
85

    
86
        public Page(long number, int size) {
87
            this.size = size;
88
            this.number = number;
89
            this.features = new Feature[size];
90
            this.lastaccess = 0;
91
        }
92

    
93
        public void setFeature(int i, Feature copy) {
94
            this.features[i] = copy;
95
        }
96

    
97
        public Feature[] getFeatures() {
98
            this.lastaccess = (new Date()).getTime();
99
            return this.features;
100
        }
101

    
102
        public long getPageNumber() {
103
            return this.number;
104
        }
105

    
106
        public long getLastAccess() {
107
            return this.lastaccess;
108
        }
109

    
110
        public int size() {
111
            return this.size;
112
        }
113

    
114
        public void dispose() {
115
            for (int i = 0; i < features.length; i++) {
116
                features[i] = null;
117
            }
118
            this.features = null;
119
            this.lastaccess = 0;
120
        }
121
    }
122

    
123
    private static class PageCache {
124

    
125
        private final int maxpages;
126
        private List<Page> pages;
127

    
128
        public PageCache(int maxpages) {
129
            this.maxpages = maxpages;
130
            this.pages = new ArrayList<>();
131
        }
132

    
133
        public void clear() {
134
            pages.forEach((page) -> {
135
                page.dispose();
136
            });
137
            this.pages = new ArrayList<>();
138
        }
139

    
140
        public Page get(long pageNumber) {
141
            for (Page page : pages) {
142
                if (page.getPageNumber() == pageNumber) {
143
                    return page;
144
                }
145
            }
146
            return null;
147
        }
148

    
149
        public void add(Page page) {
150
            if (this.pages.size() < this.maxpages) {
151
                this.pages.add(page);
152
                return;
153
            }
154
            int toDrop = 0;
155
            for (int i = 0; i < this.pages.size(); i++) {
156
                if (this.pages.get(i).getLastAccess() < this.pages.get(toDrop).getLastAccess()) {
157
                    toDrop = i;
158
                }
159
            }
160
            this.pages.set(toDrop, page);
161
        }
162
    }
163

    
164
    private FeatureQuery query;
165

    
166
    private FeatureStore featureStore;
167

    
168
    /**
169
     * If the selected Features must be returned as the first ones. *
170
     */
171
    private boolean selectionUp = false;
172

    
173
    private FeatureSet featSet = null;
174

    
175
    private Feature[] features = null;
176
    private PageCache cachedPages = null;
177

    
178
    private boolean initialization_completed = false;
179

    
180
    private FeatureSelection selection = null;
181

    
182
    /**
183
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
184
     *
185
     * @param featureStore to extract data from
186
     * @throws DataException if there is an error initializing the helper
187
     */
188
    public FeaturePagingHelperImpl(FeatureStore featureStore)
189
            throws BaseException {
190
        this(featureStore, DEFAULT_PAGE_SIZE);
191
    }
192

    
193
    /**
194
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
195
     *
196
     * @param featureStore to extract data from
197
     * @param pageSize the number of elements per page data
198
     * @throws DataException if there is an error initializing the helper
199
     */
200
    public FeaturePagingHelperImpl(FeatureStore featureStore, int pageSize)
201
            throws BaseException {
202
        this(featureStore, null, pageSize);
203
    }
204

    
205
    /**
206
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
207
     *
208
     * @param featureStore to extract data from
209
     * @param featureQuery
210
     * @throws DataException if there is an error initializing the helper
211
     */
212
    public FeaturePagingHelperImpl(FeatureStore featureStore,
213
            FeatureQuery featureQuery) throws BaseException {
214
        this(featureStore, featureQuery, DEFAULT_PAGE_SIZE);
215
    }
216

    
217
    /**
218
     * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
219
     *
220
     * @param featureStore
221
     * @param featureQuery
222
     * @param pageSize the number of elements per page data
223
     * @throws DataException if there is an error initializing the helper
224
     */
225
    public FeaturePagingHelperImpl(FeatureStore featureStore,
226
            FeatureQuery featureQuery, int pageSize) throws BaseException {
227
        super();
228
        this.cachedPages = new PageCache(3);
229
        FeatureQuery theQuery = featureQuery;
230
        if (featureQuery == null) {
231
            theQuery = featureStore.createFeatureQuery();
232
            theQuery.setFeatureType(featureStore.getDefaultFeatureType());
233
        }
234

    
235
        this.featureStore = featureStore;
236
        DisposeUtils.bind(this.featureStore);
237
        
238
        this.query = theQuery;
239
        this.query.setPageSize(pageSize);
240

    
241
        setDefaultCalculator(() -> {
242
            FeatureSet featureSet = getFeatureSet(false);
243
            try {
244
                return featureSet.getSize();
245
            } catch (BaseException e) {
246
                LOG.warn("Error getting the size of the FeatureSet: " + featureSet, e);
247
                return 0l;
248
            }
249
        }, pageSize);
250
        if (LOG.isDebugEnabled()) {
251
            LOG.debug("FeaturePagingHelperImpl created with {} pages, and a page size of {}",
252
                    getCalculator().getNumPages(), pageSize
253
            );
254
        }
255
        this.initialization_completed = true;
256
    }
257

    
258
    /**
259
     * @return the selectionUp status
260
     */
261
    @Override
262
    public boolean isSelectionUp() {
263
        return selectionUp;
264
    }
265

    
266
    @Override
267
    public FeatureSelection getSelection() {
268
        if (selection == null) {
269
            try {
270
                return getFeatureStore().getFeatureSelection();
271
            } catch (Exception e) {
272
                LOG.warn("Error getting the selection", e);
273
            }
274
        }
275
        return selection;
276
    }
277

    
278
    @Override
279
    public void setSelection(FeatureSelection selection) {
280
        DisposeUtils.disposeQuietly(this.selection);
281
        this.selection = selection;
282
        DisposeUtils.bind(this.selection);
283
    }
284

    
285
    @Override
286
    public void setSelectionUp(boolean selectionUp) {
287
        this.selectionUp = selectionUp;
288
        try {
289
            this.cachedPages.clear();
290
            FeatureSelection currentSelection = getSelection();
291
            if (selectionUp && !currentSelection.isEmpty()) {
292
//                initialSelection =(FeatureSelection) currentSelection.clone();
293
                setCalculator(new OneSubsetOneSetPagingCalculator(
294
                        new FeatureSetSizeableDelegate(currentSelection),
295
                        new FeatureSetSizeableDelegate(getFeatureSet(false)),
296
                        getMaxPageSize()));
297
            } else {
298
                setDefaultCalculator(new FeatureSetSizeableDelegate(
299
                        getFeatureSet(false)), getMaxPageSize()
300
                );
301
            }
302
        } catch (BaseException e) {
303
            LOG.warn("Error setting the selection up setting to: " + selectionUp, e);
304
        }
305
    }
306

    
307
    @Override
308
    public synchronized Feature getFeatureAt(long index) throws BaseException {
309
        // Check if we have currently loaded the viewed page data,
310
        // or we need to load a new one
311
        int maxPageSize = getMaxPageSize();
312
        long currentPage = getCurrentPage();
313
        long currentPage2 = currentPage;
314

    
315
        long pageForIndex = (long) Math.floor(index / maxPageSize);
316

    
317
        if (pageForIndex != currentPage) {
318
            setCurrentPage(pageForIndex);
319
            currentPage2 = getCurrentPage();
320
        }
321

    
322
        long positionForIndex = index - (currentPage2 * maxPageSize);
323

    
324
        if (positionForIndex >= getCurrentPageFeatures().length) {
325
            throw new FeatureIndexException(
326
                    new IndexOutOfBoundsException("positionForIndex too big: "
327
                            + positionForIndex));
328
        } else {
329
            Feature feature = getCurrentPageFeatures()[(int) positionForIndex];
330
            return feature;
331
        }
332

    
333
    }
334

    
335
    @Override
336
    public Feature[] getCurrentPageFeatures() {
337
        if (this.features == null) {
338
            try {
339
                this.loadCurrentPageData();
340
            } catch (BaseException ex) {
341
                // Do nothing
342
            }
343
            if (this.features == null) {
344
                String msg = "Can't retrieve the features from current page.";
345
                LOG.warn(msg);
346
                throw new RuntimeException(msg);
347
            }
348
        }
349
        return features;
350
    }
351

    
352
    @Override
353
    public FeatureSet getFeatureSet() {
354
        return this.getFeatureSet(false);
355
    }
356

    
357
    /**
358
     * Gets the feature set. The boolean tells whether we must create the
359
     * featureset again (for example perhaps we need it after a feature has been
360
     * added/removed)
361
     */
362
    private FeatureSet getFeatureSet(boolean reset) {
363

    
364
        if (featSet == null || reset) {
365

    
366
            if (featSet != null) {
367
                try {
368
                    featSet.dispose();
369
                    featSet = null;
370
                } catch (Exception ex) {
371
                    LOG.info("Error while disposing featset.", ex);
372
                }
373
            }
374

    
375
            try {
376
                FeatureStore featureStore = getFeatureStore();
377
                synchronized (featureStore) {
378
                    featSet = featureStore.getFeatureSet(getFeatureQuery());
379
                }
380
            } catch (DataException e) {
381
                throw new RuntimeException("Error getting a feature set with the query " + getFeatureQuery(), e);
382
            }
383
        }
384
        return featSet;
385
    }
386

    
387
    @Override
388
    public DynObjectSet getDynObjectSet() {
389
        return getFeatureSet(false).getDynObjectSet();
390
    }
391

    
392
    @Override
393
    public void reloadCurrentPage() throws BaseException {
394
        boolean sel_up = this.isSelectionUp();
395
        try {
396
            setSelectionUp(false);
397
            if (getCalculator().getCurrentPage() > -1) {
398
                this.cachedPages.clear();
399
                loadCurrentPageData();
400
            }
401
        } finally {
402
            if (sel_up) {
403
                setSelectionUp(true);
404
            }
405
        }
406
    }
407

    
408
    @Override
409
    public void reload() throws BaseException {
410

    
411
        this.cachedPages.clear();
412
        /*
413
         * Force re-creation of feature set
414
         */
415
        this.getFeatureSet(true);
416

    
417
        setDefaultCalculator(() -> {
418
            FeatureSet featureSet = getFeatureSet(false);
419
            try {
420
                return featureSet.getSize();
421
            } catch (BaseException e) {
422
                LOG.warn("Error getting the size of the FeatureSet: "+ featureSet, e);
423
                return 0l;
424
            }
425
        }, getCalculator().getMaxPageSize());
426
//        reloadCurrentPage();
427
    }
428

    
429
    @Override
430
    public FeatureStore getFeatureStore() {
431
        return featureStore;
432
    }
433

    
434
    @Override
435
    public FeatureQuery getFeatureQuery() {
436
        return query;
437
    }
438

    
439
    /**
440
     * Loads all the Features of the current page.
441
     *
442
     * @throws org.gvsig.tools.exception.BaseException
443
     */
444
    @Override
445
    protected synchronized void loadCurrentPageData() throws BaseException {
446
        if (!initialization_completed) {
447
            return;
448
        }
449
        final int currentPageSize = getCalculator().getCurrentPageSize();
450
        final long currentPage = getCalculator().getCurrentPage();
451
        Page page = this.cachedPages.get(currentPage);
452
        if (page == null) {
453
            page = new Page(currentPage, currentPageSize);
454

    
455
            long t1 = 0;
456
            if (LOG.isTraceEnabled()) {
457
                t1 = System.currentTimeMillis();
458
            }
459

    
460
            if (selectionUp) {
461
                loadCurrentPageDataWithSelectionUp(page);
462
            } else {
463
                loadCurrentPageDataNoSelection(page);
464
            }
465

    
466
            if (LOG.isTraceEnabled()) {
467
                long t2 = System.currentTimeMillis();
468
                LOG.trace("Time to load {} features: {} ms", currentPageSize, t2 - t1);
469
            }
470
            this.cachedPages.add(page);
471
        }
472
        this.features = page.getFeatures();
473
    }
474

    
475
    private void loadCurrentPageDataWithSelectionUp(final Page page)
476
            throws BaseException {
477
        FeatureSelection theSelection = getSelection();
478
        if (theSelection == null) {
479
            loadCurrentPageDataNoSelection(page);
480
        } else {
481
            FeatureSet set = getFeatureSet(false);
482
            try {
483
                OneSubsetOneSetPagingCalculator twoSetsCalculator = null;
484
                if (getCalculator() instanceof OneSubsetOneSetPagingCalculator) {
485
                    twoSetsCalculator = (OneSubsetOneSetPagingCalculator) getCalculator();
486
                } else {
487
                    twoSetsCalculator = new OneSubsetOneSetPagingCalculator(
488
                                    new FeatureSetSizeableDelegate(theSelection),
489
                                    new FeatureSetSizeableDelegate(set),
490
                                    getMaxPageSize(), getCalculator().getCurrentPage()
491
                    );
492
                    setCalculator(twoSetsCalculator);
493
                }
494
                // First load values from the selection, if the current page has
495
                // elements from it
496
                if (twoSetsCalculator.hasCurrentPageAnyValuesInFirstSet()) {
497
                    loadDataFromFeatureSet(page, 0, theSelection,
498
                            twoSetsCalculator.getFirstSetInitialIndex(),
499
                            twoSetsCalculator.getFirstSetHowMany(), null
500
                    );
501
                }
502
                // Next, load values from the FeatureSet if the current page has values
503
                // from it
504
                if (twoSetsCalculator.hasCurrentPageAnyValuesInSecondSet()) {
505
                    loadDataFromFeatureSet(
506
                            page,
507
                            // The cast will work as that size will be <= maxpagesize,
508
                            // which is an int
509
                            (int) twoSetsCalculator.getFirstSetHowMany(), set,
510
                            twoSetsCalculator.getSecondSetInitialIndex(),
511
                            twoSetsCalculator.getSecondSetHowMany(), theSelection
512
                    );
513
                }
514
            } finally {
515
                // This is the feature set we dont want to lose it
516
                // set.dispose();
517
            }
518
        }
519
    }
520

    
521
    private void loadCurrentPageDataNoSelection(final Page page)
522
            throws BaseException {
523

    
524
        long firstPosition = getCalculator().getInitialIndex();
525

    
526
        if (LOG.isDebugEnabled()) {
527
            LOG.debug("Loading {} Features starting at position {}",
528
                    getCalculator().getCurrentPageSize(), firstPosition
529
            );
530
        }
531

    
532
        FeatureSet featureSet = getFeatureSet(false);
533
        try {
534
            loadDataFromFeatureSet(page, 0, featureSet, firstPosition,
535
                    getCalculator().getCurrentPageSize(), null);
536
        } catch (Exception ex) {
537
            throw ex;
538
        } finally {
539
            // This is the feature set we dont want to lose it
540
            // featureSet.dispose();
541
        }
542
    }
543

    
544
    private void loadDataFromFeatureSet(final Page page,
545
            final int valuesPosition, FeatureSet set, long initialIndex,
546
            final long howMany, final FeatureSelection selectedFeaturesToSkip)
547
            throws DataException {
548

    
549
        try {
550
            final MutableBoolean errorReported = new MutableBoolean(false);
551
            set.accept(new Visitor() {
552
                private int i = valuesPosition;
553

    
554
                @Override
555
                public void visit(Object obj) throws VisitCanceledException,
556
                        BaseException {
557
                    if (i >= valuesPosition + howMany) {
558
                        throw new VisitCanceledException();
559
                    }
560
                    Feature current = (Feature) obj;
561
                    // Add the current Feature only if we don't skip selected
562
                    // features or the feature is not selected
563
                    if (selectedFeaturesToSkip == null
564
                            || !selectedFeaturesToSkip.isSelected(current)) {
565
                        try {
566
                            page.setFeature(i, current.getCopy());
567
                            i++;
568
                        } catch (Exception ex) {
569
                            // Aqui no deberia petar, pero...
570
                            // me he encontrado un caso que tenia una referencia a
571
                            // una feature seleccionada que ya no existia. No se como
572
                            // habia pasado, se habia quedado de antes guardada en el
573
                            // proyecto pero la feature ya no existia, y eso hacia que
574
                            // petase al intentar leer de disco la feature a partir
575
                            // de una referencia no valida.
576
                            if (!errorReported.booleanValue()) {
577
                                // Solo sacamos un error por pagina de datos.
578
                                LOG.warn("Problemas recuperando feature.", ex);
579
                                errorReported.setTrue();
580
                            }
581
                        }
582
                    }
583
                }
584
            }, initialIndex, howMany);
585
        } catch (VisitCanceledException ex) {
586
            // Do nothing
587
        } catch (RuntimeException e) {
588
            throw e;
589
        } catch (Exception e) {
590
            if (e instanceof DataException) {
591
                throw ((DataException) e);
592
            } else {
593
                LOG.warn("Error loading the data starting at position {}", initialIndex, e);
594
            }
595
        }
596
    }
597

    
598
    @Override
599
    public void delete(Feature feature) throws BaseException {
600
        featureStore.delete(feature);
601
        /*
602
         * Force re-creation of feature set
603
         */
604
        this.getFeatureSet(true);
605

    
606
        reloadCurrentPage();
607
    }
608

    
609
    @Override
610
    public void insert(EditableFeature feature) throws BaseException {
611
        featureStore.insert(feature);
612
        /*
613
         * Force re-creation of feature set
614
         */
615
        this.getFeatureSet(true);
616

    
617
        reloadCurrentPage();
618
    }
619

    
620
    @Override
621
    public boolean isEmpty() {
622
        try {
623
            return getFeatureSet(false).isEmpty();
624
        } catch (ConcurrentDataModificationException ex) {
625
            LOG.warn("ConcurrentDataModification error asking about the emptiness of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
626
            try {
627
                reload();
628
            } catch (BaseException e) {
629
                LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
630
                throw new RuntimeException(e);
631
            }
632
            try {
633
                return getFeatureSet(false).isEmpty();
634
            } catch (RuntimeException e) {
635
                throw  e;
636
            } catch (Exception e) {
637
                LOG.warn("Error asking about the emptiness of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
638
                throw new RuntimeException(e);
639
            }
640
        } catch (Exception ex) {
641
            throw new RuntimeException(ex);
642
        }
643
    }
644

    
645
    @Override
646
    public void update(EditableFeature feature) throws BaseException {
647
        featureStore.update(feature);
648
        /*
649
         * Force re-creation of feature set
650
         */
651
        this.getFeatureSet(true);
652

    
653
        reloadCurrentPage();
654
    }
655

    
656
    @Override
657
    public FeatureType getFeatureType() {
658

    
659
        FeatureType ft = null;
660

    
661
        try {
662
            ft = featureStore.getDefaultFeatureType();
663
        } catch (DataException e) {
664
            LOG.warn("Error while getting feature type: "+ e.getMessage(), e);
665
        }
666
        return ft;
667
    }
668

    
669
    @Override
670
    protected void doDispose() throws BaseException {
671
        DisposeUtils.disposeQuietly(this.featSet);
672
        DisposeUtils.disposeQuietly(this.featureStore);
673
        DisposeUtils.disposeQuietly(this.selection);
674
    }
675

    
676
    @Override
677
    public DynObject[] getCurrentPageDynObjects() {
678
        Feature[] theFeatures = getCurrentPageFeatures();
679
        DynObject[] dynobjects = new DynObject[theFeatures.length];
680
        for (int i = 0; i < dynobjects.length; i++) {
681
            dynobjects[i] = new DynObjectFeatureFacade(theFeatures[i]);
682
        }
683
        return dynobjects;
684
    }
685

    
686
    @Override
687
    public DynObject getDynObjectAt(long index) throws BaseException {
688
        return new DynObjectFeatureFacade(getFeatureAt(index));
689
    }
690

    
691
    @Override
692
    public List asList() {
693
        return new FeaturePagingHelperList();
694
    }
695

    
696
    @Override
697
    public List asListOfDynObjects() {
698
        return new DynObjectPagingHelperList();
699
    }
700

    
701
    private class FeaturePagingHelperList extends PagingHelperList {
702

    
703
        @Override
704
        public Object get(int i) {
705
            return this.get64(i);
706
        }
707

    
708
        @Override
709
        public Object get64(long i) {
710
            try {
711
                return getFeatureAt(i);
712
            } catch (ConcurrentDataModificationException ex) {
713
                LOG.info("ConcurrentDataModification error getting feature " + i + " of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
714
                try {
715
                    reload();
716
                } catch (BaseException e) {
717
                    LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
718
                    throw new RuntimeException(e);
719
                }
720
                try {
721
                    return getFeatureAt(i);
722
                } catch (Exception e) {
723
                    LOG.warn("Error getting feature " + i + " of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
724
                    throw new RuntimeException(e);
725
                }
726
            } catch (BaseException ex) {
727
                throw new RuntimeException(ex);
728
            }
729
        }
730

    
731
        @Override
732
        public Object set(int i, Object e) {
733
            return super.set(i, e);
734
        }
735

    
736
        @Override
737
        public Object remove(int i) {
738
            return super.remove(i);
739
        }
740

    
741
        @Override
742
        public boolean add(Object e) {
743
            return super.add(e);
744
        }
745
    }
746

    
747
    private class DynObjectPagingHelperList extends PagingHelperList {
748

    
749
        @Override
750
        public Object get(int i) {
751
            return this.get64(i);
752
        }
753

    
754
        @Override
755
        public Object get64(long position) {
756
            try {
757
                return getDynObjectAt(position);
758
            } catch (ConcurrentDataModificationException ex) {
759
                LOG.warn("ConcurrentDataModification error getting element " + position + " of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
760
                try {
761
                    reload();
762
                } catch (BaseException e) {
763
                    LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
764
                    throw new RuntimeException(e);
765
                }
766
                try {
767
                    return getDynObjectAt(position);
768
                } catch (Exception e) {
769
                    LOG.warn("Error getting element " + position + " of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
770
                    throw new RuntimeException(e);
771
                }
772
            } catch(FeatureIndexException ex) {
773
                IndexOutOfBoundsException ioobe = new IndexOutOfBoundsException("");
774
                ioobe.initCause(ex);
775
                throw ioobe;
776
            } catch (BaseException ex) {
777
                throw new RuntimeException(ex);
778
            }
779
        }
780

    
781
    }
782

    
783
    private abstract class PagingHelperList implements List, FacadeOfAFeaturePagingHelper, UnmodifiableBasicList, UnmodifiableBasicList64, Disposable {
784

    
785
        @Override
786
        public void dispose() {
787
            FeaturePagingHelperImpl.this.dispose();
788
        }
789

    
790
        @Override
791
        public FeaturePagingHelper getFeaturePagingHelper() {
792
            return FeaturePagingHelperImpl.this;
793
        }
794

    
795
        @Override
796
        public String toString() {
797
            return String.format("..(%d %ss)...", this.size(), featureStore.getName());
798
        }
799

    
800
        @Override
801
        public long size64() {
802
            FeatureSet fset = null;
803
            try {
804
                fset = getFeatureSet(false);
805
                return fset.getSize();
806
            } catch (ConcurrentDataModificationException ex) {
807
                LOG.info("ConcurrentDataModification error asking the size of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
808
                try {
809
                    reload();
810
                } catch (BaseException e) {
811
                    LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
812
                    throw new RuntimeException(e);
813
                }
814
                try {
815
                    fset = getFeatureSet(false);
816
                    return fset.getSize();
817
                } catch (DataException e) {
818
                    LOG.warn("Error asking the size of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
819
                    throw new RuntimeException(e);
820
                }
821
            } catch (DataException ex) {
822
                throw new RuntimeException(ex);
823
            }
824
        }
825

    
826
        @Override
827
        public int size() {
828
            long sz = this.size64();
829
            if (sz > Integer.MAX_VALUE) {
830
                sz = Integer.MAX_VALUE;
831
            }
832
            return (int) sz;
833
        }
834

    
835
        @Override
836
        public boolean isEmpty() {
837
            try {
838
                return getFeatureSet(false).isEmpty();
839
            } catch (ConcurrentDataModificationException ex) {
840
                LOG.warn("ConcurrentDataModification error asking about the emptiness of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
841
                try {
842
                    reload();
843
                } catch (BaseException e) {
844
                    LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
845
                    throw new RuntimeException(e);
846
                }
847
                try {
848
                    return getFeatureSet(false).isEmpty();
849
                } catch (RuntimeException e) {
850
                    throw  e;
851
                } catch (Exception e) {
852
                    LOG.warn("Error asking about the emptiness of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
853
                    throw new RuntimeException(e);
854
                }
855
            } catch (RuntimeException ex) {
856
                throw  ex;
857
            } catch (Exception ex) {
858
                throw new RuntimeException(ex);
859
            }
860
        }
861

    
862
        @Override
863
        public Iterator iterator() {
864
            try {
865
                return getFeatureSet(false).fastIterator();
866
            } catch (ConcurrentDataModificationException ex) {
867
                LOG.warn("ConcurrentDataModification error getting iterator of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
868
                try {
869
                    reload();
870
                } catch (BaseException e) {
871
                    LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
872
                    throw new RuntimeException(e);
873
                }
874
                try {
875
                    return getFeatureSet(false).fastIterator();
876
                } catch (DataException e) {
877
                    LOG.warn("Error getting iterator of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
878
                    throw new RuntimeException(e);
879
                }
880
            } catch (DataException ex) {
881
                throw new RuntimeException(ex);
882
            }
883
        }
884

    
885
        @Override
886
        public boolean contains(Object o) {
887
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
888
        }
889

    
890
        @Override
891
        public Object[] toArray() {
892
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
893
        }
894

    
895
        @Override
896
        public Object[] toArray(Object[] ts) {
897
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
898
        }
899

    
900
        @Override
901
        public boolean add(Object e) {
902
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
903
        }
904

    
905
        @Override
906
        public boolean remove(Object o) {
907
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
908
        }
909

    
910
        @Override
911
        public boolean containsAll(Collection clctn) {
912
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
913
        }
914

    
915
        @Override
916
        public boolean addAll(Collection clctn) {
917
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
918
        }
919

    
920
        @Override
921
        public boolean addAll(int i, Collection clctn) {
922
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
923
        }
924

    
925
        @Override
926
        public boolean removeAll(Collection clctn) {
927
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
928
        }
929

    
930
        @Override
931
        public boolean retainAll(Collection clctn) {
932
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
933
        }
934

    
935
        @Override
936
        public void clear() {
937
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
938
        }
939

    
940
        @Override
941
        public Object set(int i, Object e) {
942
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
943
        }
944

    
945
        @Override
946
        public void add(int i, Object e) {
947
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
948
        }
949

    
950
        @Override
951
        public Object remove(int i) {
952
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
953
        }
954

    
955
        @Override
956
        public int indexOf(Object o) {
957
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
958
        }
959

    
960
        @Override
961
        public int lastIndexOf(Object o) {
962
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
963
        }
964

    
965
        @Override
966
        public ListIterator listIterator() {
967
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
968
        }
969

    
970
        @Override
971
        public ListIterator listIterator(int i) {
972
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
973
        }
974

    
975
        @Override
976
        public List subList(int i, int i1) {
977
            throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
978
        }
979

    
980
        @Override
981
        public List toList() {
982
            return this;
983
        }
984

    
985
    }
986

    
987
    @Override
988
    public long size64() {
989
        try {
990
            return this.getTotalSize();
991
        } catch (ConcurrentDataModificationException ex) {
992
            LOG.warn("ConcurrentDataModification error getting size of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
993
            try {
994
                reload();
995
            } catch (BaseException e) {
996
                LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
997
                throw new RuntimeException(e);
998
            }
999
            try {
1000
                return this.getTotalSize();
1001
            } catch (Exception e) {
1002
                LOG.warn("Error getting size of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
1003
                throw new RuntimeException(e);
1004
            }
1005
        } catch (Exception ex) {
1006
            throw new RuntimeException(ex);
1007
        }
1008
    }
1009

    
1010
    @Override
1011
    public Feature get64(long position) {
1012
        try {
1013
            return getFeatureAt(position);
1014
        } catch (ConcurrentDataModificationException ex) {
1015
            LOG.warn("ConcurrentDataModification error getting element " + position + " of the list. Retrying reloading data. "+(featSet==null?0:featSet.hashCode()));
1016
            try {
1017
                reload();
1018
            } catch (BaseException e) {
1019
                LOG.warn("Error reloading data. "+(featSet==null?0:featSet.hashCode()), e);
1020
                throw new RuntimeException(e);
1021
            }
1022
            try {
1023
                return getFeatureAt(position);
1024
            } catch (Exception e) {
1025
                LOG.warn("Error getting element " + position + " of the list after reloading data. "+(featSet==null?0:featSet.hashCode()), e);
1026
                throw new RuntimeException(e);
1027
            }
1028
        } catch (BaseException ex) {
1029
            throw new RuntimeException(ex);
1030
        }
1031
    }
1032

    
1033
    @Override
1034
    public Iterator<Feature> iterator() {
1035
        try {
1036
            return getFeatureSet(false).fastIterator();
1037
        } catch (Exception ex) {
1038
            throw new RuntimeException(ex);
1039
        }
1040
    }
1041

    
1042
    @Override
1043
    public String toString() {
1044
        try {
1045
            ToStringBuilder builder = new ToStringBuilder(this);
1046
            builder.append("evaluators", this.featureStore, true);
1047
            builder.append("query", this.query, true);
1048
            builder.append("selectionUp", this.selectionUp);
1049
            return builder.toString();
1050
        } catch (Exception e) {
1051
            return super.toString();
1052
        }
1053
    }
1054
}