Statistics
| Revision:

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

History | View | Annotate | Download (15.6 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.exception.DataException;
12
import org.gvsig.fmap.dal.feature.AbstractFeatureStoreTransform;
13
import org.gvsig.fmap.dal.feature.EditableFeature;
14
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
15
import org.gvsig.fmap.dal.feature.EditableFeatureType;
16
import org.gvsig.fmap.dal.feature.Feature;
17
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
18
import org.gvsig.fmap.dal.feature.FeatureQuery;
19
import org.gvsig.fmap.dal.feature.FeatureSet;
20
import org.gvsig.fmap.dal.feature.FeatureStore;
21
import org.gvsig.fmap.dal.feature.FeatureType;
22
import org.gvsig.tools.ToolsLocator;
23
import org.gvsig.tools.dispose.DisposableIterator;
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
    public static final String PERSISTENCE_DEFINITION_NAME = "JoinTransform";
36
    
37
    /**
38
     * Store from which the join transform will get the additional attributes
39
     */
40
    private FeatureStore store2;
41

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

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

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

    
59
    /**
60
     * Attribute names may change after transformation a prefix is applied.
61
     * This map keeps correspondence between store1 original names
62
     * and their transformed counterparts.
63
     */
64
    private Map<String,String> store1NamesMap;
65

    
66
    /**
67
     * Attribute names may change after transformation if they are repeated in
68
     * both stores or if a prefix is applied. This map keeps correspondence
69
     * between store2 original names and their transformed counterparts.
70
     */
71
    private Map<String,String> store2NamesMap;
72

    
73
    private JoinTransformEvaluator evaluator = null;
74

    
75
    private FeatureType originalFeatureType;
76

    
77
    private String[] attrsForQuery;
78

    
79
    private String prefix1;
80

    
81
    private String prefix2;
82

    
83
    /**
84
     * A default constructor
85
     */
86
    public JoinTransform() {
87
        store1NamesMap = new HashMap<String,String>();
88
        store2NamesMap = new HashMap<String,String>();
89
    }
90

    
91
    /**
92
     * Initializes all the necessary data for this transform
93
     *
94
     * @param store1
95
     *            store whose default feature type is the target of this
96
     *            transform
97
     *
98
     * @param store2
99
     *            store whose default feature type will provide the new
100
     *            attributes to join
101
     *
102
     * @param keyAttr1
103
     *            key attribute in store1 that matches keyAttr2 in store2
104
     *            (foreign key), used for joining both stores.
105
     *
106
     * @param keyAttr2
107
     *            key attribute in store2 that matches keyAttr1 in store2
108
     *            (foreign key), used for joining both stores.
109
     *
110
     * @param attrs
111
     *            names of the attributes in store2 that will be joined to
112
     *            store1.
113
     */
114
    public void initialize(FeatureStore store1, FeatureStore store2,
115
        String keyAttr1, String keyAttr2, String prefix1, String prefix2,
116
        String[] attrs)
117
    throws DataException {
118

    
119
        if (store1 == store2) {
120
            throw new IllegalArgumentException("store1 == store2");
121
        }
122

    
123
        // Initialize needed data
124
        this.setFeatureStore(store1);
125
        this.store2 = store2;
126
        this.keyAttr1 = keyAttr1;
127
        this.keyAttr2 = keyAttr2;
128
        this.prefix1 = prefix1; // TODO
129
        this.prefix2 = prefix2; // TODO
130
        this.attrs = attrs;
131

    
132
        // calculate this transform resulting feature type
133
        // by adding all specified attrs from store2 to store1's default
134
        // feature type
135
        // FIXME for more than one FTypes ??
136
        this.originalFeatureType = this.getFeatureStore()
137
        .getDefaultFeatureType();
138

    
139
        // keep index of geometry and att desc ==============
140
        int orig_geom_field_index =
141
            this.originalFeatureType.getDefaultGeometryAttributeIndex();
142
        FeatureAttributeDescriptor orig_geom_field_att = 
143
            this.originalFeatureType.getDefaultGeometryAttribute();
144
        
145
        // Create the feature type and copy the store 1 type        
146
        EditableFeatureType editableFeatureType = this.getFeatureStore().getDefaultFeatureType().getEditable();
147
        FeatureAttributeDescriptor[] featureAttributeDescriptors = editableFeatureType.getAttributeDescriptors();
148
        for (int i=0 ; i<featureAttributeDescriptors.length ; i++){ 
149
            editableFeatureType.remove(featureAttributeDescriptors[i].getName());           
150
        }    
151
        addFeatureType(editableFeatureType, featureAttributeDescriptors, prefix1, store1NamesMap);
152

    
153
        // =========== set the new geom field name and restore geometry values
154
        if (orig_geom_field_index >= 0) {
155
            EditableFeatureAttributeDescriptor ed_att = null;
156
            ed_att = (EditableFeatureAttributeDescriptor)
157
                editableFeatureType.getAttributeDescriptor(orig_geom_field_index);
158
            ed_att.setSRS(orig_geom_field_att.getSRS());
159
            ed_att.setObjectClass(orig_geom_field_att.getObjectClass());
160
            ed_att.setGeometryType(orig_geom_field_att.getGeomType());
161
            ed_att.setDefaultValue(orig_geom_field_att.getDefaultValue());
162

    
163
            String new_geom_field_name = ed_att.getName();
164
            editableFeatureType.setDefaultGeometryAttributeName(new_geom_field_name);
165
        }
166
        // =====================================================================
167

    
168
        // Add the store 2 fields    
169
        FeatureType featureType2 = store2.getDefaultFeatureType();
170

    
171
        // Add the fields       
172
        for (int i = 0; i < attrs.length; i++) {
173
            addFeatureType(editableFeatureType, featureType2.getAttributeDescriptor(attrs[i]), prefix2, store2NamesMap);        
174
        }
175

    
176
        if (this.store2NamesMap.containsKey(keyAttr2)) {
177
            this.attrsForQuery = this.attrs;
178
        } else {
179
            List<String> list = new ArrayList<String>(this.attrs.length + 1);
180
            list.addAll(Arrays.asList(this.attrs));
181
            list.add(keyAttr2);
182
            this.attrsForQuery = (String[]) list.toArray(new String[] {});
183
        }
184

    
185
        // assign calculated feature type as this transform's feature type
186
        FeatureType[] types = new FeatureType[] { editableFeatureType.getNotEditableCopy() };
187
        setFeatureTypes(Arrays.asList(types), types[0]);
188
    }
189

    
190
    private void addFeatureType(EditableFeatureType targetFeatureType, FeatureAttributeDescriptor[] featureAttributeDescriptors,
191
        String prefix, Map<String, String> storeMap) throws DataException{
192

    
193
        for (int i=0 ; i<featureAttributeDescriptors.length ; i++){         
194
            addFeatureType(targetFeatureType, featureAttributeDescriptors[i], prefix, storeMap);
195
        }
196
    }
197

    
198
    private void addFeatureType(EditableFeatureType targetFeatureType, FeatureAttributeDescriptor featureAttributeDescriptor,
199
        String prefix, Map<String, String> storeMap) throws DataException{
200

    
201
        String attName = featureAttributeDescriptor.getName();
202
        if ((prefix != null) && (!prefix.equals(""))){
203
            attName = prefix + "_" + attName;
204
        }
205

    
206
        // If an attribute already exists, calculate an alternate name and add it to our type
207
        int j = 0;
208
        while (targetFeatureType.getIndex(attName) >= 0) {
209
            attName = targetFeatureType.getAttributeDescriptor(attName).getName() + "_" + ++j;
210
        }
211

    
212
        EditableFeatureAttributeDescriptor editableFeatureAttributeDescriptor = 
213
            targetFeatureType.add(attName, featureAttributeDescriptor.getType(),
214
                featureAttributeDescriptor.getSize());
215
        editableFeatureAttributeDescriptor.setPrecision(featureAttributeDescriptor.getPrecision());
216

    
217
        // keep correspondence between original name and transformed name
218
        storeMap.put(featureAttributeDescriptor.getName(), attName);
219
    } 
220

    
221
    /**
222
     *
223
     *
224
     * @param source
225
     *
226
     * @param target
227
     *
228
     * @throws DataException
229
     */
230
    public void applyTransform(Feature source, EditableFeature target)
231
    throws DataException {
232

    
233
        // copy the data from store1 into the resulting feature
234
        this.copySourceToTarget(source, target);
235

    
236
        // ask store2 for the specified attributes, filtering by the key
237
        // attribute value
238
        // from the source feature
239
        JoinTransformEvaluator eval = this.getEvaluator();
240
        eval.updateValue(source.get(this.keyAttr1));
241

    
242
        FeatureQuery query = store2.createFeatureQuery();
243
        query.setAttributeNames(attrsForQuery);
244
        query.setFilter(eval);
245
        FeatureSet set = null;
246
        DisposableIterator itFeat = null;
247

    
248
        try {
249

    
250
            set = store2.getFeatureSet(query);
251
            // In this join implementation, we will take only the first matching
252
            // feature found in store2
253

    
254
            Feature feat;
255

    
256

    
257
            itFeat = set.iterator();
258
            if (itFeat.hasNext()) {
259
                feat = (Feature) itFeat.next();
260

    
261
                // copy all attributes from joined feature to target
262
                this.copyJoinToTarget(feat, target);
263
            }
264
        } finally {
265
            if (itFeat != null) {
266
                itFeat.dispose();
267
            }
268
            if (set != null) {
269
                set.dispose();
270
            }
271
        }
272
    }
273

    
274
    /**
275
     * @param feat
276
     * @param target
277
     */
278
    private void copyJoinToTarget(Feature join, EditableFeature target) {
279
        Iterator<Entry<String, String>> iter = store2NamesMap.entrySet()
280
        .iterator();
281
        Entry<String, String> entry;
282
        FeatureType trgType = target.getType();
283
        FeatureAttributeDescriptor attr;
284
        while (iter.hasNext()) {
285
            entry = iter.next();
286
            attr = trgType.getAttributeDescriptor((String) entry.getValue());
287
            if (attr != null) {
288
                target.set(attr.getIndex(), join.get((String) entry.getKey()));
289
            }
290
        }
291
    }
292

    
293
    /**
294
     * @param source
295
     * @param target
296
     */
297
    private void copySourceToTarget(Feature source, EditableFeature target) {
298
        FeatureAttributeDescriptor attr, attrTrg;
299
        FeatureType ftSrc = source.getType();
300
        FeatureType ftTrg = target.getType();
301

    
302

    
303
        for (int i = 0; i < source.getType().size(); i++) {
304
            attr = ftSrc.getAttributeDescriptor(i);
305
            attrTrg = ftTrg.getAttributeDescriptor(store1NamesMap.get(attr.getName()));
306
            if (attrTrg != null) {
307
                try {
308
                    target.set(attrTrg.getIndex(), source.get(i));
309
                } catch (IllegalArgumentException e) {
310
                    attrTrg = ftTrg.getAttributeDescriptor(attr.getName());
311
                    target.set(attrTrg.getIndex(), attrTrg.getDefaultValue());
312
                }
313

    
314
            }
315
        }
316

    
317
    }
318

    
319
    private JoinTransformEvaluator getEvaluator() {
320
        if (this.evaluator == null){
321
            this.evaluator = new JoinTransformEvaluator(keyAttr2);
322
        }
323
        return evaluator;
324

    
325
    }
326

    
327
    private class JoinTransformEvaluator implements Evaluator {
328

    
329
        private String attribute;
330
        private Object value;
331
        private String sql;
332
        private EvaluatorFieldsInfo info = null;
333

    
334
        //                private int attributeIndex;
335

    
336
        public JoinTransformEvaluator(String attribute) {
337
            this.attribute = attribute;
338
            this.value = null;
339
            this.info = new EvaluatorFieldsInfo();
340

    
341
            //                        this.attributeIndex = attrIndex;
342
        }
343

    
344
        public void updateValue(Object value) {
345
            this.value = value;
346
            this.sql = this.attribute + "= '" + this.value + "'";
347
            this.info = new EvaluatorFieldsInfo();
348
            this.info.addMatchFieldValue(this.attribute, value);
349
        }
350

    
351
        public Object evaluate(EvaluatorData arg0) throws EvaluatorException {
352
            Object curValue = arg0.getDataValue(attribute);
353
            if (curValue == null) {
354
                return value == null;
355
            }
356
            return curValue.equals(value);
357
        }
358

    
359
        public String getSQL() {
360
            return this.sql;
361
        }
362

    
363
        public String getDescription() {
364
            return "Evaluates join transform match";
365
        }
366

    
367
        public String getName() {
368
            return "JoinTransformEvaluator";
369
        }
370

    
371
        public EvaluatorFieldsInfo getFieldsInfo() {
372
            return this.info;
373
        }
374

    
375
    }        
376

    
377
    @SuppressWarnings("unchecked")
378
    public FeatureType getSourceFeatureTypeFrom(FeatureType arg0) {
379
        return originalFeatureType;
380
    }
381

    
382
    public boolean isTransformsOriginalValues() {
383
        return false;
384
    }
385

    
386
    public static void registerPersistent() {
387
        PersistenceManager persistenceManager = ToolsLocator.getPersistenceManager();
388

    
389
        if( persistenceManager.getDefinition(AbstractFeatureStoreTransform.class) == null ) {
390
            AbstractFeatureStoreTransform.registerPersistent();
391
        }
392

    
393
        DynStruct definition = persistenceManager.getDefinition(PERSISTENCE_DEFINITION_NAME);
394

    
395
        if (definition == null){  
396
            definition = persistenceManager.addDefinition(
397
                JoinTransform.class,
398
                PERSISTENCE_DEFINITION_NAME,
399
                "JoinTransform Persistence definition",
400
                null, 
401
                null
402
            );
403
            definition.extend(PersistenceManager.PERSISTENCE_NAMESPACE,
404
                ABSTRACT_FEATURESTORE_DYNCLASS_NAME);
405

    
406
            definition.addDynFieldObject("store2").setClassOfValue(FeatureStore.class).setMandatory(true);
407
            definition.addDynFieldString("keyAttr1").setMandatory(true);
408
            definition.addDynFieldString("keyAttr2").setMandatory(true);
409
            definition.addDynFieldString("prefix1").setMandatory(false);
410
            definition.addDynFieldString("prefix2").setMandatory(false);
411
            definition.addDynFieldList("attrs").setClassOfItems(String.class).setMandatory(true);
412
        }
413
    }
414

    
415
    public void saveToState(PersistentState state) throws PersistenceException {
416
        super.saveToState(state);
417
        state.set("store2", this.store2);
418
        state.set("keyAttr1", this.keyAttr1);
419
        state.set("keyAttr2", this.keyAttr2);
420
        state.set("prefix1", this.prefix1);
421
        state.set("prefix2", this.prefix2);                
422
        state.set("attrs", this.attrs);
423
    }
424

    
425
    public void loadFromState(PersistentState state) throws PersistenceException {
426
        super.loadFromState(state);
427
        FeatureStore store2 = (FeatureStore) state.get("store2");
428
        String keyAttr1 = state.getString("keyAttr1");
429
        String keyAttr2 = state.getString("keyAttr2");
430
        String prefix2 =  state.getString("prefix2");
431
        String[] attrs = (String[]) state.getArray("attrs", String.class);
432
        try {
433
            initialize(getFeatureStore(), store2, keyAttr1, keyAttr2, prefix1, prefix2, attrs);
434
        } catch (DataException e) {
435
            throw new PersistenceException("Impossible to create the transform", e);
436
        }
437
    }
438

    
439
}