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 | } |