root / branches / v2_0_0_prep / libraries / libFMap_dal / src / org / gvsig / fmap / dal / feature / paging / impl / FeaturePagingHelperImpl.java @ 37297
History | View | Annotate | Download (13.6 KB)
1 | 23675 | cordinyana | /* gvSIG. Geographic Information System of the Valencian Government
|
---|---|---|---|
2 | *
|
||
3 | * Copyright (C) 2007-2008 Infrastructures and Transports Department
|
||
4 | 31544 | cordinyana | * of the Valencian Government (CIT)
|
5 | 23772 | jjdelcerro | *
|
6 | 23675 | cordinyana | * 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 | 23772 | jjdelcerro | *
|
11 | 23675 | cordinyana | * 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 | 23772 | jjdelcerro | *
|
16 | 23675 | cordinyana | * 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 | 23772 | jjdelcerro | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 | 23675 | cordinyana | * MA 02110-1301, USA.
|
20 | 23772 | jjdelcerro | *
|
21 | 23675 | cordinyana | */
|
22 | |||
23 | /*
|
||
24 | * AUTHORS (In addition to CIT):
|
||
25 | * 2008 {DiSiD Technologies} {Create a JTable TableModel for a FeatureCollection}
|
||
26 | */
|
||
27 | 33343 | cordinyana | package org.gvsig.fmap.dal.feature.paging.impl; |
28 | 23675 | cordinyana | |
29 | 33385 | cordinyana | import org.slf4j.Logger; |
30 | import org.slf4j.LoggerFactory; |
||
31 | |||
32 | 24505 | jmvivo | import org.gvsig.fmap.dal.exception.DataException; |
33 | 27234 | jmvivo | import org.gvsig.fmap.dal.feature.EditableFeature; |
34 | import org.gvsig.fmap.dal.feature.Feature; |
||
35 | import org.gvsig.fmap.dal.feature.FeatureQuery; |
||
36 | 33378 | cordinyana | import org.gvsig.fmap.dal.feature.FeatureSelection; |
37 | 27234 | jmvivo | import org.gvsig.fmap.dal.feature.FeatureSet; |
38 | import org.gvsig.fmap.dal.feature.FeatureStore; |
||
39 | import org.gvsig.fmap.dal.feature.FeatureType; |
||
40 | 33657 | cordinyana | import org.gvsig.fmap.dal.feature.impl.featureset.DynObjectFeatureFacade; |
41 | 33343 | cordinyana | import org.gvsig.fmap.dal.feature.paging.FeaturePagingHelper; |
42 | 33205 | cordinyana | import org.gvsig.tools.dynobject.DynObject; |
43 | 33385 | cordinyana | import org.gvsig.tools.dynobject.impl.DefaultDynObjectPagingHelper; |
44 | 31110 | cordinyana | import org.gvsig.tools.exception.BaseException; |
45 | import org.gvsig.tools.visitor.VisitCanceledException; |
||
46 | import org.gvsig.tools.visitor.Visitor; |
||
47 | 23675 | cordinyana | |
48 | /**
|
||
49 | * Helper class to access the values of a FeatureCollection by position. Handles
|
||
50 | * pagination automatically to avoid filling the memory in case of big
|
||
51 | * collections.
|
||
52 | 31110 | cordinyana | *
|
53 | 23930 | cordinyana | * TODO: evaluate if its more convenient to read values in the background when
|
54 | * the returned value is near the end of the page, instead of loading a page on
|
||
55 | * demand.
|
||
56 | 31110 | cordinyana | *
|
57 | 33385 | cordinyana | * @author gvSIG Team
|
58 | 23675 | cordinyana | */
|
59 | 33385 | cordinyana | public class FeaturePagingHelperImpl extends DefaultDynObjectPagingHelper |
60 | implements FeaturePagingHelper {
|
||
61 | 23675 | cordinyana | |
62 | 33385 | cordinyana | private static final Logger LOG = LoggerFactory |
63 | .getLogger(FeaturePagingHelperImpl.class); |
||
64 | 23930 | cordinyana | |
65 | 33385 | cordinyana | private FeatureQuery query;
|
66 | 23675 | cordinyana | |
67 | 33385 | cordinyana | private FeatureSet featureSet;
|
68 | 23772 | jjdelcerro | |
69 | 33385 | cordinyana | private FeatureStore featureStore;
|
70 | 23675 | cordinyana | |
71 | 33385 | cordinyana | /** If the selected Features must be returned as the first ones. **/
|
72 | private boolean selectionUp = false; |
||
73 | 23675 | cordinyana | |
74 | 33385 | cordinyana | private FeatureSelection initialSelection = null; |
75 | 23675 | cordinyana | |
76 | 33385 | cordinyana | private Feature[] features = null; |
77 | 23675 | cordinyana | |
78 | 33385 | cordinyana | /**
|
79 | * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
|
||
80 | *
|
||
81 | * @param featureStore
|
||
82 | * to extract data from
|
||
83 | * @throws DataException
|
||
84 | * if there is an error initializing the helper
|
||
85 | */
|
||
86 | public FeaturePagingHelperImpl(FeatureStore featureStore)
|
||
87 | throws BaseException {
|
||
88 | this(featureStore, DEFAULT_PAGE_SIZE);
|
||
89 | } |
||
90 | 23675 | cordinyana | |
91 | 33385 | cordinyana | /**
|
92 | * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
|
||
93 | *
|
||
94 | * @param featureStore
|
||
95 | * to extract data from
|
||
96 | * @param pageSize
|
||
97 | * the number of elements per page data
|
||
98 | * @throws DataException
|
||
99 | * if there is an error initializing the helper
|
||
100 | */
|
||
101 | public FeaturePagingHelperImpl(FeatureStore featureStore, int pageSize) |
||
102 | throws BaseException {
|
||
103 | this(featureStore, null, pageSize); |
||
104 | } |
||
105 | 23675 | cordinyana | |
106 | 33385 | cordinyana | /**
|
107 | * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
|
||
108 | *
|
||
109 | * @param featureStore
|
||
110 | * to extract data from
|
||
111 | * @throws DataException
|
||
112 | * if there is an error initializing the helper
|
||
113 | */
|
||
114 | public FeaturePagingHelperImpl(FeatureStore featureStore,
|
||
115 | FeatureQuery featureQuery) throws BaseException {
|
||
116 | this(featureStore, featureQuery, DEFAULT_PAGE_SIZE);
|
||
117 | } |
||
118 | 23675 | cordinyana | |
119 | 33385 | cordinyana | /**
|
120 | * Constructs a FeaturePagingHelperImpl from data of a FeatureStore.
|
||
121 | *
|
||
122 | * @param featureSet
|
||
123 | * to extract data from
|
||
124 | * @param pageSize
|
||
125 | * the number of elements per page data
|
||
126 | * @throws DataException
|
||
127 | * if there is an error initializing the helper
|
||
128 | */
|
||
129 | public FeaturePagingHelperImpl(FeatureStore featureStore,
|
||
130 | FeatureQuery featureQuery, int pageSize) throws BaseException { |
||
131 | super();
|
||
132 | FeatureQuery query = featureQuery; |
||
133 | if (featureQuery == null) { |
||
134 | query = featureStore.createFeatureQuery(); |
||
135 | query.setFeatureType(featureStore.getDefaultFeatureType()); |
||
136 | } |
||
137 | 23675 | cordinyana | |
138 | 33385 | cordinyana | this.featureStore = featureStore;
|
139 | this.query = query;
|
||
140 | this.query.setPageSize(pageSize);
|
||
141 | loadFeatureSet(); |
||
142 | 33378 | cordinyana | |
143 | 33385 | cordinyana | 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 | 24023 | cordinyana | |
150 | 33385 | cordinyana | /**
|
151 | * @return the selectionUp status
|
||
152 | */
|
||
153 | protected boolean isSelectionUp() { |
||
154 | return selectionUp;
|
||
155 | } |
||
156 | 33378 | cordinyana | |
157 | 33385 | cordinyana | public void setSelectionUp(boolean selectionUp) { |
158 | this.selectionUp = selectionUp;
|
||
159 | try {
|
||
160 | if (selectionUp) {
|
||
161 | initialSelection = |
||
162 | (FeatureSelection) getFeatureStore().getFeatureSelection() |
||
163 | .clone(); |
||
164 | setCalculator(new OneSubsetOneSetPagingCalculator(
|
||
165 | new FeatureSetSizeableDelegate(initialSelection),
|
||
166 | new FeatureSetSizeableDelegate(getFeatureSet()),
|
||
167 | getMaxPageSize())); |
||
168 | } else {
|
||
169 | if (initialSelection != null) { |
||
170 | initialSelection.dispose(); |
||
171 | initialSelection = null;
|
||
172 | } |
||
173 | setDefaultCalculator(new FeatureSetSizeableDelegate(
|
||
174 | 33657 | cordinyana | getFeatureSet()), getMaxPageSize()); |
175 | 33385 | cordinyana | } |
176 | } catch (BaseException e) {
|
||
177 | LOG.error("Error setting the selection up setting to: "
|
||
178 | + selectionUp, e); |
||
179 | } catch (CloneNotSupportedException e) { |
||
180 | LOG.error("Error cloning the selection "
|
||
181 | + "while setting the selection up", e);
|
||
182 | } |
||
183 | } |
||
184 | 23675 | cordinyana | |
185 | 33385 | cordinyana | public Feature getFeatureAt(long index) throws BaseException { |
186 | // Check if we have currently loaded the viewed page data,
|
||
187 | // or we need to load a new one
|
||
188 | long pageForIndex = (long) Math.floor(index / getMaxPageSize()); |
||
189 | 33378 | cordinyana | |
190 | 33385 | cordinyana | if (pageForIndex != getCurrentPage()) {
|
191 | setCurrentPage(pageForIndex); |
||
192 | } |
||
193 | 33378 | cordinyana | |
194 | 33385 | cordinyana | long positionForIndex = index - (getCurrentPage() * getMaxPageSize());
|
195 | 23675 | cordinyana | |
196 | 33385 | cordinyana | return features[(int) positionForIndex]; |
197 | } |
||
198 | 23675 | cordinyana | |
199 | 33385 | cordinyana | public Feature[] getCurrentPageFeatures() { |
200 | return features;
|
||
201 | } |
||
202 | 23675 | cordinyana | |
203 | 33385 | cordinyana | public FeatureSet getFeatureSet() {
|
204 | return featureSet;
|
||
205 | } |
||
206 | 23675 | cordinyana | |
207 | 33385 | cordinyana | public void reloadCurrentPage() throws BaseException { |
208 | setSelectionUp(false);
|
||
209 | if (getCalculator().getCurrentPage() > -1) { |
||
210 | loadCurrentPageData(); |
||
211 | } |
||
212 | } |
||
213 | 23675 | cordinyana | |
214 | 33385 | cordinyana | public void reload() throws BaseException { |
215 | loadFeatureSet(); |
||
216 | reloadCurrentPage(); |
||
217 | } |
||
218 | 23675 | cordinyana | |
219 | 33385 | cordinyana | public FeatureStore getFeatureStore() {
|
220 | return featureStore;
|
||
221 | } |
||
222 | 23675 | cordinyana | |
223 | 33385 | cordinyana | public FeatureQuery getFeatureQuery() {
|
224 | return query;
|
||
225 | } |
||
226 | 23772 | jjdelcerro | |
227 | 33385 | cordinyana | /**
|
228 | * Loads all the Features of the current page.
|
||
229 | */
|
||
230 | protected void loadCurrentPageData() throws BaseException { |
||
231 | final int currentPageSize = getCalculator().getCurrentPageSize(); |
||
232 | final Feature[] values = new Feature[currentPageSize]; |
||
233 | 23772 | jjdelcerro | |
234 | 33385 | cordinyana | long t1 = 0; |
235 | if (LOG.isTraceEnabled()) {
|
||
236 | t1 = System.currentTimeMillis();
|
||
237 | } |
||
238 | 23772 | jjdelcerro | |
239 | 33385 | cordinyana | if (selectionUp) {
|
240 | loadCurrentPageDataWithSelectionUp(values); |
||
241 | } else {
|
||
242 | loadCurrentPageDataNoSelection(values); |
||
243 | } |
||
244 | 23675 | cordinyana | |
245 | 33385 | cordinyana | if (LOG.isTraceEnabled()) {
|
246 | long t2 = System.currentTimeMillis(); |
||
247 | LOG.trace("Time to load {} features: {} ms", new Integer( |
||
248 | currentPageSize), new Long(t2 - t1)); |
||
249 | } |
||
250 | 23772 | jjdelcerro | |
251 | 33385 | cordinyana | this.features = values;
|
252 | } |
||
253 | 23772 | jjdelcerro | |
254 | 33385 | cordinyana | private void loadCurrentPageDataWithSelectionUp(final Feature[] values) |
255 | throws BaseException {
|
||
256 | FeatureSelection selection = initialSelection; |
||
257 | 23675 | cordinyana | |
258 | 33385 | cordinyana | OneSubsetOneSetPagingCalculator twoSetsCalculator = null;
|
259 | if (getCalculator() instanceof OneSubsetOneSetPagingCalculator) { |
||
260 | twoSetsCalculator = |
||
261 | (OneSubsetOneSetPagingCalculator) getCalculator(); |
||
262 | } else {
|
||
263 | twoSetsCalculator = |
||
264 | new OneSubsetOneSetPagingCalculator(
|
||
265 | new FeatureSetSizeableDelegate(selection),
|
||
266 | new FeatureSetSizeableDelegate(getFeatureSet()),
|
||
267 | getMaxPageSize(), getCalculator().getCurrentPage()); |
||
268 | setCalculator(twoSetsCalculator); |
||
269 | } |
||
270 | 23675 | cordinyana | |
271 | 33385 | cordinyana | // First load values from the selection, if the current page has
|
272 | // elements from it
|
||
273 | if (twoSetsCalculator.hasCurrentPageAnyValuesInFirstSet()) {
|
||
274 | loadDataFromFeatureSet(values, 0, selection,
|
||
275 | twoSetsCalculator.getFirstSetInitialIndex(), |
||
276 | twoSetsCalculator.getFirstSetHowMany(), null);
|
||
277 | } |
||
278 | // Next, load values from the FeatureSet if the current page has values
|
||
279 | // from it
|
||
280 | if (twoSetsCalculator.hasCurrentPageAnyValuesInSecondSet()) {
|
||
281 | loadDataFromFeatureSet( |
||
282 | values, |
||
283 | // The cast will work as that size will be <= maxpagesize,
|
||
284 | // which is an int
|
||
285 | (int) twoSetsCalculator.getFirstSetHowMany(), getFeatureSet(),
|
||
286 | twoSetsCalculator.getSecondSetInitialIndex(), |
||
287 | twoSetsCalculator.getSecondSetHowMany(), selection); |
||
288 | } |
||
289 | } |
||
290 | 23714 | cordinyana | |
291 | 33385 | cordinyana | private void loadCurrentPageDataNoSelection(final Feature[] values) |
292 | throws DataException {
|
||
293 | 33378 | cordinyana | |
294 | 33385 | cordinyana | long firstPosition = getCalculator().getInitialIndex();
|
295 | 23675 | cordinyana | |
296 | 33385 | cordinyana | if (LOG.isDebugEnabled()) {
|
297 | LOG.debug("Loading {} Features starting at position {}",
|
||
298 | new Integer(getCalculator().getCurrentPageSize()), new Long( |
||
299 | firstPosition)); |
||
300 | } |
||
301 | 23675 | cordinyana | |
302 | 33385 | cordinyana | loadDataFromFeatureSet(values, 0, getFeatureSet(), firstPosition,
|
303 | getCalculator().getCurrentPageSize(), null);
|
||
304 | } |
||
305 | 23675 | cordinyana | |
306 | 33385 | cordinyana | private void loadDataFromFeatureSet(final Feature[] values, |
307 | final int valuesPosition, FeatureSet set, long initialIndex, |
||
308 | final long howMany, final FeatureSelection selectedFeaturesToSkip) |
||
309 | throws DataException {
|
||
310 | 24023 | cordinyana | |
311 | 33385 | cordinyana | try {
|
312 | set.accept(new Visitor() {
|
||
313 | 33378 | cordinyana | |
314 | 33385 | cordinyana | private int i = valuesPosition; |
315 | 33378 | cordinyana | |
316 | 33385 | cordinyana | public void visit(Object obj) throws VisitCanceledException, |
317 | BaseException { |
||
318 | if (i >= valuesPosition + howMany) {
|
||
319 | throw new VisitCanceledException(); |
||
320 | } |
||
321 | Feature current = (Feature) obj; |
||
322 | // Add the current Feature only if we don't skip selected
|
||
323 | // features or the feature is not selected
|
||
324 | if (selectedFeaturesToSkip == null |
||
325 | || !selectedFeaturesToSkip.isSelected(current)) { |
||
326 | values[i] = current.getCopy(); |
||
327 | i++; |
||
328 | } |
||
329 | } |
||
330 | }, initialIndex); |
||
331 | } catch (BaseException e) {
|
||
332 | if (e instanceof DataException) { |
||
333 | throw ((DataException) e);
|
||
334 | } else {
|
||
335 | LOG.error("Error loading the data starting at position {}",
|
||
336 | new Long(initialIndex), e); |
||
337 | } |
||
338 | } |
||
339 | } |
||
340 | 33378 | cordinyana | |
341 | 33385 | cordinyana | private void loadFeatureSet() throws BaseException { |
342 | if (featureSet != null) { |
||
343 | featureSet.dispose(); |
||
344 | } |
||
345 | featureSet = getFeatureStore().getFeatureSet(getFeatureQuery()); |
||
346 | 33657 | cordinyana | setDynObjectSet(featureSet.getDynObjectSet(), getMaxPageSize()); |
347 | 33385 | cordinyana | } |
348 | 33378 | cordinyana | |
349 | 33385 | cordinyana | public void delete(Feature feature) throws BaseException { |
350 | if (featureSet == null) { |
||
351 | // FIXME change to RuntimeDalException
|
||
352 | throw new IllegalStateException(); |
||
353 | } |
||
354 | featureSet.delete(feature); |
||
355 | reloadCurrentPage(); |
||
356 | } |
||
357 | 33378 | cordinyana | |
358 | 33385 | cordinyana | public void insert(EditableFeature feature) throws BaseException { |
359 | if (featureSet == null) { |
||
360 | // FIXME change to RuntimeDalException
|
||
361 | throw new IllegalStateException(); |
||
362 | } |
||
363 | featureSet.insert(feature); |
||
364 | reloadCurrentPage(); |
||
365 | } |
||
366 | 23772 | jjdelcerro | |
367 | 33385 | cordinyana | public void update(EditableFeature feature) throws BaseException { |
368 | if (featureSet == null) { |
||
369 | // FIXME change to RuntimeDalException
|
||
370 | throw new IllegalStateException(); |
||
371 | } |
||
372 | featureSet.update(feature); |
||
373 | reloadCurrentPage(); |
||
374 | } |
||
375 | 33378 | cordinyana | |
376 | 33385 | cordinyana | public FeatureType getFeatureType() {
|
377 | return featureSet.getDefaultFeatureType();
|
||
378 | } |
||
379 | 33378 | cordinyana | |
380 | 33385 | cordinyana | protected void doDispose() throws BaseException { |
381 | initialSelection.dispose(); |
||
382 | featureSet.dispose(); |
||
383 | } |
||
384 | 33378 | cordinyana | |
385 | 33385 | cordinyana | public DynObject[] getCurrentPageDynObjects() { |
386 | 33657 | cordinyana | Feature[] features = getCurrentPageFeatures();
|
387 | DynObject[] dynobjects = new DynObject[features.length]; |
||
388 | for (int i = 0; i < dynobjects.length; i++) { |
||
389 | dynobjects[i] = new DynObjectFeatureFacade(features[i]);
|
||
390 | } |
||
391 | return dynobjects;
|
||
392 | 33385 | cordinyana | } |
393 | 31110 | cordinyana | |
394 | 33385 | cordinyana | public DynObject getDynObjectAt(long index) throws BaseException { |
395 | 33657 | cordinyana | return new DynObjectFeatureFacade(getFeatureAt(index)); |
396 | 33385 | cordinyana | } |
397 | 31110 | cordinyana | |
398 | 33385 | cordinyana | } |