svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.file / org.gvsig.fmap.dal.file.dbf / src / test / java / org / gvsig / fmap / dal / store / dbf / JoinTransform.java @ 40559
History | View | Annotate | Download (10.2 KB)
1 | 40559 | jjdelcerro | /**
|
---|---|---|---|
2 | * gvSIG. Desktop Geographic Information System.
|
||
3 | *
|
||
4 | * Copyright (C) 2007-2013 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 | 40435 | jjdelcerro | package org.gvsig.fmap.dal.store.dbf; |
25 | |||
26 | import java.util.ArrayList; |
||
27 | import java.util.Arrays; |
||
28 | import java.util.HashMap; |
||
29 | import java.util.Iterator; |
||
30 | import java.util.Map; |
||
31 | import java.util.Map.Entry; |
||
32 | |||
33 | import org.gvsig.fmap.dal.exception.DataException; |
||
34 | import org.gvsig.fmap.dal.feature.AbstractFeatureStoreTransform; |
||
35 | import org.gvsig.tools.dispose.DisposableIterator; |
||
36 | import org.gvsig.fmap.dal.feature.EditableFeature; |
||
37 | import org.gvsig.fmap.dal.feature.EditableFeatureType; |
||
38 | import org.gvsig.fmap.dal.feature.Feature; |
||
39 | import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor; |
||
40 | import org.gvsig.fmap.dal.feature.FeatureQuery; |
||
41 | import org.gvsig.fmap.dal.feature.FeatureSet; |
||
42 | import org.gvsig.fmap.dal.feature.FeatureStore; |
||
43 | import org.gvsig.fmap.dal.feature.FeatureType; |
||
44 | import org.gvsig.tools.evaluator.Evaluator; |
||
45 | import org.gvsig.tools.evaluator.EvaluatorData; |
||
46 | import org.gvsig.tools.evaluator.EvaluatorException; |
||
47 | import org.gvsig.tools.evaluator.EvaluatorFieldsInfo; |
||
48 | import org.gvsig.tools.persistence.PersistentState; |
||
49 | import org.gvsig.tools.persistence.exception.PersistenceException; |
||
50 | |||
51 | public class JoinTransform extends AbstractFeatureStoreTransform { |
||
52 | |||
53 | /**
|
||
54 | * Store from which the join transform will get the additional attributes
|
||
55 | */
|
||
56 | private FeatureStore store2;
|
||
57 | |||
58 | /**
|
||
59 | * name of the key attr in store1 that will be used to match features in
|
||
60 | * store2
|
||
61 | */
|
||
62 | private String keyAttr1; |
||
63 | |||
64 | /**
|
||
65 | * name of the key attr in store2 that will be used to match features in
|
||
66 | * store1
|
||
67 | */
|
||
68 | private String keyAttr2; |
||
69 | |||
70 | /**
|
||
71 | * names of the attributes to join from store2 to store1
|
||
72 | */
|
||
73 | private String[] attrs; |
||
74 | |||
75 | /**
|
||
76 | * Attribute names may change after transformation if they are repeated in
|
||
77 | * both stores. This map keeps correspondence between store2 original names
|
||
78 | * and their transformed counterparts.
|
||
79 | */
|
||
80 | private Map targetNamesMap; |
||
81 | |||
82 | private JoinTransformEvaluator evaluator = null; |
||
83 | |||
84 | private FeatureType originalFeatureType;
|
||
85 | |||
86 | private String[] attrsForQuery; |
||
87 | |||
88 | private String prefix1; |
||
89 | |||
90 | private String prefix2; |
||
91 | |||
92 | /**
|
||
93 | * A default constructor
|
||
94 | */
|
||
95 | public JoinTransform() {
|
||
96 | targetNamesMap = new HashMap(); |
||
97 | } |
||
98 | |||
99 | /**
|
||
100 | * Initializes all the necessary data for this transform
|
||
101 | *
|
||
102 | * @param store1
|
||
103 | * store whose default feature type is the target of this
|
||
104 | * transform
|
||
105 | *
|
||
106 | * @param store2
|
||
107 | * store whose default feature type will provide the new
|
||
108 | * attributes to join
|
||
109 | *
|
||
110 | * @param keyAttr1
|
||
111 | * key attribute in store1 that matches keyAttr2 in store2
|
||
112 | * (foreign key), used for joining both stores.
|
||
113 | *
|
||
114 | * @param keyAttr2
|
||
115 | * key attribute in store2 that matches keyAttr1 in store2
|
||
116 | * (foreign key), used for joining both stores.
|
||
117 | *
|
||
118 | * @param attrs
|
||
119 | * names of the attributes in store2 that will be joined to
|
||
120 | * store1.
|
||
121 | */
|
||
122 | public void initialize(FeatureStore store1, FeatureStore store2, |
||
123 | String keyAttr1, String keyAttr2, String prefix1, String prefix2, |
||
124 | String[] attrs) |
||
125 | throws DataException {
|
||
126 | |||
127 | if (store1 == store2) {
|
||
128 | throw new IllegalArgumentException("store1 == store2"); |
||
129 | } |
||
130 | |||
131 | // Initialize needed data
|
||
132 | this.setFeatureStore(store1);
|
||
133 | this.store2 = store2;
|
||
134 | this.keyAttr1 = keyAttr1;
|
||
135 | this.keyAttr2 = keyAttr2;
|
||
136 | this.prefix1 = prefix1; // TODO |
||
137 | this.prefix2 = prefix2; // TODO |
||
138 | this.attrs = attrs;
|
||
139 | |||
140 | // calculate this transform resulting feature type
|
||
141 | // by adding all specified attrs from store2 to store1's default
|
||
142 | // feature type
|
||
143 | // FIXME for more than one FTypes ??
|
||
144 | this.originalFeatureType = this.getFeatureStore() |
||
145 | .getDefaultFeatureType(); |
||
146 | |||
147 | // TODO tener en cuenta prefix1
|
||
148 | EditableFeatureType type = this.getFeatureStore().getDefaultFeatureType().getEditable();
|
||
149 | |||
150 | FeatureType type2 = store2.getDefaultFeatureType(); |
||
151 | |||
152 | // TODO tener en cuenta prefix2
|
||
153 | for (int i = 0; i < attrs.length; i++) { |
||
154 | String name = attrs[i];
|
||
155 | |||
156 | // If an attribute already exists with the same name in store1's
|
||
157 | // default feature type,
|
||
158 | // calculate an alternate name and add it to our type
|
||
159 | int j = 0; |
||
160 | while (type.getIndex(name) >= 0) { |
||
161 | name = attrs[i] + "_" + ++j;
|
||
162 | } |
||
163 | type.add(name, |
||
164 | type2.getAttributeDescriptor(attrs[i]).getType()); |
||
165 | |||
166 | // keep correspondence between original name and transformed name
|
||
167 | this.targetNamesMap.put(attrs[i], name);
|
||
168 | } |
||
169 | if (this.targetNamesMap.containsKey(keyAttr2)) { |
||
170 | this.attrsForQuery = this.attrs; |
||
171 | } else {
|
||
172 | ArrayList list = new ArrayList(this.attrs.length + 1); |
||
173 | list.addAll(Arrays.asList(this.attrs)); |
||
174 | list.add(keyAttr2); |
||
175 | this.attrsForQuery = (String[]) list.toArray(new String[] {}); |
||
176 | } |
||
177 | |||
178 | // assign calculated feature type as this transform's feature type
|
||
179 | FeatureType[] types = new FeatureType[] { type.getNotEditableCopy() }; |
||
180 | setFeatureTypes(Arrays.asList(types), types[0]); |
||
181 | } |
||
182 | |||
183 | /**
|
||
184 | *
|
||
185 | *
|
||
186 | * @param source
|
||
187 | *
|
||
188 | * @param target
|
||
189 | *
|
||
190 | * @throws DataException
|
||
191 | */
|
||
192 | public void applyTransform(Feature source, EditableFeature target) |
||
193 | throws DataException {
|
||
194 | |||
195 | // copy the data from store1 into the resulting feature
|
||
196 | this.copySourceToTarget(source, target);
|
||
197 | |||
198 | // ask store2 for the specified attributes, filtering by the key
|
||
199 | // attribute value
|
||
200 | // from the source feature
|
||
201 | JoinTransformEvaluator eval = this.getEvaluator();
|
||
202 | eval.updateValue(source.get(this.keyAttr1));
|
||
203 | |||
204 | FeatureQuery query = this.getFeatureStore().createFeatureQuery();
|
||
205 | query.setAttributeNames(attrsForQuery); |
||
206 | query.setFilter(eval); |
||
207 | |||
208 | FeatureSet set = store2.getFeatureSet(query); |
||
209 | |||
210 | // In this join implementation, we will take only the first matching
|
||
211 | // feature found in store2
|
||
212 | |||
213 | FeatureAttributeDescriptor attr; |
||
214 | Feature feat; |
||
215 | String targetName;
|
||
216 | |||
217 | Iterator itAttr;
|
||
218 | |||
219 | DisposableIterator itFeat = set.iterator(); |
||
220 | if (itFeat.hasNext()) {
|
||
221 | feat = (Feature) itFeat.next(); |
||
222 | |||
223 | // copy all attributes from joined feature to target
|
||
224 | this.copyJoinToTarget(feat, target);
|
||
225 | } |
||
226 | itFeat.dispose(); |
||
227 | set.dispose(); |
||
228 | } |
||
229 | |||
230 | /**
|
||
231 | * @param feat
|
||
232 | * @param target
|
||
233 | */
|
||
234 | private void copyJoinToTarget(Feature join, EditableFeature target) { |
||
235 | Iterator iter = targetNamesMap.entrySet()
|
||
236 | .iterator(); |
||
237 | Entry entry; |
||
238 | FeatureType trgType = target.getType(); |
||
239 | FeatureAttributeDescriptor attr; |
||
240 | while (iter.hasNext()) {
|
||
241 | entry = (Entry) iter.next(); |
||
242 | attr = trgType.getAttributeDescriptor((String) entry.getValue());
|
||
243 | if (attr != null) { |
||
244 | target.set(attr.getIndex(), join.get((String) entry.getKey()));
|
||
245 | } |
||
246 | } |
||
247 | |||
248 | |||
249 | } |
||
250 | |||
251 | /**
|
||
252 | * @param source
|
||
253 | * @param target
|
||
254 | */
|
||
255 | private void copySourceToTarget(Feature source, EditableFeature target) { |
||
256 | FeatureAttributeDescriptor attr, attrTrg; |
||
257 | FeatureType ftSrc = source.getType(); |
||
258 | FeatureType ftTrg = target.getType(); |
||
259 | |||
260 | |||
261 | for (int i = 0; i < source.getType().size(); i++) { |
||
262 | attr = ftSrc.getAttributeDescriptor(i); |
||
263 | if (ftTrg.getIndex(attr.getName()) > -1) { |
||
264 | try {
|
||
265 | target.set(attr.getName(), source.get(i)); |
||
266 | } catch (IllegalArgumentException e) { |
||
267 | attrTrg = ftTrg.getAttributeDescriptor(attr.getName()); |
||
268 | target.set(attrTrg.getIndex(), attrTrg.getDefaultValue()); |
||
269 | } |
||
270 | |||
271 | } |
||
272 | } |
||
273 | |||
274 | } |
||
275 | |||
276 | private JoinTransformEvaluator getEvaluator() {
|
||
277 | if (this.evaluator == null){ |
||
278 | this.evaluator = new JoinTransformEvaluator(keyAttr2); |
||
279 | } |
||
280 | return evaluator;
|
||
281 | |||
282 | } |
||
283 | |||
284 | private class JoinTransformEvaluator implements Evaluator { |
||
285 | |||
286 | private String attribute; |
||
287 | private Object value; |
||
288 | private String sql; |
||
289 | private EvaluatorFieldsInfo info = null; |
||
290 | |||
291 | // private int attributeIndex;
|
||
292 | |||
293 | public JoinTransformEvaluator(String attribute) { |
||
294 | this.attribute = attribute;
|
||
295 | this.value = null; |
||
296 | this.info = new EvaluatorFieldsInfo(); |
||
297 | |||
298 | // this.attributeIndex = attrIndex;
|
||
299 | } |
||
300 | |||
301 | public void updateValue(Object value) { |
||
302 | this.value = value;
|
||
303 | this.sql = this.attribute + "= '" + this.value + "'"; |
||
304 | this.info = new EvaluatorFieldsInfo(); |
||
305 | this.info.addMatchFieldValue(this.attribute, value); |
||
306 | } |
||
307 | |||
308 | public Object evaluate(EvaluatorData arg0) throws EvaluatorException { |
||
309 | Object curValue = arg0.getDataValue(attribute);
|
||
310 | if (curValue == null) { |
||
311 | return new Boolean(value == null); |
||
312 | } |
||
313 | return new Boolean(curValue.equals(value)); |
||
314 | } |
||
315 | |||
316 | public String getSQL() { |
||
317 | return this.sql; |
||
318 | } |
||
319 | |||
320 | public String getDescription() { |
||
321 | return "Evaluates join transform match"; |
||
322 | } |
||
323 | |||
324 | public String getName() { |
||
325 | return "JoinTransformEvaluator"; |
||
326 | } |
||
327 | |||
328 | public EvaluatorFieldsInfo getFieldsInfo() {
|
||
329 | return this.info; |
||
330 | } |
||
331 | |||
332 | } |
||
333 | |||
334 | public void saveToState(PersistentState state) throws PersistenceException { |
||
335 | // TODO Auto-generated method stub
|
||
336 | |||
337 | } |
||
338 | |||
339 | public void loadFromState(PersistentState state) throws PersistenceException { |
||
340 | // TODO Auto-generated method stub
|
||
341 | |||
342 | } |
||
343 | |||
344 | public FeatureType getSourceFeatureTypeFrom(FeatureType arg0) {
|
||
345 | EditableFeatureType orgType = originalFeatureType.getEditable(); |
||
346 | Iterator iter = arg0.iterator();
|
||
347 | FeatureAttributeDescriptor attr; |
||
348 | ArrayList toRetain = new ArrayList(); |
||
349 | while (iter.hasNext()) {
|
||
350 | attr = (FeatureAttributeDescriptor) iter.next(); |
||
351 | if (this.targetNamesMap.containsValue(attr.getName())) { |
||
352 | continue;
|
||
353 | } |
||
354 | toRetain.add(attr.getName()); |
||
355 | } |
||
356 | |||
357 | if (!toRetain.contains(keyAttr1)) {
|
||
358 | toRetain.add(keyAttr1); |
||
359 | } |
||
360 | |||
361 | iter = originalFeatureType.iterator(); |
||
362 | while (iter.hasNext()) {
|
||
363 | attr = (FeatureAttributeDescriptor) iter.next(); |
||
364 | if (!toRetain.contains(attr.getName())) {
|
||
365 | orgType.remove(attr.getName()); |
||
366 | } |
||
367 | |||
368 | } |
||
369 | |||
370 | return orgType.getNotEditableCopy();
|
||
371 | } |
||
372 | |||
373 | public boolean isTransformsOriginalValues() { |
||
374 | return false; |
||
375 | } |
||
376 | } |