Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.impl / src / main / java / org / gvsig / fmap / dal / feature / impl / DefaultFeature.java @ 47784

History | View | Annotate | Download (69.8 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.fmap.dal.feature.impl;
25

    
26
import java.lang.ref.WeakReference;
27
import java.math.BigDecimal;
28
import java.time.LocalDateTime;
29
import java.time.ZoneId;
30
import java.time.format.DateTimeFormatter;
31
import java.util.ArrayList;
32
import java.util.Date;
33
import java.util.HashMap;
34
import java.util.HashSet;
35
import java.util.Iterator;
36
import java.util.List;
37
import java.util.Map;
38
import java.util.Objects;
39
import java.util.Set;
40
import java.util.function.Predicate;
41
import javax.json.JsonObject;
42
import org.apache.commons.collections4.CollectionUtils;
43
import org.apache.commons.lang3.ArrayUtils;
44
import static org.apache.commons.lang3.ObjectUtils.mode;
45
import org.apache.commons.lang3.StringUtils;
46
import org.cresques.cts.IProjection;
47
import org.gvsig.expressionevaluator.Expression;
48
import org.gvsig.expressionevaluator.ExpressionBuilder;
49
import org.gvsig.expressionevaluator.ExpressionUtils;
50
import org.gvsig.fmap.dal.DALLocator;
51
import org.gvsig.fmap.dal.DataTypes;
52
import org.gvsig.fmap.dal.SupportTransactions;
53
import org.gvsig.fmap.dal.exception.DataEvaluatorRuntimeException;
54
import org.gvsig.fmap.dal.exception.DataException;
55
import org.gvsig.fmap.dal.exception.DataRuntimeException;
56
import org.gvsig.fmap.dal.feature.DataProfile;
57
import org.gvsig.fmap.dal.feature.EditableFeature;
58
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
59
import org.gvsig.fmap.dal.feature.Feature;
60
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
61
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
62
import org.gvsig.fmap.dal.feature.FeatureAttributeGetter;
63
import org.gvsig.fmap.dal.feature.FeatureExtraColumns;
64
import org.gvsig.fmap.dal.feature.FeatureReference;
65
import org.gvsig.fmap.dal.feature.FeatureStore;
66
import org.gvsig.fmap.dal.feature.FeatureType;
67
import org.gvsig.fmap.dal.feature.ForeingKey;
68
import org.gvsig.fmap.dal.feature.exception.IllegalValueException;
69
import org.gvsig.fmap.dal.feature.exception.SetReadOnlyAttributeException;
70
import org.gvsig.fmap.dal.feature.impl.dynobjectutils.DynObjectFeatureFacade;
71
import org.gvsig.fmap.dal.feature.impl.featurereference.FeatureReferenceFactory;
72
import org.gvsig.fmap.dal.feature.spi.DefaultFeatureProvider;
73
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
74
import org.gvsig.fmap.geom.Geometry;
75
import org.gvsig.fmap.geom.primitive.Envelope;
76
import org.gvsig.json.Json;
77
import org.gvsig.json.JsonArrayBuilder;
78
import org.gvsig.json.JsonObjectBuilder;
79
import org.gvsig.tools.ToolsLocator;
80
import org.gvsig.tools.dataTypes.Coercion;
81
import org.gvsig.tools.dataTypes.CoercionException;
82
import org.gvsig.tools.dataTypes.DataType;
83
import org.gvsig.tools.dataTypes.DataTypesManager;
84
import org.gvsig.tools.dispose.DisposeUtils;
85
import org.gvsig.tools.dynobject.DynField;
86
import org.gvsig.tools.dynobject.DynObject;
87
import org.gvsig.tools.dynobject.Tags;
88
import org.gvsig.tools.evaluator.Evaluator;
89
import org.gvsig.tools.evaluator.EvaluatorData;
90
import org.gvsig.tools.evaluator.EvaluatorException;
91
import org.gvsig.tools.exception.BaseException;
92
import org.gvsig.tools.exception.BaseRuntimeException;
93
import org.gvsig.tools.lang.Cloneable;
94
import org.gvsig.tools.util.Bitmask;
95
import org.slf4j.Logger;
96
import org.slf4j.LoggerFactory;
97

    
98
@SuppressWarnings("UseSpecificCatch")
99
public class DefaultFeature implements Feature, EvaluatorData, Cloneable {
100

    
101
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultFeature.class);
102

    
103
    private static DataTypesManager dataTypesManager = null;
104
    protected FeatureProvider data;
105
    protected FeatureReference reference;
106
    private WeakReference storeRef;
107

    
108
    private boolean inserted = false;
109
    private Object[] extraValuesData;
110
    private Map<String, Object> extraValues; // not persistent
111

    
112
    /*
113
         * Usar con mucha precaucion o mejor no usar. Lo precisa el
114
         * DefaultFeatureSet en la ordenacion.
115
     */
116
    public DefaultFeature(FeatureStore store) {
117
        this.storeRef = new WeakReference(store);
118
        this.reference = null;
119
    }
120

    
121
    public DefaultFeature(FeatureStore store, FeatureProvider data) {
122
        this.data = data;
123
        this.extraValuesData = null;
124
        this.storeRef = new WeakReference(store);
125
        this.reference = null;
126
        this.inserted = !data.isNew();
127
    }
128

    
129
    DefaultFeature(DefaultFeature feature) {
130
        this.data = feature.data.getCopy();
131
        this.extraValuesData = ArrayUtils.clone(feature.extraValuesData);
132
        this.storeRef = feature.storeRef;
133
        this.reference = feature.reference;
134
        this.inserted = feature.isInserted();
135
    }
136

    
137
    public DefaultFeature(FeatureType targetType, Feature sourceFeature) {
138
        DefaultFeature defaultFeature = (DefaultFeature) sourceFeature;
139
        this.data = new DefaultFeatureProvider(targetType, (DefaultFeatureProvider) defaultFeature.getData());
140
        this.extraValuesData = null;
141
        this.storeRef = defaultFeature.storeRef;
142
        this.reference = defaultFeature.reference;
143
        this.inserted = defaultFeature.isInserted();
144

    
145
        FeatureType sourceType = sourceFeature.getType();
146

    
147
        for (FeatureAttributeDescriptor targetAttrDescriptor : targetType) {
148
            if (targetAttrDescriptor.isComputed()) {
149
                continue;
150
            }
151
            int sourceIndex = sourceType.getIndex(targetAttrDescriptor.getName());
152
            if (sourceIndex < 0) {
153
                continue;
154
            }
155
            Object value = sourceFeature.get(sourceIndex);
156
            if (value == null && !targetAttrDescriptor.allowNull()) {
157
                continue;
158
            }
159
            this.setforced(targetAttrDescriptor.getIndex(), targetAttrDescriptor, value);
160
        }
161
    }
162

    
163
    public void setData(FeatureProvider data) {
164
        this.data = data;
165
        this.extraValuesData = null;
166
        this.reference = null;
167
        this.inserted = true;
168
    }
169

    
170
    public FeatureProvider getData() {
171
        return this.data;
172
    }
173

    
174
    protected DataTypesManager getDataTypesManager() {
175
        if (dataTypesManager == null) {
176
            dataTypesManager = ToolsLocator.getDataTypesManager();
177
        }
178
        return dataTypesManager;
179
    }
180

    
181
    public boolean canSetValue(String name) {
182
        return this.canSetValue(this.getType().getAttributeDescriptor(name), null);
183
    }
184

    
185
    public boolean canSetValue(FeatureAttributeDescriptor attr,Predicate<FeatureAttributeDescriptor> copy) {
186
        // helper function to use in copyFrom
187
        if (attr==null  ) {
188
            return false;
189
        }
190
        if (attr.isAutomatic()  || attr.isComputed() ) {
191
            return false;
192
        }
193
        if( this.isInserted() &&  attr.isReadOnly()) {
194
            return false;
195
        }
196
        if( copy!=null && !copy.test(attr) ) {
197
            return false;
198
        }
199
        return true;
200
    }
201

    
202
    protected void set(FeatureAttributeDescriptor attribute, Object value) {
203
        int i = attribute.getIndex();
204

    
205
        if( this.isInserted() ) {
206
            if (attribute.isReadOnly()) {
207
                throw new SetReadOnlyAttributeException(attribute.getName(), this.getType());
208
            }
209
        } else {
210
            if (attribute.isComputed()) {
211
                throw new SetReadOnlyAttributeException(attribute.getName(), this.getType());
212
            }
213
        }
214
        FeatureAttributeEmulator emulator = attribute.getFeatureAttributeEmulator();
215
        if (emulator != null) {
216
            emulator.set((EditableFeature) this, value);
217
            return;
218
        }
219

    
220
        if (value == null) {
221
            if (this.isInserted()) {
222
                if (!attribute.allowNull()) {
223
                    if (!attribute.isAutomatic()) {
224
                        throw new IllegalValueException(attribute, value);
225
                    }
226
                }
227
            }
228
            this.data.set(i, null);
229
            return;
230

    
231
        }
232

    
233
        if (attribute.getFeatureAttributeGetter() != null) {
234
            value = attribute.getFeatureAttributeGetter().setter(value);
235
        }
236
        this.setforced(i, attribute, value);
237
    }
238

    
239
    private void setforced(int i, FeatureAttributeDescriptor attribute, Object value) {
240

    
241
        Class objectClass = attribute.getObjectClass();
242
        if (objectClass != null) {
243
            if (objectClass.isInstance(value)) {
244
                if (attribute.getType() == DataTypes.DECIMAL) {
245
                    BigDecimal d = (BigDecimal) value;
246
                    if (d.scale() == attribute.getScale() && d.precision() <= attribute.getPrecision()) {
247
                        this.data.set(i, value);
248
                        return;
249
                    }
250
                } else if (attribute.getType() == DataTypes.GEOMETRY) {
251
                    if (!attribute.getGeomType().equals(((Geometry) value).getGeometryType())) {
252
                        try {
253
                            Coercion coercer = attribute.getDataType().getCoercion();
254
                            value = coercer.coerce(value, attribute.getCoercionContext());
255
                        } catch (CoercionException e) {
256
                            throw new IllegalArgumentException("Can't convert to "
257
                                    + attribute.getDataType().getName()
258
                                    + " from '"
259
                                    + value == null ? "NULL" : value.getClass().getName()
260
                                            + "' with value '"
261
                                            + Objects.toString(value)
262
                                            + "' and context '"
263
                                            + attribute.getCoercionContext()
264
                                            + "'.",e);
265
                        }
266
                    } 
267
                    this.data.set(i, value);
268
                    return;
269
                } else {
270
                    this.data.set(i, value);
271
                    return;
272
                }
273
            }
274
            DataProfile dataProfile = attribute.getDataProfile();
275
            if (dataProfile != null) {
276
                try {
277
                    value = dataProfile.coerce(
278
                            attribute.getDataType(),
279
                            value,
280
                            attribute.getTags()
281
                    );
282
                } catch (CoercionException e) {
283

    
284
                }
285
            }
286
            try {
287
                Coercion coercer = attribute.getDataType().getCoercion();
288
                if (attribute.getType() == DataTypes.STRING && value instanceof Boolean) {
289
                    value = coercer.coerce(value, attribute.getCoercionContext());
290
                    value = StringUtils.left((String) value, attribute.getSize());
291
                } else {
292
                    value = coercer.coerce(value, attribute.getCoercionContext());
293
                }
294
            } catch (CoercionException e) {
295
                throw new IllegalArgumentException("Can't assign value [" + 
296
                        toStringQuietly(value)+
297
                        "] of type '"+
298
                        (value == null ? "NULL" : value.getClass().getName())+
299
                        "' to field '"+
300
                        attribute.getName()+
301
                        "' of type "+
302
                        attribute.getDataType().getName()+
303
                        "."
304
                ,e);
305
            }
306
        }
307
        this.data.set(i, value);
308
    }
309

    
310
    private String toStringQuietly(Object v) {
311
        try {
312
            return Objects.toString(v);
313
        } catch(Throwable t) {
314
            return "ERROR";
315
        }
316
    }
317
    
318
    private Object get(int index, Class theClass, int type) {
319
        Object value = this.get(index);
320
        if (theClass.isInstance(value)) {
321
            return value;
322
        }
323
        try {
324
            return this.getDataTypesManager().coerce(type, value, this.getType().getAttributeDescriptor(index).getCoercionContext());
325
        } catch (CoercionException e) {
326

    
327
            if (value == null) {
328
                return null;
329
            }
330
            throw new IllegalArgumentException(
331
                    "Can't convert to " + theClass.getName()
332
                    + " from '" + value.getClass().getName()
333
                    + "' with value '" + value.toString() + "'.");
334
        }
335
    }
336

    
337
    public void initializeValues() {
338
        FeatureType type = this.getType();
339
        for (FeatureAttributeDescriptor attribute : type) {
340
            if (attribute.isAutomatic() || attribute.isReadOnly()
341
                    || attribute.isComputed()) {
342
                continue;
343
            }
344
            if (attribute.getDefaultValue() == null && !attribute.allowNull()) {
345
                continue;
346
            }
347
            Object value = attribute.getDefaultValue();
348
            if (value instanceof CharSequence) {
349
                String s = ((CharSequence) value).toString();
350
                if (ExpressionUtils.isDynamicText(s)) {
351
                    try {
352
                        value = ExpressionUtils.evaluateDynamicText(s);
353
                    } catch (Throwable th) {
354
                        value = null;
355
                    }
356
                }
357
            }
358
            this.set(attribute, value);
359
        }
360
    }
361

    
362
    public void clear() {
363
        initializeValues();
364
    }
365

    
366
    public void initializeValues(Feature feature) {
367
        FeatureType myType = this.getType();
368
        FeatureType type = feature.getType();
369
        extraValuesData = null;
370
        for (FeatureAttributeDescriptor attribute : type) {
371
            FeatureAttributeDescriptor myAttribute = myType.getAttributeDescriptor(attribute.getName());
372
            if (myAttribute != null) {
373
                this.set(myAttribute, feature.get(attribute.getIndex()));
374
            }
375
        }
376
    }
377

    
378
    @Override
379
    public FeatureStore getStore() {
380
        return (FeatureStore) this.storeRef.get();
381
    }
382

    
383
    @Override
384
    public FeatureType getType() {
385
        return this.data.getType();
386
    }
387

    
388
    @Override
389
    public EditableFeature getEditable() {
390
        return new DefaultEditableFeature(this);
391
    }
392

    
393
    @Override
394
    public Feature getCopy() {
395
        return new DefaultFeature(this);
396
    }
397

    
398
    @Override
399
    @SuppressWarnings("CloneDoesntCallSuperClone")
400
    public Object clone() throws CloneNotSupportedException {
401
        return new DefaultFeature(this);
402
    }
403

    
404
    @Override
405
    public FeatureReference getReference() {
406
        if(!this.getType().supportReferences()){
407
            return null;
408
        }
409
        if (this.reference == null) {
410
            if (!isInserted()) {
411
                return null;
412
            }
413
            reference = FeatureReferenceFactory.createFromFeature(this);
414
        }
415
        return this.reference;
416
    }
417

    
418
    @Override
419
    public Object getOrDefault(String name, Object defaultValue) {
420
        int index = this.data.getType().getIndex(name);
421
        if (index < 0) {
422
            return defaultValue;
423
        }
424
        return this.get(index);
425
    }
426
    
427
    @Override
428
    public Object getOrDefault(String name, int type, Object defaultValue) {
429
        DataType dataType = ToolsLocator.getDataTypesManager().get(type);
430
        return getOrDefault(name, dataType, defaultValue);
431
    }
432
    
433
    @Override
434
    public Object getOrDefault(String name, DataType type, Object defaultValue) {
435
        int index = this.data.getType().getIndex(name);
436
        if (index < 0) {
437
            return defaultValue;
438
        }
439
        try {
440
            Object value = this.get(index);
441
            if(value == null){
442
                return defaultValue;
443
            }
444
            return type.coerce(value);
445
        } catch (Throwable th) {
446
            return defaultValue;
447
        }
448
    }
449

    
450
    @Override
451
    public String getStringOrDefault(String name, String defaultValue) {
452
        int index = this.data.getType().getIndex(name);
453
        if (index < 0) {
454
            return defaultValue;
455
        }
456
        try {
457
            return (String) this.get(index);
458
        } catch (Throwable th) {
459
            return defaultValue;
460
        }
461
    }
462

    
463
    @Override
464
    public boolean getBooleanOrDefault(String name, boolean defaultValue) {
465
        int index = this.data.getType().getIndex(name);
466
        if (index < 0) {
467
            return defaultValue;
468
        }
469
        try {
470
            return this.getBoolean(index);
471
        } catch (Throwable th) {
472
            return defaultValue;
473
        }
474
    }
475

    
476
    @Override
477
    public int getIntOrDefault(String name, int defaultValue) {
478
        int index = this.data.getType().getIndex(name);
479
        if (index < 0) {
480
            return defaultValue;
481
        }
482
        try {
483
            return this.getInt(index);
484
        } catch (Throwable th) {
485
            return defaultValue;
486
        }
487
    }
488

    
489
    @Override
490
    public long getLongOrDefault(String name, long defaultValue) {
491
        int index = this.data.getType().getIndex(name);
492
        if (index < 0) {
493
            return defaultValue;
494
        }
495
        try {
496
            return this.getLong(index);
497
        } catch (Throwable th) {
498
            return defaultValue;
499
        }
500
    }
501

    
502
    @Override
503
    public float getFloatOrDefault(String name, float defaultValue) {
504
        int index = this.data.getType().getIndex(name);
505
        if (index < 0) {
506
            return defaultValue;
507
        }
508
        try {
509
            return this.getFloat(index);
510
        } catch (Throwable th) {
511
            return defaultValue;
512
        }
513
    }
514

    
515
    @Override
516
    public double getDoubleOrDefault(String name, double defaultValue) {
517
        int index = this.data.getType().getIndex(name);
518
        if (index < 0) {
519
            return defaultValue;
520
        }
521
        try {
522
            return this.getDouble(index);
523
        } catch (Throwable th) {
524
            return defaultValue;
525
        }
526
    }
527

    
528
    @Override
529
    public BigDecimal getDecimalOrDefault(String name, BigDecimal defaultValue) {
530
        int index = this.data.getType().getIndex(name);
531
        if (index < 0) {
532
            return defaultValue;
533
        }
534
        try {
535
            return this.getDecimal(index);
536
        } catch (Throwable th) {
537
            return defaultValue;
538
        }
539
    }
540

    
541
    @Override
542
    public Date getDateOrDefault(String name, Date defaultValue) {
543
        int index = this.data.getType().getIndex(name);
544
        if (index < 0) {
545
            return defaultValue;
546
        }
547
        try {
548
            return this.getDate(index);
549
        } catch (Throwable th) {
550
            return defaultValue;
551
        }
552
    }
553

    
554
    @Override
555
    public Object getOrDefault(int index, Object defaultValue) {
556
        if (index < 0 || index >= this.data.getType().size()) {
557
            return defaultValue;
558
        }
559
        try {
560
            return this.get(index);
561
        } catch (Throwable th) {
562
            return defaultValue;
563
        }
564
    }
565

    
566
    @Override
567
    public String getStringOrDefault(int index, String defaultValue) {
568
        if (index < 0 || index >= this.data.getType().size()) {
569
            return defaultValue;
570
        }
571
        try {
572
            return this.getString(index);
573
        } catch (Throwable th) {
574
            return defaultValue;
575
        }
576
    }
577

    
578
    @Override
579
    public boolean getBooleanOrDefault(int index, boolean defaultValue) {
580
        if (index < 0 || index >= this.data.getType().size()) {
581
            return defaultValue;
582
        }
583
        try {
584
            return this.getBoolean(index);
585
        } catch (Throwable th) {
586
            return defaultValue;
587
        }
588
    }
589

    
590
    @Override
591
    public int getIntOrDefault(int index, int defaultValue) {
592
        if (index < 0 || index >= this.data.getType().size()) {
593
            return defaultValue;
594
        }
595
        try {
596
            return this.getInt(index);
597
        } catch (Throwable th) {
598
            return defaultValue;
599
        }
600
    }
601

    
602
    @Override
603
    public long getLongOrDefault(int index, long defaultValue) {
604
        if (index < 0 || index >= this.data.getType().size()) {
605
            return defaultValue;
606
        }
607
        try {
608
            return this.getLong(index);
609
        } catch (Throwable th) {
610
            return defaultValue;
611
        }
612
    }
613

    
614
    @Override
615
    public float getFloatOrDefault(int index, float defaultValue) {
616
        if (index < 0 || index >= this.data.getType().size()) {
617
            return defaultValue;
618
        }
619
        try {
620
            return this.getFloat(index);
621
        } catch (Throwable th) {
622
            return defaultValue;
623
        }
624
    }
625

    
626
    @Override
627
    public double getDoubleOrDefault(int index, double defaultValue) {
628
        if (index < 0 || index >= this.data.getType().size()) {
629
            return defaultValue;
630
        }
631
        try {
632
            return this.getDouble(index);
633
        } catch (Throwable th) {
634
            return defaultValue;
635
        }
636
    }
637

    
638
    @Override
639
    public BigDecimal getDecimalOrDefault(int index, BigDecimal defaultValue) {
640
        if (index < 0 || index >= this.data.getType().size()) {
641
            return defaultValue;
642
        }
643
        try {
644
            return this.getDecimal(index);
645
        } catch (Throwable th) {
646
            return defaultValue;
647
        }
648
    }
649

    
650
    @Override
651
    public Date getDateOrDefault(int index, Date defaultValue) {
652
        if (index < 0 || index >= this.data.getType().size()) {
653
            return defaultValue;
654
        }
655
        try {
656
            return this.getDate(index);
657
        } catch (Throwable th) {
658
            return defaultValue;
659
        }
660
    }
661

    
662
    @Override
663
    public void validate(int check) throws DataException {
664
        ((DefaultFeatureType) this.data.getType()).validateFeature(this, check);
665
    }
666

    
667
    @Override
668
    public boolean isBroken() {
669
        return this.data.isBroken();
670
    }
671

    
672
    class UnableToGetReferenceException extends BaseRuntimeException {
673

    
674
        /**
675
         *
676
         */
677
        private static final long serialVersionUID = 1812805035204824163L;
678

    
679
        /**
680
         * @param exception
681
         */
682
        @SuppressWarnings("OverridableMethodCallInConstructor")
683
        public UnableToGetReferenceException(BaseException exception) {
684
            super("Unable to get reference", "_UnableToGetReferenceException",
685
                    serialVersionUID);
686
            this.initCause(exception);
687

    
688
        }
689

    
690
    }
691

    
692
    @Override
693
    public List getSRSs() {
694
        // TODO Auto-generated method stub
695
        return null;
696
    }
697

    
698
    @Override
699
    public Envelope getDefaultEnvelope() {
700
        Envelope envelope = this.data.getDefaultEnvelope();
701
        if (envelope == null) {
702
            int i = this.data.getType().getDefaultGeometryAttributeIndex();
703
            if (i < 0) {
704
                return null;
705
            }
706
            Geometry geom = this.getDefaultGeometry();
707
            if (geom != null) {
708
                envelope = geom.getEnvelope();
709
            }
710
        }
711
        return envelope;
712
    }
713

    
714
    @Override
715
    public Geometry getDefaultGeometry() {
716
        Geometry geom = this.data.getDefaultGeometry();
717
        if (geom == null) {
718
            int i = this.data.getType().getDefaultGeometryAttributeIndex();
719
            if (i < 0) {
720
                return null;
721
            }
722
            Object x = this.get(i);
723
            if (x instanceof Geometry) {
724
                geom = (Geometry) x;
725
            } else {
726
                geom = this.getGeometry(i);
727
            }
728
        }
729
        if (geom != null) {
730
            if (geom.getProjection() == null) {
731
                FeatureType type = this.getType();
732
                DefaultFeatureAttributeDescriptor attrdesc = (DefaultFeatureAttributeDescriptor) type.get(type.getDefaultGeometryAttributeIndex());
733
                IProjection proj = attrdesc.getSRS(this.storeRef);
734
                geom.setProjection(proj);
735
            }
736
        }
737
        return geom;
738
    }
739

    
740
//    @Override
741
//    public Time getDefaultTime() {
742
//            Time time = this.data.getDefaultTime();
743
//        if( time == null ) {
744
//            int i = this.data.getType().getDefaultTimeAttributeIndex();
745
//            Object x = this.get(i);
746
//            if( x instanceof Time ) {
747
//                time = (Time) x;
748
//            } else {
749
//                time = this.getTime(i);
750
//            }
751
//        }
752
//        return time;
753
//    }
754
//
755
    @Override
756
    public IProjection getDefaultSRS() {
757
        IProjection srs = this.data.getType().getDefaultSRS();
758
        if (srs == null) {
759
            FeatureType type = this.getType();
760
            DefaultFeatureAttributeDescriptor attrdesc = (DefaultFeatureAttributeDescriptor) type.get(type.getDefaultGeometryAttributeIndex());
761
            srs = attrdesc.getSRS(this.storeRef);
762
        }
763
        return srs;
764
    }
765

    
766
    @Override
767
    public List getGeometries() {
768
        // TODO Auto-generated method stub
769
        return null;
770
    }
771

    
772
    @Override
773
    public Object getFromProfile(int index) {
774
        FeatureAttributeDescriptor descriptor = this.data.getType().getAttributeDescriptor(index);
775
        Object value = this.get(index);
776
        String profileName = descriptor.getDataProfileName();
777
        if (StringUtils.isBlank(profileName)) {
778
            return value;
779
        }
780
        DataProfile profile = DALLocator.getDataManager().getDataProfile(profileName);
781
        if (profile == null) {
782
            return value;
783
        }
784
        return profile.createData(value, descriptor.getTags());
785
    }
786

    
787
    @Override
788
    public Object getFromProfile(String name) {
789
        FeatureAttributeDescriptor descriptor = this.data.getType().getAttributeDescriptor(name);
790
        return this.getFromProfile(descriptor.getIndex());
791
    }
792

    
793
    private Object get(String name, Class theClass, int type) {
794
        Object value = this.get(name);
795
        if (theClass.isInstance(value)) {
796
            return value;
797
        }
798
        try {
799
            return this.getDataTypesManager().coerce(type, value);
800
        } catch (CoercionException e) {
801

    
802
            if (value == null) {
803
                return null;
804
            }
805
            throw new IllegalArgumentException(
806
                    "Can't convert to " + theClass.getName()
807
                    + " from '" + value.getClass().getName()
808
                    + "' with value '" + value.toString() + "'.");
809
        }
810
    }
811

    
812
    @Override
813
    public Object get(String name) {
814
        int index = this.data.getType().getIndex(name);
815
        if (index < 0) {
816
            // buscamos en los extra cols
817
            if (hasExtraColumnValue(name)) {
818
                return getExtraColumnValue(name);
819
            }
820
            if (hasExtraValue(name)) {
821
                return getExtraValue(name);
822
            }
823
            // y si esta ahi return
824
            throw new IllegalArgumentException("Attribute name '" + name + "' not found in the feature.");
825
        }
826
        return this.get(index);
827
    }
828

    
829
    @Override
830
    public boolean isNull(int index) {
831
        FeatureType type = this.data.getType();
832
        if (index < 0 || index >= type.size()) {
833
            throw new IllegalArgumentException("Attribute index '" + index + "' out of range (0 to " + this.data.getType().size() + ".");
834
        }
835
        FeatureAttributeDescriptor attribute = type.getAttributeDescriptor(index);
836
        if (!this.data.getType().hasEvaluators()) {
837
            return this.data.get(index) == null;
838
        }
839
        Evaluator eval = attribute.getEvaluator();
840
        if (eval == null) {
841
            return this.data.get(index) == null;
842
        }
843
        Object value = this.data.get(index);
844
        if (value != null) {
845
            return true;
846
        }
847
        try {
848
            value = eval.evaluate(this);
849
        } catch (EvaluatorException e) {
850
            throw new DataEvaluatorRuntimeException(e);
851
        }
852
        this.data.set(index, value);
853
        return value == null;
854
    }
855

    
856
    @Override
857
    public boolean isNull(String name) {
858
        int index = this.data.getType().getIndex(name);
859
        if (index < 0) {
860
            throw new IllegalArgumentException("Attribute name '" + name + "' not found in the feature.");
861
        }
862
        return this.isNull(index);
863
    }
864

    
865
    public boolean has_key(String key) {
866
        Object x = this.getType().get(key);
867
        return x != null;
868
    }
869

    
870
    public List<String> keys() {
871
        List<String> ks = new ArrayList<>();
872
        for (FeatureAttributeDescriptor attr : this.getType()) {
873
            ks.add(attr.getName());
874
        }
875
        return ks;
876
    }
877

    
878
    public Iterator<String> iterkeys() {
879
        final Iterator it = this.getType().iterator();
880
        return new Iterator<String>() {
881
            @Override
882
            public boolean hasNext() {
883
                return it.hasNext();
884
            }
885

    
886
            @Override
887
            public String next() {
888
                return ((FeatureAttributeDescriptor) it.next()).getName();
889
            }
890

    
891
            @Override
892
            public void remove() {
893
                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
894
            }
895
        };
896
    }
897

    
898
    public Iterator iteritems() {
899
        final Iterator it = this.getType().iterator();
900
        return new Iterator<Map.Entry>() {
901
            @Override
902
            public boolean hasNext() {
903
                return it.hasNext();
904
            }
905

    
906
            @Override
907
            public Map.Entry next() {
908
                final String name = ((FeatureAttributeDescriptor) it.next()).getName();
909
                return new Map.Entry<String, Object>() {
910
                    @Override
911
                    public String getKey() {
912
                        return name;
913
                    }
914

    
915
                    @Override
916
                    public Object getValue() {
917
                        return get(name);
918
                    }
919

    
920
                    @Override
921
                    public Object setValue(Object value) {
922
                        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
923
                    }
924

    
925
                };
926
            }
927

    
928
            @Override
929
            public void remove() {
930
                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
931
            }
932
        };
933
    }
934

    
935
    @Override
936
    public Object get(int index) {
937
        FeatureType type = this.data.getType();
938
        if (index < 0 || index >= type.size()) {
939
            throw new IllegalArgumentException("Attribute index '" + index + "' out of range (0 to " + this.data.getType().size() + ".");
940
        }
941
        Object value = this.data.get(index);
942
        FeatureAttributeDescriptor attribute = type.getAttributeDescriptor(index);
943
        if (type.hasEvaluators()) {
944
            Evaluator eval = attribute.getEvaluator();
945
            if (eval != null) {
946
                if (value == null) { // Ya hemos calculado el campo ?
947
                    // FIXME: para comprobar si esta calculado usar un array especifico.
948
                    try {
949
                        value = eval.evaluate(this);
950
                    } catch (EvaluatorException e) {
951
                        throw new DataEvaluatorRuntimeException(e);
952
                    }
953
                    this.data.set(index, value);
954
                }
955
            }
956
        }
957
        value = get(attribute, value);
958
        return value;
959
    }
960

    
961
    private Object get(FeatureAttributeDescriptor featureAttributeDescriptor, Object value) {
962
        FeatureAttributeEmulator emulator = featureAttributeDescriptor.getFeatureAttributeEmulator();
963
        if (emulator != null) {
964
//            int index = featureAttributeDescriptor.getIndex();
965
//            value = this.data.get(index);
966
//            if( value==null ) {
967
            value = this.getExtraValue(featureAttributeDescriptor.getName());
968
            if (value==null) {
969
                value = emulator.get(this);
970
            }
971
//                this.data.set(index,value);
972
//            }
973
        } else {
974
            FeatureAttributeGetter getter = featureAttributeDescriptor.getFeatureAttributeGetter();
975
            if (getter != null) {
976
                value = getter.getter(value);
977
            }
978
        }
979
        value = coerce(featureAttributeDescriptor, value);
980
        return value;
981
    }
982
    
983
    private Object coerce(FeatureAttributeDescriptor attr, Object value) {
984
        if( value == null ) {
985
            return null;
986
        }
987
        DataType dataType = attr.getDataType();
988
        Class<? extends DataType> theClass = dataType.getDefaultClass();
989
        if (theClass != null && !theClass.isInstance(value)) {
990
            try {
991
                value = this.getDataTypesManager().coerce(dataType.getType(), value);
992
            } catch (CoercionException e) {
993
                throw new IllegalArgumentException(
994
                        "Can't convert to " + theClass.getSimpleName()
995
                        + " from '" + value==null? "NULL":value.getClass().getSimpleName()
996
                        + "' with value '" + Objects.toString(value) + "'.");
997
            }
998
        }
999
        if (attr.getType() == DataTypes.GEOMETRY) {
1000
            if (value != null) {
1001
                Geometry geom = (Geometry) value;
1002
                if (geom.getProjection() == null) {
1003
                    IProjection proj = ((DefaultFeatureAttributeDescriptor) attr).getSRS(this.storeRef);
1004
                    geom.setProjection(proj);
1005
                }
1006
            }
1007
        }
1008
        return value;
1009
    }
1010

    
1011
    @Override
1012
    public byte[] getByteArray(String name) {
1013
        return (byte[]) this.get(name);
1014
    }
1015

    
1016
    @Override
1017
    public byte[] getByteArray(int index) {
1018
        return (byte[]) this.get(index);
1019
    }
1020

    
1021
    @Override
1022
    public Object[] getArray(String name) {
1023
        return (Object[]) this.get(name);
1024
    }
1025

    
1026
    @Override
1027
    public Object[] getArray(int index) {
1028
        return (Object[]) this.get(index);
1029
    }
1030

    
1031
    @Override
1032
    public boolean getBoolean(String name) {
1033
        Boolean value = ((Boolean) this.get(name, Boolean.class, DataTypes.BOOLEAN));
1034
        if (value == null) {
1035
            return false;
1036
        }
1037
        return value;
1038
    }
1039

    
1040
    @Override
1041
    public boolean getBoolean(int index) {
1042
        Boolean value = ((Boolean) this.get(index, Boolean.class, DataTypes.BOOLEAN));
1043
        if (value == null) {
1044
            return false;
1045
        }
1046
        return value;
1047
    }
1048

    
1049
    @Override
1050
    public byte getByte(String name) {
1051
        Byte value = ((Byte) this.get(name, Byte.class, DataTypes.BYTE));
1052
        if (value == null) {
1053
            return 0;
1054
        }
1055
        return value;
1056
    }
1057

    
1058
    @Override
1059
    public byte getByte(int index) {
1060
        Byte value = ((Byte) this.get(index, Byte.class, DataTypes.BYTE));
1061
        if (value == null) {
1062
            return 0;
1063
        }
1064
        return value;
1065
    }
1066

    
1067
    @Override
1068
    public java.sql.Date getDate(String name) {
1069
        java.sql.Date value = ((java.sql.Date) this.get(name, java.sql.Date.class, DataTypes.DATE));
1070
        return value;
1071
    }
1072

    
1073
    @Override
1074
    public java.sql.Date getDate(int index) {
1075
        java.sql.Date value = ((java.sql.Date) this.get(index, java.sql.Date.class, DataTypes.DATE));
1076
        return value;
1077
    }
1078

    
1079
    @Override
1080
    public java.sql.Time getTime(String name) {
1081
        java.sql.Time value = ((java.sql.Time) this.get(name, java.sql.Time.class, DataTypes.TIME));
1082
        return value;
1083
    }
1084

    
1085
    @Override
1086
    public java.sql.Time getTime(int index) {
1087
        java.sql.Time value = ((java.sql.Time) this.get(index, java.sql.Time.class, DataTypes.TIME));
1088
        return value;
1089
    }
1090

    
1091
    @Override
1092
    public java.sql.Timestamp getTimestamp(String name) {
1093
        java.sql.Timestamp value = ((java.sql.Timestamp) this.get(name, java.sql.Timestamp.class, DataTypes.TIMESTAMP));
1094
        return value;
1095
    }
1096

    
1097
    @Override
1098
    public java.sql.Timestamp getTimestamp(int index) {
1099
        java.sql.Timestamp value = ((java.sql.Timestamp) this.get(index, java.sql.Timestamp.class, DataTypes.TIMESTAMP));
1100
        return value;
1101
    }
1102

    
1103
    @Override
1104
    public double getDouble(String name) {
1105
        Double value = ((Double) this.get(name, Double.class, DataTypes.DOUBLE));
1106
        if (value == null) {
1107
            return 0;
1108
        }
1109
        return value;
1110
    }
1111

    
1112
    @Override
1113
    public double getDouble(int index) {
1114

    
1115
        Double value = ((Double) this.get(index, Double.class, DataTypes.DOUBLE));
1116
        if (value == null) {
1117
            return 0;
1118
        }
1119
        return value;
1120
    }
1121

    
1122
    @Override
1123
    public BigDecimal getDecimal(String name) {
1124
        BigDecimal value = ((BigDecimal) this.get(name, BigDecimal.class, DataTypes.DECIMAL));
1125
        return value;
1126
    }
1127

    
1128
    @Override
1129
    public BigDecimal getDecimal(int index) {
1130
        BigDecimal value = ((BigDecimal) this.get(index, BigDecimal.class, DataTypes.DECIMAL));
1131
        return value;
1132
    }
1133

    
1134
    @Override
1135
    public Feature getFeature(String name) {
1136
        return this.getFeature(this.data.getType().getIndex(name));
1137
    }
1138

    
1139
    @Override
1140
    public Feature getFeature(int index) {
1141
        return (Feature) this.get(index);
1142
    }
1143

    
1144
    @Override
1145
    public float getFloat(String name) {
1146
        Float value = ((Float) this.get(name, Float.class, DataTypes.FLOAT));
1147
        if (value == null) {
1148
            return 0;
1149
        }
1150
        return value;
1151
    }
1152

    
1153
    @Override
1154
    public float getFloat(int index) {
1155
        Float value = ((Float) this.get(index, Float.class, DataTypes.FLOAT));
1156
        if (value == null) {
1157
            return 0;
1158
        }
1159
        return value;
1160
    }
1161

    
1162
    @Override
1163
    public Geometry getGeometry(String name) {
1164
        return (Geometry) this.get(name, Geometry.class, DataTypes.GEOMETRY);
1165
    }
1166

    
1167
    @Override
1168
    public Geometry getGeometry(int index) {
1169
        return (Geometry) this.get(index, Geometry.class, DataTypes.GEOMETRY);
1170
    }
1171

    
1172
    @Override
1173
    public int getInt(String name) {
1174
        Integer value = ((Integer) this.get(name, Integer.class, DataTypes.INT));
1175
        if (value == null) {
1176
            return 0;
1177
        }
1178
        return value;
1179
    }
1180

    
1181
    @Override
1182
    public int getInt(int index) {
1183
        Integer value = ((Integer) this.get(index, Integer.class, DataTypes.INT));
1184
        if (value == null) {
1185
            return 0;
1186
        }
1187
        return value;
1188
    }
1189

    
1190
    @Override
1191
    public long getLong(String name) {
1192
        Long value = ((Long) this.get(name, Long.class, DataTypes.LONG));
1193
        if (value == null) {
1194
            return 0;
1195
        }
1196
        return value;
1197
    }
1198

    
1199
    @Override
1200
    public long getLong(int index) {
1201
        Long value = ((Long) this.get(index, Long.class, DataTypes.LONG));
1202
        if (value == null) {
1203
            return 0;
1204
        }
1205
        return value;
1206
    }
1207

    
1208
    @Override
1209
    public String getString(String name) {
1210
        return (String) this.get(name, String.class, DataTypes.STRING);
1211
    }
1212

    
1213
    @Override
1214
    public String getString(int index) {
1215
        return (String) this.get(index, String.class, DataTypes.STRING);
1216
    }
1217

    
1218
    @Override
1219
    public Object getContextValue(String name) {
1220
        name = name.toLowerCase();
1221
        if (name.equals("store")) {
1222
            return this.getStore();
1223
        }
1224

    
1225
        if (name.equals("featuretype")) {
1226
            return this.data.getType();
1227
        }
1228

    
1229
        if (name.equals("feature")) {
1230
            return this;
1231
        }
1232

    
1233
        throw new IllegalArgumentException(name);
1234
    }
1235

    
1236
    @Override
1237
    public Iterator getDataNames() {
1238
        class DataNamesIterator implements Iterator {
1239

    
1240
            Iterator attributeIteraror;
1241

    
1242
            DataNamesIterator(DefaultFeature feature) {
1243
                this.attributeIteraror = feature.getType().iterator();
1244
            }
1245

    
1246
            @Override
1247
            public boolean hasNext() {
1248
                return this.attributeIteraror.hasNext();
1249
            }
1250

    
1251
            @Override
1252
            public Object next() {
1253
                return ((FeatureAttributeDescriptor) this.attributeIteraror
1254
                        .next()).getName();
1255
            }
1256

    
1257
            @Override
1258
            public void remove() {
1259
                throw new UnsupportedOperationException();
1260
            }
1261

    
1262
        }
1263
        return new DataNamesIterator(this);
1264
    }
1265

    
1266
    @Override
1267
    public Object getDataValue(String name) {
1268
        name = name.toLowerCase();
1269
        try {
1270
            return get(name);
1271
        } catch (IllegalArgumentException ex) {
1272
            if ("defaultgeometry".equalsIgnoreCase(name)) {
1273
                return this.getDefaultGeometry();
1274
            }
1275
            throw ex;
1276
        }
1277
    }
1278

    
1279
    @Override
1280
    public Iterator getDataValues() {
1281
        class DataValuesIterator implements Iterator {
1282

    
1283
            DefaultFeature feature;
1284
            int current = 0;
1285

    
1286
            DataValuesIterator(DefaultFeature feature) {
1287
                this.feature = feature;
1288
            }
1289

    
1290
            @Override
1291
            public boolean hasNext() {
1292
                return current < feature.getType().size() - 1;
1293
            }
1294

    
1295
            @Override
1296
            public Object next() {
1297
                return feature.get(current++);
1298
            }
1299

    
1300
            @Override
1301
            public void remove() {
1302
                throw new UnsupportedOperationException();
1303
            }
1304

    
1305
        }
1306
        return new DataValuesIterator(this);
1307
    }
1308

    
1309
    @Override
1310
    public boolean hasContextValue(String name) {
1311
        name = name.toLowerCase();
1312
        if (name.equals("store")) {
1313
            return true;
1314
        }
1315

    
1316
        if (name.equals("featuretype")) {
1317
            return true;
1318
        }
1319

    
1320
        return name.equals("feature");
1321
    }
1322

    
1323
    @Override
1324
    public boolean hasDataValue(String name) {
1325
        return this.hasValue(name);
1326
    }
1327

    
1328
//    @Override
1329
//    public Time getTime(int index) {
1330
//        return ((Time) this.get(index,Time.class,DataTypes.INSTANT));
1331
//    }
1332
//
1333
//    @Override
1334
//    public Time getTime(String name) {
1335
//        return this.getInstant(this.data.getType().getIndex(name));
1336
//    }
1337
//
1338
//    @Override
1339
//    public Instant getInstant(int index) {
1340
//        return ((Instant) this.get(index,Instant.class,DataTypes.INSTANT));
1341
//    }
1342
//
1343
//    @Override
1344
//    public Instant getInstant(String name) {
1345
//        return this.getInstant(this.data.getType().getIndex(name));
1346
//    }
1347
//
1348
//    @Override
1349
//    public Interval getInterval(int index) {
1350
//        return ((Interval) this.get(index,Interval.class,DataTypes.INTERVAL));
1351
//    }
1352
//
1353
//    @Override
1354
//    public Interval getInterval(String name) {
1355
//        return this.getInterval(this.data.getType().getIndex(name));
1356
//    }
1357
//
1358
    @Override
1359
    public DynObject getAsDynObject() {
1360
        DynObjectFeatureFacade facade = new DynObjectFeatureFacade(this);
1361
        return facade;
1362
    }
1363

    
1364
    @Override
1365
    public String toString() {
1366
        StringBuilder builder = new StringBuilder();
1367
        FeatureAttributeDescriptor[] attributeDescriptors
1368
                = getType().getAttributeDescriptors();
1369
        for (int i = 0; i < attributeDescriptors.length; i++) {
1370
            String name = attributeDescriptors[i].getName();
1371
            Object value = get(name);
1372
            builder.append(value);
1373
            if (i < attributeDescriptors.length - 1) {
1374
                builder.append(", ");
1375
            }
1376
        }
1377
        return builder.toString();
1378
    }
1379

    
1380
    /**
1381
     * It is a new feature that has already been inserted into the store but has not yet been saved to disk
1382
     * 
1383
     * @return the inserted
1384
     */
1385
    public boolean isInserted() {
1386
        return inserted;
1387
    }
1388

    
1389
    /**
1390
     * If true, marks the feature as already inserted in the vault but has not yet been saved to disk
1391
     * 
1392
     * @param inserted the inserted to set
1393
     */
1394
    public void setInserted(boolean inserted) {
1395
        this.inserted = inserted;
1396
//        this.data.setNew(!inserted);
1397
    }
1398

    
1399
    @Override
1400
    public EvaluatorData getEvaluatorData() {
1401
        return this;
1402
    }
1403

    
1404
    @Override
1405
    public int size() {
1406
        return this.data.getType().size();
1407
    }
1408

    
1409
    public boolean isEmpty() {
1410
        return false;
1411
    }
1412

    
1413
    public Iterator<String> iterator() {
1414
        final Iterator<FeatureAttributeDescriptor> x = this.data.getType().iterator();
1415
        return new Iterator<String>() {
1416
            @Override
1417
            public boolean hasNext() {
1418
                return x.hasNext();
1419
            }
1420

    
1421
            @Override
1422
            public String next() {
1423
                return x.next().getName();
1424
            }
1425
        };
1426
    }
1427

    
1428
    public boolean containsKey(String key) {
1429
        return this.data.getType().get(key) != null;
1430
    }
1431

    
1432
    @Override
1433
    public String getLabelOfValue(String name) {
1434
        FeatureAttributeDescriptor attrdesc = this.data.getType().getAttributeDescriptor(name);
1435
        Object value;
1436
        if (attrdesc == null) { // extra column
1437
            FeatureExtraColumns extraColumns = this.data.getType().getExtraColumns();
1438
            if (extraColumns==null) {
1439
                return name;
1440
            }
1441
            attrdesc = extraColumns.get(name);
1442
            if(attrdesc==null) {
1443
                return name;
1444
            }
1445
           value = this.get(name);
1446
        } else {
1447
           value = this.get(attrdesc.getIndex());
1448
        }
1449
        String label;
1450
        try {
1451
            label = attrdesc.getLabelOfValue(value);
1452
        } catch(Throwable th) {
1453
            label = Objects.toString(value, "");
1454
        }
1455
        return label;
1456
    }
1457

    
1458
    @Override
1459
    public void setExtraValue(String name, Object value) {
1460
        if (this.extraValues == null) {
1461
            this.extraValues = new HashMap<>();
1462
        }
1463
        this.extraValues.put(name, value);
1464
    }
1465

    
1466
    public Object getExtraColumnValue(String name) {
1467
        Object value;
1468
        FeatureExtraColumns columns = this.getType().getExtraColumns();
1469
        int index = columns.getIndexOf(name);
1470
        if (this.extraValues != null) {
1471
            if (this.extraValues.containsKey(name)) {
1472
                value = this.extraValues.get(name);
1473
                if (index >= 0){
1474
                    EditableFeatureAttributeDescriptor attrdesc = columns.get(index);
1475
                    value = coerce(attrdesc, value);
1476
                }
1477
                return value;
1478
            }
1479
        }
1480
        if (index < 0) {
1481
            throw new RuntimeException("Not extra column value found");
1482
        }
1483
        if (extraValuesData == null) {
1484
            extraValuesData = new Object[columns.size()];
1485
        }
1486
        EditableFeatureAttributeDescriptor attrdesc = columns.get(index);
1487
        value = extraValuesData[index];
1488
        if (value != null) {
1489
            return value;
1490
        }
1491
        value = this.getExtraValue(name);
1492
        if (value == null && !this.hasExtraValue(name) && attrdesc.getFeatureAttributeEmulator() != null) {
1493
            value = attrdesc.getFeatureAttributeEmulator().get(this);
1494
            value = coerce(attrdesc, value);
1495
            extraValuesData[index] = value;
1496
        } else {
1497
            value = coerce(attrdesc, value);
1498
            extraValuesData[index] = value;
1499
        }
1500
        
1501
        return value;
1502
    }
1503

    
1504
    @Override
1505
    public Object getExtraValue(String name) {
1506
        Object value = this.data.getExtraValue(name);
1507
        return value;
1508
    }
1509

    
1510
    @Override
1511
    public boolean hasExtraValue(String name) {
1512
        return this.data.hasExtraValue(name);
1513
    }
1514

    
1515
    private boolean hasExtraColumnValue(String name) {
1516
        if (this.extraValues != null) {
1517
            if (this.extraValues.containsKey(name)) {
1518
                return true;
1519
            }
1520
        }
1521
        FeatureExtraColumns columns = this.getType().getExtraColumns();
1522
        int index = columns.getIndexOf(name);
1523
        if (index >= 0) {
1524
            return true;
1525
        }
1526
        return false;
1527
    }
1528

    
1529
    private EditableFeatureAttributeDescriptor getExtraColumn(String name) {
1530
        FeatureExtraColumns columns = this.getType().getExtraColumns();
1531
        return columns.get(name);
1532
    }
1533

    
1534
    @Override
1535
    public boolean hasValue(String name) {
1536
        name = name.toLowerCase();
1537
        return this.data.getType().getIndex(name) >= 0
1538
                || hasExtraValue(name)
1539
                || hasExtraColumnValue(name);
1540
    }
1541

    
1542
    @Override
1543
    public Object getExtraValue(int index) {
1544
        return this.data.getExtraValue(index);
1545
    }
1546

    
1547
    @Override
1548
    public JsonObject toJson() {
1549
        JsonObjectBuilder builder = this.toJsonBuilder();
1550
        return builder.build();
1551
    }
1552
    
1553
    public JsonObject toJson(Map<String, Object> props) {
1554
        JsonObjectBuilder builder = this.toJsonBuilder(props);
1555
        return builder.build();
1556
    }
1557

    
1558
    @Override
1559
    public JsonObjectBuilder toJsonBuilder() {
1560
        return this.toJsonBuilder(null, Bitmask.createBitmask(TOJSON_MODE_SHALLOW), null);
1561
//        JsonObjectBuilder builder = Json.createObjectBuilder();
1562
//        Date date;
1563
//        Geometry geom;
1564
//        FeatureType ft = this.getType();
1565
//        for (FeatureAttributeDescriptor desc : ft) {
1566
//            if (desc.isComputed()) {
1567
//                continue;
1568
//            }
1569
//            if(this.isNull(desc.getName())){
1570
//                builder.addNull(desc.getName());
1571
//                continue;
1572
//            }
1573
//            switch (desc.getType()) {
1574
//                case DataTypes.GEOMETRY:
1575
//                    geom = this.getGeometry(desc.getIndex());
1576
//                    if (geom != null) {
1577
//                        builder.add(desc.getName(), geom.convertToHexWKBQuietly());
1578
//                    }
1579
//                    break;
1580
//                case DataTypes.BOOLEAN:
1581
//                    builder.add(desc.getName(), this.getBoolean(desc.getIndex()));
1582
//                    break;
1583
//                case DataTypes.BYTE:
1584
//                    builder.add(desc.getName(), this.getByte(desc.getIndex()));
1585
//                    break;
1586
//                case DataTypes.INT:
1587
//                    builder.add(desc.getName(), this.getInt(desc.getIndex()));
1588
//                    break;
1589
//                case DataTypes.LONG:
1590
//                    builder.add(desc.getName(), this.getLong(desc.getIndex()));
1591
//                    break;
1592
//                case DataTypes.DOUBLE:
1593
//                    builder.add(desc.getName(), this.getDouble(desc.getIndex()));
1594
//                    break;
1595
//                case DataTypes.FLOAT:
1596
//                    builder.add(desc.getName(), this.getFloat(desc.getIndex()));
1597
//                    break;
1598
//                case DataTypes.DECIMAL:
1599
//                    builder.add(desc.getName(), this.getDecimal(desc.getIndex()));
1600
//                    break;
1601
//                case DataTypes.DATE:
1602
//                    // Format date as ISO 8601
1603
//                    date = this.getDate(desc.getIndex());
1604
//                    if (date == null) {
1605
//                        builder.addNull(desc.getName());
1606
//                    } else {
1607
//                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1608
//                        String value = DateTimeFormatter.ISO_DATE.format(localDateTime);
1609
//                        builder.add(desc.getName(), value);
1610
//                    }
1611
//                    break;
1612
//                case DataTypes.TIMESTAMP:
1613
//                    // Format date as ISO 8601
1614
//                    date = this.getTimestamp(desc.getIndex());
1615
//                    if (date == null) {
1616
//                        builder.addNull(desc.getName());
1617
//                    } else {
1618
//                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1619
//                        String value = DateTimeFormatter.ISO_DATE_TIME.format(localDateTime);
1620
//                        builder.add(desc.getName(), value);
1621
//                    }
1622
//                    break;
1623
//                case DataTypes.TIME:
1624
//                    // Format date as ISO 8601
1625
//                    date = this.getTime(desc.getIndex());
1626
//                    if (date == null) {
1627
//                        builder.addNull(desc.getName());
1628
//                    } else {
1629
//                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1630
//                        String value = DateTimeFormatter.ISO_TIME.format(localDateTime);
1631
//                        builder.add(desc.getName(), value);
1632
//                    }
1633
//                    break;
1634
//                default:
1635
//                    builder.add(desc.getName(), this.getStringOrDefault(desc.getIndex(), ""));
1636
//            }
1637
//        }
1638
//        return builder;
1639
    }
1640

    
1641
    public static final String TOJSON_MODE = "mode";
1642
    public static final String FORMAT_MODE = "format";
1643
    
1644
    public static final String FORMAT_MODE_GVSIGDESKTOP = "gvsigdesktop";
1645
    public static final String FORMAT_MODE_GEOJSON = "geojson";
1646
    
1647
    public static final int TOJSON_MODE_SHALLOW = 0;
1648
    public static final int TOJSON_MODE_DEEP =        0b00001;
1649
    public static final int TOJSON_MODE_COMPUTEDS =   0b00010;
1650
    public static final int TOJSON_MODE_COLLECTIONS = 0b00100;
1651
    
1652
    @Override
1653
    public JsonObjectBuilder toJsonBuilder(Map<String, Object> props) {    
1654
        if( props == null ) {
1655
            return toJsonBuilderDefault(props);
1656
        }
1657
        String format = (String) props.getOrDefault(FORMAT_MODE,FORMAT_MODE_GVSIGDESKTOP);
1658
        switch(format.toLowerCase()) {
1659
            case FORMAT_MODE_GEOJSON:
1660
                return toJsonBuilderGeoJson(props);
1661
            case FORMAT_MODE_GVSIGDESKTOP:
1662
            default:
1663
                return toJsonBuilderDefault(props);
1664
        }
1665
    }
1666
    
1667
    public JsonObjectBuilder toJsonBuilderDefault(Map<String, Object> props) {    
1668
        Bitmask modemask = null;
1669
        Set<String> visiteds = null;
1670
        int mode = TOJSON_MODE_SHALLOW;
1671
        if( props!=null ) {
1672
            mode = (int) props.getOrDefault(TOJSON_MODE,mode);
1673
            modemask = Bitmask.createBitmask(mode);
1674
            if( modemask.isSetBits(TOJSON_MODE_DEEP) ) {
1675
                visiteds = (Set<String>) props.getOrDefault("visiteds", null);
1676
                if( visiteds == null ) {
1677
                    visiteds = new HashSet<>();
1678
                    props.put("visiteds", visiteds);
1679
                }
1680
            }
1681
        } else {
1682
            modemask = Bitmask.createBitmask(mode);
1683
        }
1684
        return this.toJsonBuilder(props, modemask, visiteds);
1685
    }
1686
    
1687
    private JsonObjectBuilder toJsonBuilderGeoJson(Map<String, Object> props) {    
1688
        FeatureType ft = this.getType();
1689
        Geometry geom = this.getDefaultGeometry();
1690
        
1691
        Bitmask mode = Bitmask.createBitmask((int) props.getOrDefault(TOJSON_MODE,TOJSON_MODE_SHALLOW));
1692
        boolean skipNulls = (boolean) props.getOrDefault("skipnulls", false);
1693
        String[] skipFields = (String[]) props.getOrDefault("skipfields", null);
1694
        Map<String,Object>properties = new HashMap<>();
1695
        
1696
        for (FeatureAttributeDescriptor desc : ft) {
1697
            if (desc.isComputed() ) { 
1698
                if( !mode.isSetBits(TOJSON_MODE_COMPUTEDS) ) {
1699
                    continue;
1700
                }
1701
            }
1702
            if(ArrayUtils.contains(skipFields, desc.getName())) {
1703
                continue;
1704
            }
1705
            
1706
            if(desc.getType() == DataTypes.GEOMETRY){
1707
                continue;
1708
            }
1709
            
1710
            if( this.isNull(desc.getName()) ){
1711
                if(!skipNulls){
1712
                    properties.put(desc.getName(),null);
1713
                }
1714
                continue;
1715
            }
1716
            switch (desc.getType()) {
1717
                case DataTypes.BOOLEAN:
1718
                    properties.put(desc.getName(), this.getBoolean(desc.getIndex()));
1719
                    break;
1720
                case DataTypes.BYTE:
1721
                    properties.put(desc.getName(), this.getByte(desc.getIndex()));
1722
                    break;
1723
                case DataTypes.INT:
1724
                    properties.put(desc.getName(), this.getInt(desc.getIndex()));
1725
                    break;
1726
                case DataTypes.LONG:
1727
                    properties.put(desc.getName(), this.getLong(desc.getIndex()));
1728
                    break;
1729
                case DataTypes.DOUBLE:
1730
                    properties.put(desc.getName(), this.getDouble(desc.getIndex()));
1731
                    break;
1732
                case DataTypes.FLOAT:
1733
                    properties.put(desc.getName(), this.getFloat(desc.getIndex()));
1734
                    break;
1735
                case DataTypes.DECIMAL:
1736
                    properties.put(desc.getName(), this.getDecimal(desc.getIndex()));
1737
                    break;
1738
                case DataTypes.DATE: {
1739
                        // Format date as ISO 8601
1740
                        java.sql.Date date = this.getDate(desc.getIndex());
1741
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1742
                        String value = DateTimeFormatter.ISO_DATE.format(localDateTime);
1743
                        properties.put(desc.getName(), value);
1744
                    }
1745
                    break;
1746
                case DataTypes.TIMESTAMP: {
1747
                        // Format date as ISO 8601
1748
                        java.sql.Date date = this.getDate(desc.getIndex());
1749
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1750
                        String value = DateTimeFormatter.ISO_DATE_TIME.format(localDateTime);
1751
                        properties.put(desc.getName(), value);
1752
                    }
1753
                    break;
1754
                case DataTypes.TIME: {
1755
                        // Format date as ISO 8601
1756
                        java.sql.Date date = this.getDate(desc.getIndex());
1757
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1758
                        String value = DateTimeFormatter.ISO_TIME.format(localDateTime);
1759
                        properties.put(desc.getName(), value);
1760
                    }
1761
                    break;
1762

    
1763
                    
1764
                default:
1765
                    properties.put(desc.getName(), this.getStringOrDefault(desc.getIndex(), ""));
1766
            }
1767
        }
1768
        String json_s;
1769
        try {
1770
            json_s = geom.convertToGeoJson(properties, (boolean) props.getOrDefault("encodecrs", true));
1771
        } catch (Exception ex) {
1772
            throw new ConvertFeatureToGeoJsonException(ex);
1773
        }
1774
        JsonObjectBuilder json = Json.createObjectBuilder(json_s);
1775
        return json;
1776
        
1777
    }
1778
    
1779
    private static class ConvertFeatureToGeoJsonException extends DataRuntimeException {
1780

    
1781
        private static final long serialVersionUID = -1;
1782
        private final static String MESSAGE_FORMAT = "Create GeoJson builder exception.";
1783
        private final static String MESSAGE_KEY = "_ConvertFeatureToGeoJsonException";
1784

    
1785
        public ConvertFeatureToGeoJsonException(Throwable cause) {
1786
            super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
1787
        }
1788

    
1789
    }
1790

    
1791
    private JsonObjectBuilder toJsonBuilder(Map<String, Object> props, Bitmask mode, Set<String> visiteds) {    
1792
        JsonObjectBuilder builder = Json.createObjectBuilder();
1793
        Date date;
1794
        Geometry geom;
1795
        FeatureType ft = this.getType();
1796
        
1797
        boolean hasVisited = false;
1798
        if(visiteds != null){
1799
            FeatureReference ref = this.getReference();
1800
            String code = ref.getCode();
1801
            hasVisited = visiteds.contains(code);
1802
            if(hasVisited){
1803
               return this.featureReferenceToJson(ref);
1804
            }
1805
            visiteds.add(code);
1806
        }
1807
        
1808
        for (FeatureAttributeDescriptor desc : ft) {
1809
            if (desc.isComputed() ) { 
1810
                if( !mode.isSetBits(TOJSON_MODE_COMPUTEDS) ) {
1811
                    if( !(mode.isSetBits(TOJSON_MODE_COLLECTIONS) && desc.getType()==DataTypes.LIST) ) { 
1812
                        continue;
1813
                    }
1814
                }
1815
            }
1816
            if(desc.getType() != DataTypes.LIST && this.isNull(desc.getName()) ){
1817
                builder.addNull(desc.getName());
1818
                continue;
1819
            }
1820
            if( desc.isForeingKey() && !desc.getForeingKey().isClosedList() && mode.isSetBits(TOJSON_MODE_DEEP) ) {
1821
                if( desc.getRelationType()==DynField.RELATION_TYPE_COLLABORATION_WITH_COMPOSITION) {
1822
                    Object value = this.get(desc.getName());
1823
                    if(value != null){
1824
                        Feature f = desc.getForeingKey().getFeature(null, value);
1825
                        if(f!=null){ //Ojo, si no hay constraint no tendria porque ser un error que no se encontrara la feature
1826
                            String x = f.getReference().getCode();
1827
                            if( visiteds==null ) {
1828
                                if( f instanceof DefaultFeature ) {
1829
                                    builder.add(desc.getName(), ((DefaultFeature)f).toJsonBuilder(props, mode, visiteds));
1830
                                } else {
1831
                                    builder.add(desc.getName(), f.toJsonBuilder(props));
1832
                                }
1833
                                continue;
1834
                            } else if( !visiteds.contains(x) ) {
1835
                                if( f instanceof DefaultFeature ) {
1836
                                    builder.add(desc.getName(), ((DefaultFeature)f).toJsonBuilder(props, mode, visiteds));
1837
                                } else {
1838
                                    builder.add(desc.getName(), f.toJsonBuilder(props));
1839
                                }
1840
                                continue;
1841
                            }                           
1842
                        }
1843
                    }
1844
                }
1845
            }
1846
            switch (desc.getType()) {
1847
                case DataTypes.GEOMETRY:
1848
                    geom = this.getGeometry(desc.getIndex());
1849
                    if (geom != null) {
1850
                        geom = this.getGeometry(desc.getIndex());
1851
                        Tags tags = desc.getTags();
1852
                        switch(tags.getString("geomformat", "WKB").toUpperCase()) {
1853
                            default:
1854
                            case "WKB":
1855
                            case "HEXWKB":
1856
                                builder.add(desc.getName(), geom.convertToHexWKBQuietly());
1857
                                break;
1858
                            case "EWKB":
1859
                            case "HEXEWKB":
1860
                                builder.add(desc.getName(), geom.convertToHexEWKBQuietly());
1861
                                break;
1862
                            case "WKT":
1863
                                builder.add(desc.getName(), geom.convertToWKTQuietly());
1864
                                break;
1865
                        }
1866
                    }
1867
                    break;
1868

    
1869
                case DataTypes.BOOLEAN:
1870
                    builder.add(desc.getName(), this.getBoolean(desc.getIndex()));
1871
                    break;
1872
                case DataTypes.BYTE:
1873
                    builder.add(desc.getName(), this.getByte(desc.getIndex()));
1874
                    break;
1875
                case DataTypes.INT:
1876
                    builder.add(desc.getName(), this.getInt(desc.getIndex()));
1877
                    break;
1878
                case DataTypes.LONG:
1879
                    builder.add(desc.getName(), this.getLong(desc.getIndex()));
1880
                    break;
1881
                case DataTypes.DOUBLE:
1882
                    builder.add(desc.getName(), this.getDouble(desc.getIndex()));
1883
                    break;
1884
                case DataTypes.FLOAT:
1885
                    builder.add(desc.getName(), this.getFloat(desc.getIndex()));
1886
                    break;
1887
                case DataTypes.DECIMAL:
1888
                    builder.add(desc.getName(), this.getDecimal(desc.getIndex()));
1889
                    break;
1890
                case DataTypes.DATE:
1891
                    // Format date as ISO 8601
1892
                    date = this.getDate(desc.getIndex());
1893
                    if (date == null) {
1894
                        builder.addNull(desc.getName());
1895
                    } else {
1896
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1897
                        String value = DateTimeFormatter.ISO_DATE.format(localDateTime);
1898
                        builder.add(desc.getName(), value);
1899
                    }
1900
                    break;
1901
                case DataTypes.TIMESTAMP:
1902
                    // Format date as ISO 8601
1903
                    date = this.getTimestamp(desc.getIndex());
1904
                    if (date == null) {
1905
                        builder.addNull(desc.getName());
1906
                    } else {
1907
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1908
                        String value = DateTimeFormatter.ISO_DATE_TIME.format(localDateTime);
1909
                        builder.add(desc.getName(), value);
1910
                    }
1911
                    break;
1912
                case DataTypes.TIME:
1913
                    // Format date as ISO 8601
1914
                    date = this.getTime(desc.getIndex());
1915
                    if (date == null) {
1916
                        builder.addNull(desc.getName());
1917
                    } else {
1918
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1919
                        String value = DateTimeFormatter.ISO_TIME.format(localDateTime);
1920
                        builder.add(desc.getName(), value);
1921
                    }
1922
                    break;
1923
                case DataTypes.LIST: 
1924
                    if( mode.isSetBits(TOJSON_MODE_COLLECTIONS) ) {
1925
                        if( desc.getRelationType()== DynField.RELATION_TYPE_AGGREGATE_WITH_COMPOSITION) {
1926
                            JsonArrayBuilder arraybuilder = Json.createArrayBuilder();
1927
                            Object x = this.get(desc.getName());
1928
                            if( x instanceof List ) {
1929
                                for (Object v : (List)x) {
1930
                                    if( v instanceof DefaultFeature ) {
1931
                                            arraybuilder.add(((DefaultFeature)v).toJsonBuilder(props, mode, visiteds));
1932
                                    } else if( v instanceof Feature ) {
1933
                                        arraybuilder.add(((Feature)v).toJsonBuilder(props));
1934
                                    }
1935
                                }
1936
                            }
1937
                            DisposeUtils.disposeQuietly(x);
1938
                            builder.add(desc.getName(), arraybuilder);
1939
                        }
1940
                    }
1941
                    break;
1942
                    
1943
                default:
1944
                    builder.add(desc.getName(), this.getStringOrDefault(desc.getIndex(), ""));
1945
            }
1946
        }
1947
        return builder;
1948
    }
1949
    
1950
    private JsonObjectBuilder featureReferenceToJson(FeatureReference ref){
1951
        JsonObjectBuilder builder = Json.createObjectBuilder();
1952
        builder.add("$reference", ref.toJsonBuilder());
1953
        return builder;
1954
    }
1955

    
1956
    @Override
1957
    public List<String> getKeys() {
1958
        List<String> l = new ArrayList<>();
1959
        for (FeatureAttributeDescriptor descriptor : this.getType()) {
1960
            l.add(descriptor.getName());
1961
        }
1962
        return l;
1963
    }
1964

    
1965
    @Override
1966
    public String format(String name) {
1967
        int index = this.data.getType().getIndex(name);
1968
        if (index < 0) {
1969
            // buscamos en los extra cols
1970
            FeatureAttributeDescriptor attribute = this.getExtraColumn(name);
1971
            if( attribute!=null ) {
1972
                Object value = getExtraColumnValue(name);
1973
                return attribute.format(value);
1974
            }
1975
            throw new IllegalArgumentException("Attribute name '" + name + "' not found in the feature.");
1976
        }
1977
        return this.format(index);
1978
    }
1979

    
1980
    @Override
1981
    public String format(int index) {
1982
        Object value = this.get(index);
1983
        FeatureType type = this.data.getType();
1984
        FeatureAttributeDescriptor attribute = type.getAttributeDescriptor(index);
1985
        return attribute.format(value);
1986
    }
1987
    
1988
    @Override
1989
    public Feature getForeignFeature(String attrName) {
1990
        ForeingKey.ContextForeingKey context = null;
1991
        try {
1992
            FeatureAttributeDescriptor attr = this.getType().getAttributeDescriptor(attrName);
1993
            if (attr == null) {
1994
                return null;
1995
            }
1996
            if (!attr.isForeingKey()) {
1997
                return null;
1998
            }
1999
            ForeingKey fk = attr.getForeingKey();
2000
            if(fk == null){
2001
                return null;
2002
            }
2003
            context = fk.createContext();
2004
            FeatureStore store = this.getStore();
2005
            if(store instanceof SupportTransactions){
2006
                context.setTransaction(((SupportTransactions) store).getTransaction());
2007
            }
2008
            Feature foreignfeat = fk.getFeature(context, this.get(attrName));
2009
            return foreignfeat;
2010
        } catch (Exception ex) {
2011
            return null;
2012
        } finally {
2013
            DisposeUtils.disposeQuietly(context);
2014
        }
2015
    }
2016

    
2017

    
2018
    @Override
2019
    public Expression createFilter() {
2020
        FeatureType ftype = this.getType();
2021
        FeatureAttributeDescriptor[] pk = ftype.getPrimaryKey();
2022
        if( ArrayUtils.isEmpty(pk) ) {
2023
            return null;
2024
        }
2025
        ExpressionBuilder builder = ExpressionUtils.createExpressionBuilder();
2026
        for (FeatureAttributeDescriptor attrdesc : pk) {
2027
            Object value = this.get(attrdesc.getName());
2028
            if( value == null ) {
2029
                builder.and(
2030
                        builder.is_null(builder.column(attrdesc.getName()))
2031
                );
2032
            } else {
2033
                builder.and(
2034
                        builder.eq(
2035
                                builder.column(attrdesc.getName()),
2036
                                builder.constant(value)
2037
                        )
2038
                );
2039
            }
2040
        }
2041
        Expression filter = ExpressionUtils.createExpression(builder.toString());                            
2042
        return filter;
2043
    }
2044
}