Statistics
| Revision:

root / branches / v2_0_0_prep / extensions / extDalTransformJoin / src / org / gvsig / app / join / dal / feature / JoinTransform.java @ 32880

History | View | Annotate | Download (11.2 KB)

1
package org.gvsig.app.join.dal.feature;
2

    
3
import java.util.ArrayList;
4
import java.util.Arrays;
5
import java.util.HashMap;
6
import java.util.Iterator;
7
import java.util.List;
8
import java.util.Map;
9
import java.util.Map.Entry;
10

    
11
import org.gvsig.fmap.dal.DataTypes;
12
import org.gvsig.fmap.dal.exception.DataException;
13
import org.gvsig.fmap.dal.feature.AbstractFeatureStoreTransform;
14
import org.gvsig.fmap.dal.feature.DisposableIterator;
15
import org.gvsig.fmap.dal.feature.EditableFeature;
16
import org.gvsig.fmap.dal.feature.EditableFeatureType;
17
import org.gvsig.fmap.dal.feature.Feature;
18
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
19
import org.gvsig.fmap.dal.feature.FeatureQuery;
20
import org.gvsig.fmap.dal.feature.FeatureSet;
21
import org.gvsig.fmap.dal.feature.FeatureStore;
22
import org.gvsig.fmap.dal.feature.FeatureType;
23
import org.gvsig.tools.ToolsLocator;
24
import org.gvsig.tools.dynobject.DynStruct;
25
import org.gvsig.tools.evaluator.Evaluator;
26
import org.gvsig.tools.evaluator.EvaluatorData;
27
import org.gvsig.tools.evaluator.EvaluatorException;
28
import org.gvsig.tools.evaluator.EvaluatorFieldsInfo;
29
import org.gvsig.tools.persistence.PersistenceManager;
30
import org.gvsig.tools.persistence.PersistentState;
31
import org.gvsig.tools.persistence.exception.PersistenceException;
32

    
33
public class JoinTransform extends AbstractFeatureStoreTransform {
34

    
35
        /**
36
         * Store from which the join transform will get the additional attributes
37
         */
38
        private FeatureStore store2;
39

    
40
        /**
41
         * name of the key attr in store1 that will be used to match features in
42
         * store2
43
         */
44
        private String keyAttr1;
45

    
46
        /**
47
         * name of the key attr in store2 that will be used to match features in
48
         * store1
49
         */
50
        private String keyAttr2;
51

    
52
        /**
53
         * names of the attributes to join from store2 to store1
54
         */
55
        private String[] attrs;
56

    
57
        /**
58
         * Attribute names may change after transformation if they are repeated in
59
         * both stores. This map keeps correspondence between store2 original names
60
         * and their transformed counterparts.
61
         */
62
        private Map<String,String> targetNamesMap;
63

    
64
        private JoinTransformEvaluator evaluator = null;
65

    
66
        private FeatureType originalFeatureType;
67

    
68
        private String[] attrsForQuery;
69

    
70
        private String prefix1;
71

    
72
        private String prefix2;
73

    
74
        /**
75
         * A default constructor
76
         */
77
        public JoinTransform() {
78
                targetNamesMap = new HashMap<String,String>();
79
        }
80

    
81
        /**
82
         * Initializes all the necessary data for this transform
83
         *
84
         * @param store1
85
         *            store whose default feature type is the target of this
86
         *            transform
87
         *
88
         * @param store2
89
         *            store whose default feature type will provide the new
90
         *            attributes to join
91
         *
92
         * @param keyAttr1
93
         *            key attribute in store1 that matches keyAttr2 in store2
94
         *            (foreign key), used for joining both stores.
95
         *
96
         * @param keyAttr2
97
         *            key attribute in store2 that matches keyAttr1 in store2
98
         *            (foreign key), used for joining both stores.
99
         *
100
         * @param attrs
101
         *            names of the attributes in store2 that will be joined to
102
         *            store1.
103
         */
104
        public void initialize(FeatureStore store1, FeatureStore store2,
105
                        String keyAttr1, String keyAttr2, String prefix1, String prefix2,
106
                        String[] attrs)
107
        throws DataException {
108

    
109
                if (store1 == store2) {
110
                        throw new IllegalArgumentException("store1 == store2");
111
                }
112

    
113
                // Initialize needed data
114
                this.setFeatureStore(store1);
115
                this.store2 = store2;
116
                this.keyAttr1 = keyAttr1;
117
                this.keyAttr2 = keyAttr2;
118
                this.prefix1 = prefix1; // TODO
119
                this.prefix2 = prefix2; // TODO
120
                this.attrs = attrs;
121

    
122
                // calculate this transform resulting feature type
123
                // by adding all specified attrs from store2 to store1's default
124
                // feature type
125
                // FIXME for more than one FTypes ??
126
                this.originalFeatureType = this.getFeatureStore()
127
                .getDefaultFeatureType();
128

    
129
                // TODO tener en cuenta prefix1
130
                EditableFeatureType type = this.getFeatureStore().getDefaultFeatureType().getEditable();
131

    
132
                FeatureType type2 = store2.getDefaultFeatureType();
133

    
134
                // TODO tener en cuenta prefix2
135
                for (int i = 0; i < attrs.length; i++) {
136
                        String name = attrs[i];
137

    
138
                        // If an attribute already exists with the same name in store1's
139
                        // default feature type,
140
                        // calculate an alternate name and add it to our type
141
                        int j = 0;
142
                        while (type.getIndex(name) >= 0) {
143
                                name = attrs[i] + "_" + ++j;
144
                        }
145
                        type.add(name,
146
                                        type2.getAttributeDescriptor(attrs[i]).getDataType());
147

    
148
                        // keep correspondence between original name and transformed name
149
                        this.targetNamesMap.put(attrs[i], name);
150
                }
151
                if (this.targetNamesMap.containsKey(keyAttr2)) {
152
                        this.attrsForQuery = this.attrs;
153
                } else {
154
                        List<String> list = new ArrayList<String>(this.attrs.length + 1);
155
                        list.addAll(Arrays.asList(this.attrs));
156
                        list.add(keyAttr2);
157
                        this.attrsForQuery = (String[]) list.toArray(new String[] {});
158
                }
159

    
160
                // assign calculated feature type as this transform's feature type
161
                FeatureType[] types = new FeatureType[] { type.getNotEditableCopy() };
162
                setFeatureTypes(Arrays.asList(types), types[0]);
163
        }
164

    
165
        /**
166
         *
167
         *
168
         * @param source
169
         *
170
         * @param target
171
         *
172
         * @throws DataException
173
         */
174
        public void applyTransform(Feature source, EditableFeature target)
175
        throws DataException {
176

    
177
                // copy the data from store1 into the resulting feature
178
                this.copySourceToTarget(source, target);
179

    
180
                // ask store2 for the specified attributes, filtering by the key
181
                // attribute value
182
                // from the source feature
183
                JoinTransformEvaluator eval = this.getEvaluator();
184
                eval.updateValue(source.get(this.keyAttr1));
185

    
186
                FeatureQuery query = store2.createFeatureQuery();
187
                query.setAttributeNames(attrsForQuery);
188
                query.setFilter(eval);
189
                FeatureSet set = null;
190
                DisposableIterator itFeat = null;
191

    
192
                try {
193

    
194
                        set = store2.getFeatureSet(query);
195
                        // In this join implementation, we will take only the first matching
196
                        // feature found in store2
197

    
198
                        Feature feat;
199

    
200

    
201
                        itFeat = set.iterator();
202
                        if (itFeat.hasNext()) {
203
                                feat = (Feature) itFeat.next();
204

    
205
                                // copy all attributes from joined feature to target
206
                                this.copyJoinToTarget(feat, target);
207
                        }
208
                } finally {
209
                        if (itFeat != null) {
210
                                itFeat.dispose();
211
                        }
212
                        if (set != null) {
213
                                set.dispose();
214
                        }
215
                }
216
        }
217

    
218
        /**
219
         * @param feat
220
         * @param target
221
         */
222
        private void copyJoinToTarget(Feature join, EditableFeature target) {
223
                Iterator<Entry<String, String>> iter = targetNamesMap.entrySet()
224
                .iterator();
225
                Entry<String, String> entry;
226
                FeatureType trgType = target.getType();
227
                FeatureAttributeDescriptor attr;
228
                while (iter.hasNext()) {
229
                        entry = iter.next();
230
                        attr = trgType.getAttributeDescriptor((String) entry.getValue());
231
                        if (attr != null) {
232
                                target.set(attr.getIndex(), join.get((String) entry.getKey()));
233
                        }
234
                }
235

    
236

    
237
        }
238

    
239
        /**
240
         * @param source
241
         * @param target
242
         */
243
        private void copySourceToTarget(Feature source, EditableFeature target) {
244
                FeatureAttributeDescriptor attr, attrTrg;
245
                FeatureType ftSrc = source.getType();
246
                FeatureType ftTrg = target.getType();
247

    
248

    
249
                for (int i = 0; i < source.getType().size(); i++) {
250
                        attr = ftSrc.getAttributeDescriptor(i);
251
                        attrTrg = ftTrg.getAttributeDescriptor(attr.getName());
252
                        if (attrTrg != null) {
253
                                try {
254
                                        target.set(attrTrg.getIndex(), source.get(i));
255
                                } catch (IllegalArgumentException e) {
256
                                        attrTrg = ftTrg.getAttributeDescriptor(attr.getName());
257
                                        target.set(attrTrg.getIndex(), attrTrg.getDefaultValue());
258
                                }
259

    
260
                        }
261
                }
262

    
263
        }
264

    
265
        private JoinTransformEvaluator getEvaluator() {
266
                if (this.evaluator == null){
267
                        this.evaluator = new JoinTransformEvaluator(keyAttr2);
268
                }
269
                return evaluator;
270

    
271
        }
272

    
273
        private class JoinTransformEvaluator implements Evaluator {
274

    
275
                private String attribute;
276
                private Object value;
277
                private String cql;
278
                private EvaluatorFieldsInfo info = null;
279

    
280
                //                private int attributeIndex;
281

    
282
                public JoinTransformEvaluator(String attribute) {
283
                        this.attribute = attribute;
284
                        this.value = null;
285
                        this.info = new EvaluatorFieldsInfo();
286

    
287
                        //                        this.attributeIndex = attrIndex;
288
                }
289

    
290
                public void updateValue(Object value) {
291
                        this.value = value;
292
                        this.cql = this.attribute + "= '" + this.value + "'";
293
                        this.info = new EvaluatorFieldsInfo();
294
                        this.info.addMatchFieldValue(this.attribute, value);
295
                }
296

    
297
                public Object evaluate(EvaluatorData arg0) throws EvaluatorException {
298
                        Object curValue = arg0.getDataValue(attribute);
299
                        if (curValue == null) {
300
                                return value == null;
301
                        }
302
                        return curValue.equals(value);
303
                }
304

    
305
                public String getCQL() {
306
                        return this.cql;
307
                }
308

    
309
                public String getDescription() {
310
                        return "Evaluates join transform match";
311
                }
312

    
313
                public String getName() {
314
                        return "JoinTransformEvaluator";
315
                }
316

    
317
                public EvaluatorFieldsInfo getFieldsInfo() {
318
                        return this.info;
319
                }
320

    
321
        }        
322

    
323
        @SuppressWarnings("unchecked")
324
        public FeatureType getSourceFeatureTypeFrom(FeatureType arg0) {
325
                EditableFeatureType orgType = originalFeatureType.getEditable();
326
                
327
                Iterator<FeatureAttributeDescriptor> iter = arg0.iterator();
328
                FeatureAttributeDescriptor attr;
329
                List<String> toRetain = new ArrayList<String>();
330
                while (iter.hasNext()) {
331
                        attr = (FeatureAttributeDescriptor) iter.next();
332
                        if (this.targetNamesMap.containsValue(attr.getName())) {
333
                                continue;
334
                        }
335
                        toRetain.add(attr.getName());
336
                }
337

    
338
                if (!toRetain.contains(keyAttr1)) {
339
                        toRetain.add(keyAttr1);
340
                }
341

    
342
                iter = originalFeatureType.iterator();
343
                while (iter.hasNext()) {
344
                        attr = (FeatureAttributeDescriptor) iter.next();
345
                        if (!toRetain.contains(attr.getName())) {
346
                                orgType.remove(attr.getName());
347
                        }
348

    
349
                }
350

    
351
                return orgType.getNotEditableCopy();
352
        }
353

    
354
        public boolean isTransformsOriginalValues() {
355
                return false;
356
        }
357

    
358
        public static void registerPersistent() {
359
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
360
                DynStruct definition = manager.addDefinition(
361
                                JoinTransform.class,
362
                                "JoinTransform",
363
                                "JoinTransform Persistence definition",
364
                                null, 
365
                                null
366
                );
367
                definition.extend(ABSTRACT_FEATURESTORE_DYNCLASS_NAME);
368

    
369
                definition.addDynField("store2").setType(DataTypes.OBJECT);
370
                definition.addDynField("keyAttr1").setType(DataTypes.STRING);
371
                definition.addDynField("keyAttr2").setType(DataTypes.STRING);
372
                definition.addDynField("prefix1").setType(DataTypes.STRING);
373
                definition.addDynField("prefix2").setType(DataTypes.STRING);
374
                definition.addDynField("attrs").setType(DataTypes.LIST);
375
        }
376

    
377
        public void saveToState(PersistentState state) throws PersistenceException {
378
                super.saveToState(state);
379
                state.set("store2", this.store2);
380
                state.set("keyAttr1", this.keyAttr1);
381
                state.set("keyAttr2", this.keyAttr2);
382
                state.set("prefix1", this.prefix1);
383
                state.set("prefix2", this.prefix2);                
384
                state.set("attrs", Arrays.asList(this.attrs));
385
        }
386

    
387
        @SuppressWarnings("unchecked")
388
        public void loadFromState(PersistentState state) throws PersistenceException {
389
                super.loadFromState(state);
390
                FeatureStore store2 = (FeatureStore) state.get("store2");
391
                String keyAttr1 = state.getString("keyAttr1");
392
                String keyAttr2 = state.getString("keyAttr2");
393
                String prefix2 =  state.getString("prefix2");                
394
                List attrList = (List) state.get("attrs");
395
                String[] attrs = null;
396
                if (attrList == null) {
397
                        attrs = null;
398
                } else {
399
                        attrs = (String[]) attrList
400
                        .toArray(new String[attrList.size()]);
401
                }                
402
                try {
403
                        initialize(getFeatureStore(), store2, keyAttr1, keyAttr2, prefix1, prefix2, attrs);
404
                } catch (DataException e) {
405
                        throw new PersistenceException("Impossible to create the transform", e);
406
                }
407
        }
408

    
409
}