Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.daltransform.app / org.gvsig.daltransform.app.join / src / main / java / org / gvsig / app / join / dal / feature / JoinTransform.java @ 40558

History | View | Annotate | Download (16.5 KB)

1
/**
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
package org.gvsig.app.join.dal.feature;
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.List;
31
import java.util.Map;
32
import java.util.Map.Entry;
33

    
34
import org.gvsig.fmap.dal.exception.DataException;
35
import org.gvsig.fmap.dal.feature.AbstractFeatureStoreTransform;
36
import org.gvsig.fmap.dal.feature.EditableFeature;
37
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
38
import org.gvsig.fmap.dal.feature.EditableFeatureType;
39
import org.gvsig.fmap.dal.feature.Feature;
40
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
41
import org.gvsig.fmap.dal.feature.FeatureQuery;
42
import org.gvsig.fmap.dal.feature.FeatureSet;
43
import org.gvsig.fmap.dal.feature.FeatureStore;
44
import org.gvsig.fmap.dal.feature.FeatureType;
45
import org.gvsig.tools.ToolsLocator;
46
import org.gvsig.tools.dispose.DisposableIterator;
47
import org.gvsig.tools.dynobject.DynStruct;
48
import org.gvsig.tools.evaluator.Evaluator;
49
import org.gvsig.tools.evaluator.EvaluatorData;
50
import org.gvsig.tools.evaluator.EvaluatorException;
51
import org.gvsig.tools.evaluator.EvaluatorFieldsInfo;
52
import org.gvsig.tools.persistence.PersistenceManager;
53
import org.gvsig.tools.persistence.PersistentState;
54
import org.gvsig.tools.persistence.exception.PersistenceException;
55

    
56
public class JoinTransform extends AbstractFeatureStoreTransform {
57

    
58
    public static final String PERSISTENCE_DEFINITION_NAME = "JoinTransform";
59
    
60
    /**
61
     * Store from which the join transform will get the additional attributes
62
     */
63
    private FeatureStore store2;
64

    
65
    /**
66
     * name of the key attr in store1 that will be used to match features in
67
     * store2
68
     */
69
    private String keyAttr1;
70

    
71
    /**
72
     * name of the key attr in store2 that will be used to match features in
73
     * store1
74
     */
75
    private String keyAttr2;
76

    
77
    /**
78
     * names of the attributes to join from store2 to store1
79
     */
80
    private String[] attrs;
81

    
82
    /**
83
     * Attribute names may change after transformation a prefix is applied.
84
     * This map keeps correspondence between store1 original names
85
     * and their transformed counterparts.
86
     */
87
    private Map<String,String> store1NamesMap;
88

    
89
    /**
90
     * Attribute names may change after transformation if they are repeated in
91
     * both stores or if a prefix is applied. This map keeps correspondence
92
     * between store2 original names and their transformed counterparts.
93
     */
94
    private Map<String,String> store2NamesMap;
95

    
96
    private JoinTransformEvaluator evaluator = null;
97

    
98
    private FeatureType originalFeatureType;
99

    
100
    private String[] attrsForQuery;
101

    
102
    private String prefix1;
103

    
104
    private String prefix2;
105

    
106
    /**
107
     * A default constructor
108
     */
109
    public JoinTransform() {
110
        store1NamesMap = new HashMap<String,String>();
111
        store2NamesMap = new HashMap<String,String>();
112
    }
113

    
114
    /**
115
     * Initializes all the necessary data for this transform
116
     *
117
     * @param store1
118
     *            store whose default feature type is the target of this
119
     *            transform
120
     *
121
     * @param store2
122
     *            store whose default feature type will provide the new
123
     *            attributes to join
124
     *
125
     * @param keyAttr1
126
     *            key attribute in store1 that matches keyAttr2 in store2
127
     *            (foreign key), used for joining both stores.
128
     *
129
     * @param keyAttr2
130
     *            key attribute in store2 that matches keyAttr1 in store2
131
     *            (foreign key), used for joining both stores.
132
     *
133
     * @param attrs
134
     *            names of the attributes in store2 that will be joined to
135
     *            store1.
136
     */
137
    public void initialize(FeatureStore store1, FeatureStore store2,
138
        String keyAttr1, String keyAttr2, String prefix1, String prefix2,
139
        String[] attrs)
