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 @ 47779

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 org.apache.commons.lang3.StringUtils;
45
import org.cresques.cts.IProjection;
46
import org.gvsig.expressionevaluator.Expression;
47
import org.gvsig.expressionevaluator.ExpressionBuilder;
48
import org.gvsig.expressionevaluator.ExpressionUtils;
49
import org.gvsig.fmap.dal.DALLocator;
50
import org.gvsig.fmap.dal.DataTypes;
51
import org.gvsig.fmap.dal.SupportTransactions;
52
import org.gvsig.fmap.dal.exception.DataEvaluatorRuntimeException;
53
import org.gvsig.fmap.dal.exception.DataException;
54
import org.gvsig.fmap.dal.exception.DataRuntimeException;
55
import org.gvsig.fmap.dal.feature.DataProfile;
56
import org.gvsig.fmap.dal.feature.EditableFeature;
57
import org.gvsig.fmap.dal.feature.EditableFeatureAttributeDescriptor;
58
import org.gvsig.fmap.dal.feature.Feature;
59
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
60
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
61
import org.gvsig.fmap.dal.feature.FeatureAttributeGetter;
62
import org.gvsig.fmap.dal.feature.FeatureExtraColumns;
63
import org.gvsig.fmap.dal.feature.FeatureReference;
64
import org.gvsig.fmap.dal.feature.FeatureStore;
65
import org.gvsig.fmap.dal.feature.FeatureType;
66
import org.gvsig.fmap.dal.feature.ForeingKey;
67
import org.gvsig.fmap.dal.feature.exception.IllegalValueException;
68
import org.gvsig.fmap.dal.feature.exception.SetReadOnlyAttributeException;
69
import org.gvsig.fmap.dal.feature.impl.dynobjectutils.DynObjectFeatureFacade;
70
import org.gvsig.fmap.dal.feature.impl.featurereference.FeatureReferenceFactory;
71
import org.gvsig.fmap.dal.feature.spi.DefaultFeatureProvider;
72
import org.gvsig.fmap.dal.feature.spi.FeatureProvider;
73
import org.gvsig.fmap.geom.Geometry;
74
import org.gvsig.fmap.geom.primitive.Envelope;
75
import org.gvsig.json.Json;
76
import org.gvsig.json.JsonArrayBuilder;
77
import org.gvsig.json.JsonObjectBuilder;
78
import org.gvsig.tools.ToolsLocator;
79
import org.gvsig.tools.dataTypes.Coercion;
80
import org.gvsig.tools.dataTypes.CoercionException;
81
import org.gvsig.tools.dataTypes.DataType;
82
import org.gvsig.tools.dataTypes.DataTypesManager;
83
import org.gvsig.tools.dispose.DisposeUtils;
84
import org.gvsig.tools.dynobject.DynField;
85
import org.gvsig.tools.dynobject.DynObject;
86
import org.gvsig.tools.dynobject.Tags;
87
import org.gvsig.tools.evaluator.Evaluator;
88
import org.gvsig.tools.evaluator.EvaluatorData;
89
import org.gvsig.tools.evaluator.EvaluatorException;
90
import org.gvsig.tools.exception.BaseException;
91
import org.gvsig.tools.exception.BaseRuntimeException;
92
import org.gvsig.tools.lang.Cloneable;
93
import org.gvsig.tools.util.Bitmask;
94
import org.slf4j.Logger;
95
import org.slf4j.LoggerFactory;
96

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

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

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

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

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

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

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

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

    
144
        FeatureType sourceType = sourceFeature.getType();
145

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

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

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

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

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

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

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

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

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

    
230
        }
231

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
671
    class UnableToGetReferenceException extends BaseRuntimeException {
672

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

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

    
687
        }
688

    
689
    }
690

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
924
                };
925
            }
926

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1232
        throw new IllegalArgumentException(name);
1233
    }
1234

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

    
1239
            Iterator attributeIteraror;
1240

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

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

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

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

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

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

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

    
1282
            DefaultFeature feature;
1283
            int current = 0;
1284

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1788
    }
1789

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

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

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

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

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

    
2016

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