140
    throws DataException {
141

    
142
        if (store1 == store2) {
143
            throw new IllegalArgumentException("store1 == store2");
144
        }
145

    
146
        // Initialize needed data
147
        this.setFeatureStore(store1);
148
        this.store2 = store2;
149
        this.keyAttr1 = keyAttr1;
150
        this.keyAttr2 = keyAttr2;
151
        this.prefix1 = prefix1; // TODO
152
        this.prefix2 = prefix2; // TODO
153
        this.attrs = attrs;
154

    
155
        // calculate this transform resulting feature type
156
        // by adding all specified attrs from store2 to store1's default
157
        // feature type
158
        // FIXME for more than one FTypes ??
159
        this.originalFeatureType = this.getFeatureStore()
160
        .getDefaultFeatureType();
161

    
162
        // keep index of geometry and att desc ==============
163
        int orig_geom_field_index =
164
            this.originalFeatureType.getDefaultGeometryAttributeIndex();
165
        FeatureAttributeDescriptor orig_geom_field_att = 
166
            this.originalFeatureType.getDefaultGeometryAttribute();
167
        
168
        // Create the feature type and copy the store 1 type        
169
        EditableFeatureType editableFeatureType = this.getFeatureStore().getDefaultFeatureType().getEditable();
170
        FeatureAttributeDescriptor[] featureAttributeDescriptors = editableFeatureType.getAttributeDescriptors();
171
        for (int i=0 ; i<featureAttributeDescriptors.length ; i++){ 
172
            editableFeatureType.remove(featureAttributeDescriptors[i].getName());           
173
        }    
174
        addFeatureType(editableFeatureType, featureAttributeDescriptors, prefix1, store1NamesMap);
175

    
176
        // =========== set the new geom field name and restore geometry values
177
        if (orig_geom_field_index >= 0) {
178
            EditableFeatureAttributeDescriptor ed_att = null;
179
            ed_att = (EditableFeatureAttributeDescriptor)
180
                editableFeatureType.getAttributeDescriptor(orig_geom_field_index);
181
            ed_att.setSRS(orig_geom_field_att.getSRS());
182
            ed_att.setObjectClass(orig_geom_field_att.getObjectClass());
183
            ed_att.setGeometryType(orig_geom_field_att.getGeomType());
184
            ed_att.setDefaultValue(orig_geom_field_att.getDefaultValue());
185

    
186
            String new_geom_field_name = ed_att.getName();
187
            editableFeatureType.setDefaultGeometryAttributeName(new_geom_field_name);
188
        }
189
        // =====================================================================
190

    
191
        // Add the store 2 fields    
192
        FeatureType featureType2 = store2.getDefaultFeatureType();
193

    
194
        // Add the fields       
195
        for (int i = 0; i < attrs.length; i++) {
196
            addFeatureType(editableFeatureType, featureType2.getAttributeDescriptor(attrs[i]), prefix2, store2NamesMap);        
197
        }
198

    
199
        if (this.store2NamesMap.containsKey(keyAttr2)) {
200
            this.attrsForQuery = this.attrs;
201
        } else {
202
            List<String> list = new ArrayList<String>(this.attrs.length + 1);
203
            list.addAll(Arrays.asList(this.attrs));
204
            list.add(keyAttr2);
205
            this.attrsForQuery = (String[]) list.toArray(new String[] {});
206
        }
207

    
208
        // assign calculated feature type as this transform's feature type
209
        FeatureType[] types = new FeatureType[] { editableFeatureType.getNotEditableCopy() };
210
        setFeatureTypes(Arrays.asList(types), types[0]);
211
    }
212

    
213
    private void addFeatureType(EditableFeatureType targetFeatureType, FeatureAttributeDescriptor[] featureAttributeDescriptors,
214
        String prefix, Map<String, String> storeMap) throws DataException{
215

    
216
        for (int i=0 ; i<featureAttributeDescriptors.length ; i++){         
217
            addFeatureType(targetFeatureType, featureAttributeDescriptors[i], prefix, storeMap);
218
        }
219
    }
220

    
221
    private void addFeatureType(EditableFeatureType targetFeatureType, FeatureAttributeDescriptor featureAttributeDescriptor,
222
        String prefix, Map<String, String> storeMap) throws DataException{
223

    
224
        String attName = featureAttributeDescriptor.getName();
225
        if ((prefix != null) && (!prefix.equals(""))){
226
            attName = prefix + "_" + attName;
227
        }
228

    
229
        // If an attribute already exists, calculate an alternate name and add it to our type
230
        int j = 0;
231
        while (targetFeatureType.getIndex(attName) >= 0) {
232
            attName = targetFeatureType.getAttributeDescriptor(attName).getName() + "_" + ++j;
233
        }
234

    
235
        EditableFeatureAttributeDescriptor editableFeatureAttributeDescriptor = 
236
            targetFeatureType.add(attName, featureAttributeDescriptor.getType(),
237
                featureAttributeDescriptor.getSize());
238
        editableFeatureAttributeDescriptor.setPrecision(featureAttributeDescriptor.getPrecision());
239

    
240
        // keep correspondence between original name and transformed name
241
        storeMap.put(featureAttributeDescriptor.getName(), attName);
242
    } 
243

    
244
    /**
245
     *
246
     *
247
     * @param source
248
     *
249
     * @param target
250
     *
251
     * @throws DataException
252
     */
253
    public void applyTransform(Feature source, EditableFeature target)
254
    throws DataException {
255

    
256
        // copy the data from store1 into the resulting feature
257
        this.copySourceToTarget(source, target);
258

    
259
        // ask store2 for the specified attributes, filtering by the key
260
        // attribute value
261
        // from the source feature
262
        JoinTransformEvaluator eval = this.getEvaluator();
263
        eval.updateValue(source.get(this.keyAttr1));
264

    
265
        FeatureQuery query = store2.createFeatureQuery();
266
        query.setAttributeNames(attrsForQuery);
267
        query.setFilter(eval);
268
        FeatureSet set = null;
269
        DisposableIterator itFeat = null;
270

    
271
        try {
272

    
273
            set = store2.getFeatureSet(query);
274
            // In this join implementation, we will take only the first matching
275
            // feature found in store2
276

    
277
            Feature feat;
278

    
279

    
280
            itFeat = set.iterator();
281
            if (itFeat.hasNext()) {
282
                feat = (Feature) itFeat.next();
283

    
284
                // copy all attributes from joined feature to target
285
                this.copyJoinToTarget(feat, target);
286
            }
287
        } finally {
288
            if (itFeat != null) {
289
                itFeat.dispose();
290
            }
291
            if (set != null) {
292
                set.dispose();
293
            }
294
        }
295
    }
296

    
297
    /**
298
     * @param feat
299
     * @param target
300
     */
301
    private void copyJoinToTarget(Feature join, EditableFeature target) {
302
        Iterator<Entry<String, String>> iter = store2NamesMap.entrySet()
303
        .iterator();
304
        Entry<String, String> entry;
305
        FeatureType trgType = target.getType();
306
        FeatureAttributeDescriptor attr;
307
        while (iter.hasNext()) {
308
            entry = iter.next();
309
            attr = trgType.getAttributeDescriptor((String) entry.getValue());
310
            if (attr != null) {
311
                target.set(attr.getIndex(), join.get((String) entry.getKey()));
312
            }
313
        }
314
    }
315

    
316
    /**
317
     * @param source
318
     * @param target
319
     */
320
    private void copySourceToTarget(Feature source, EditableFeature target) {
321
        FeatureAttributeDescriptor attr, attrTrg;
322
        FeatureType ftSrc = source.getType();
323
        FeatureType ftTrg = target.getType();
324

    
325

    
326
        for (int i = 0; i < source.getType().size(); i++) {
327
            attr = ftSrc.getAttributeDescriptor(i);
328
            attrTrg = ftTrg.getAttributeDescriptor(store1NamesMap.get(attr.getName()));
329
            if (attrTrg != null) {
330
                try {
331
                    target.set(attrTrg.getIndex(), source.get(i));
332
                } catch (IllegalArgumentException e) {
333
                    attrTrg = ftTrg.getAttributeDescriptor(attr.getName());
334
                    target.set(attrTrg.getIndex(), attrTrg.getDefaultValue());
335
                }
336

    
337
            }
338
        }
339

    
340
    }
341

    
342
    private JoinTransformEvaluator getEvaluator() {
343
        if (this.evaluator == null){
344
            this.evaluator = new JoinTransformEvaluator(keyAttr2);
345
        }
346
        return evaluator;
347

    
348
    }
349

    
350
    private class JoinTransformEvaluator implements Evaluator {
351

    
352
        private String attribute;
353
        private Object value;
354
        private String sql;
355
        private EvaluatorFieldsInfo info = null;
356

    
357
        //                private int attributeIndex;
358

    
359
        public JoinTransformEvaluator(String attribute) {
360
            this.attribute = attribute;
361
            this.value = null;
362
            this.info = new EvaluatorFieldsInfo();
363

    
364
            //                        this.attributeIndex = attrIndex;
365
        }
366

    
367
        public void updateValue(Object value) {
368
            this.value = value;
369
            this.sql = this.attribute + "= '" + this.value + "'";
370
            this.info = new EvaluatorFieldsInfo();
371
            this.info.addMatchFieldValue(this.attribute, value);
372
        }
373

    
374
        public Object evaluate(EvaluatorData arg0) throws EvaluatorException {
375
            Object curValue = arg0.getDataValue(attribute);
376
            if (curValue == null) {
377
                return value == null;
378
            }
379
            return curValue.equals(value);
380
        }
381

    
382
        public String getSQL() {
383
            return this.sql;
384
        }
385

    
386
        public String getDescription() {
387
            return "Evaluates join transform match";
388
        }
389

    
390
        public String getName() {
391
            return "JoinTransformEvaluator";
392
        }
393

    
394
        public EvaluatorFieldsInfo getFieldsInfo() {
395
            return this.info;
396
        }
397

    
398
    }        
399

    
400
    @SuppressWarnings("unchecked")
401
    public FeatureType getSourceFeatureTypeFrom(FeatureType arg0) {
402
        return originalFeatureType;
403
    }
404

    
405
    public boolean isTransformsOriginalValues() {
406
        return false;
407
    }
408

    
409
    public static void registerPersistent() {
410
        PersistenceManager persistenceManager = ToolsLocator.getPersistenceManager();
411

    
412
        if( persistenceManager.getDefinition(AbstractFeatureStoreTransform.class) == null ) {
413
            AbstractFeatureStoreTransform.registerPersistent();
414
        }
415

    
416
        DynStruct definition = persistenceManager.getDefinition(PERSISTENCE_DEFINITION_NAME);
417

    
418
        if (definition == null){  
419
            definition = persistenceManager.addDefinition(
420
                JoinTransform.class,
421
                PERSISTENCE_DEFINITION_NAME,
422
                "JoinTransform Persistence definition",
423
                null, 
424
                null
425
            );
426
            definition.extend(PersistenceManager.PERSISTENCE_NAMESPACE,
427
                ABSTRACT_FEATURESTORE_DYNCLASS_NAME);
428

    
429
            definition.addDynFieldObject("store2").setClassOfValue(FeatureStore.class).setMandatory(true);
430
            definition.addDynFieldString("keyAttr1").setMandatory(true);
431
            definition.addDynFieldString("keyAttr2").setMandatory(true);
432
            definition.addDynFieldString("prefix1").setMandatory(false);
433
            definition.addDynFieldString("prefix2").setMandatory(false);
434
            definition.addDynFieldList("attrs").setClassOfItems(String.class).setMandatory(true);
435
        }
436
    }
437

    
438
    public void saveToState(PersistentState state) throws PersistenceException {
439
        super.saveToState(state);
440
        state.set("store2", this.store2);
441
        state.set("keyAttr1", this.keyAttr1);
442
        state.set("keyAttr2", this.keyAttr2);
443
        state.set("prefix1", this.prefix1);
444
        state.set("prefix2", this.prefix2);                
445
        state.set("attrs", this.attrs);
446
    }
447

    
448
    public void loadFromState(PersistentState state) throws PersistenceException {
449
        super.loadFromState(state);
450
        FeatureStore store2 = (FeatureStore) state.get("store2");
451
        String keyAttr1 = state.getString("keyAttr1");
452
        String keyAttr2 = state.getString("keyAttr2");
453
        String prefix1 =  state.getString("prefix1");
454
        String prefix2 =  state.getString("prefix2");
455
        String[] attrs = (String[]) state.getArray("attrs", String.class);
456
        try {
457
            initialize(getFeatureStore(), store2, keyAttr1, keyAttr2, prefix1, prefix2, attrs);
458
        } catch (DataException e) {
459
            throw new PersistenceException("Impossible to create the transform", e);
460
        }
461
    }
462

    
463
}