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

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

    
93
@SuppressWarnings("UseSpecificCatch")
94
public class DefaultFeature implements Feature, EvaluatorData, Cloneable {
95

    
96
    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultFeature.class);
97

    
98
    private static DataTypesManager dataTypesManager = null;
99
    protected FeatureProvider data;
100
    protected FeatureReference reference;
101
    private WeakReference storeRef;
102

    
103
    private boolean inserted = false;
104
    private Object[] extraValuesData;
105
    private Map<String, Object> extraValues; // not persistent
106

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

    
116
    public DefaultFeature(FeatureStore store, FeatureProvider data) {
117
        this.data = data;
118
        this.extraValuesData = null;
119
        this.storeRef = new WeakReference(store);
120
        this.reference = null;
121
        this.inserted = !data.isNew();
122
    }
123

    
124
    DefaultFeature(DefaultFeature feature) {
125
        this.data = feature.data.getCopy();
126
        this.extraValuesData = ArrayUtils.clone(feature.extraValuesData);
127
        this.storeRef = feature.storeRef;
128
        this.reference = feature.reference;
129
        this.inserted = feature.isInserted();
130
    }
131

    
132
    public DefaultFeature(FeatureType targetType, Feature sourceFeature) {
133
        DefaultFeature defaultFeature = (DefaultFeature) sourceFeature;
134
        this.data = new DefaultFeatureProvider(targetType, (DefaultFeatureProvider) defaultFeature.getData());
135
        this.extraValuesData = null;
136
        this.storeRef = defaultFeature.storeRef;
137
        this.reference = defaultFeature.reference;
138
        this.inserted = defaultFeature.isInserted();
139

    
140
        FeatureType sourceType = sourceFeature.getType();
141

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

    
158
    public void setData(FeatureProvider data) {
159
        this.data = data;
160
        this.extraValuesData = null;
161
        this.reference = null;
162
        this.inserted = true;
163
    }
164

    
165
    public FeatureProvider getData() {
166
        return this.data;
167
    }
168

    
169
    protected DataTypesManager getDataTypesManager() {
170
        if (dataTypesManager == null) {
171
            dataTypesManager = ToolsLocator.getDataTypesManager();
172
        }
173
        return dataTypesManager;
174
    }
175

    
176
    public boolean canSetValue(String name) {
177
        return this.canSetValue(this.getType().getAttributeDescriptor(name), null);
178
    }
179

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

    
197
    protected void set(FeatureAttributeDescriptor attribute, Object value) {
198
        int i = attribute.getIndex();
199

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

    
215
        if (value == null) {
216
            if (!attribute.allowNull()) {
217
                if (!attribute.isAutomatic()) {
218
                    throw new IllegalValueException(attribute, value);
219
                }
220
            }
221
            this.data.set(i, null);
222
            return;
223

    
224
        }
225

    
226
        if (attribute.getFeatureAttributeGetter() != null) {
227
            value = attribute.getFeatureAttributeGetter().setter(value);
228
        }
229
        this.setforced(i, attribute, value);
230
    }
231

    
232
    private void setforced(int i, FeatureAttributeDescriptor attribute, Object value) {
233

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

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

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

    
320
            if (value == null) {
321
                return null;
322
            }
323
            throw new IllegalArgumentException(
324
                    "Can't convert to " + theClass.getName()
325
                    + " from '" + value.getClass().getName()
326
                    + "' with value '" + value.toString() + "'.");
327
        }
328
    }
329

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

    
355
    public void clear() {
356
        initializeValues();
357
    }
358

    
359
    public void initializeValues(Feature feature) {
360
        FeatureType myType = this.getType();
361
        FeatureType type = feature.getType();
362
        extraValuesData = null;
363
        for (FeatureAttributeDescriptor attribute : type) {
364
            FeatureAttributeDescriptor myAttribute = myType.getAttributeDescriptor(attribute.getName());
365
            if (myAttribute != null) {
366
                this.set(myAttribute, feature.get(attribute.getIndex()));
367
            }
368
        }
369
    }
370

    
371
    @Override
372
    public FeatureStore getStore() {
373
        return (FeatureStore) this.storeRef.get();
374
    }
375

    
376
    @Override
377
    public FeatureType getType() {
378
        return this.data.getType();
379
    }
380

    
381
    @Override
382
    public EditableFeature getEditable() {
383
        return new DefaultEditableFeature(this);
384
    }
385

    
386
    @Override
387
    public Feature getCopy() {
388
        return new DefaultFeature(this);
389
    }
390

    
391
    @Override
392
    @SuppressWarnings("CloneDoesntCallSuperClone")
393
    public Object clone() throws CloneNotSupportedException {
394
        return new DefaultFeature(this);
395
    }
396

    
397
    @Override
398
    public FeatureReference getReference() {
399
        if(!this.getType().supportReferences()){
400
            return null;
401
        }
402
        if (this.reference == null) {
403
            if (!isInserted()) {
404
                return null;
405
            }
406
            reference = FeatureReferenceFactory.createFromFeature(this);
407
        }
408
        return this.reference;
409
    }
410

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

    
443
    @Override
444
    public String getStringOrDefault(String name, String defaultValue) {
445
        int index = this.data.getType().getIndex(name);
446
        if (index < 0) {
447
            return defaultValue;
448
        }
449
        try {
450
            return (String) this.get(index);
451
        } catch (Throwable th) {
452
            return defaultValue;
453
        }
454
    }
455

    
456
    @Override
457
    public boolean getBooleanOrDefault(String name, boolean defaultValue) {
458
        int index = this.data.getType().getIndex(name);
459
        if (index < 0) {
460
            return defaultValue;
461
        }
462
        try {
463
            return this.getBoolean(index);
464
        } catch (Throwable th) {
465
            return defaultValue;
466
        }
467
    }
468

    
469
    @Override
470
    public int getIntOrDefault(String name, int defaultValue) {
471
        int index = this.data.getType().getIndex(name);
472
        if (index < 0) {
473
            return defaultValue;
474
        }
475
        try {
476
            return this.getInt(index);
477
        } catch (Throwable th) {
478
            return defaultValue;
479
        }
480
    }
481

    
482
    @Override
483
    public long getLongOrDefault(String name, long defaultValue) {
484
        int index = this.data.getType().getIndex(name);
485
        if (index < 0) {
486
            return defaultValue;
487
        }
488
        try {
489
            return this.getLong(index);
490
        } catch (Throwable th) {
491
            return defaultValue;
492
        }
493
    }
494

    
495
    @Override
496
    public float getFloatOrDefault(String name, float defaultValue) {
497
        int index = this.data.getType().getIndex(name);
498
        if (index < 0) {
499
            return defaultValue;
500
        }
501
        try {
502
            return this.getFloat(index);
503
        } catch (Throwable th) {
504
            return defaultValue;
505
        }
506
    }
507

    
508
    @Override
509
    public double getDoubleOrDefault(String name, double defaultValue) {
510
        int index = this.data.getType().getIndex(name);
511
        if (index < 0) {
512
            return defaultValue;
513
        }
514
        try {
515
            return this.getDouble(index);
516
        } catch (Throwable th) {
517
            return defaultValue;
518
        }
519
    }
520

    
521
    @Override
522
    public BigDecimal getDecimalOrDefault(String name, BigDecimal defaultValue) {
523
        int index = this.data.getType().getIndex(name);
524
        if (index < 0) {
525
            return defaultValue;
526
        }
527
        try {
528
            return this.getDecimal(index);
529
        } catch (Throwable th) {
530
            return defaultValue;
531
        }
532
    }
533

    
534
    @Override
535
    public Date getDateOrDefault(String name, Date defaultValue) {
536
        int index = this.data.getType().getIndex(name);
537
        if (index < 0) {
538
            return defaultValue;
539
        }
540
        try {
541
            return this.getDate(index);
542
        } catch (Throwable th) {
543
            return defaultValue;
544
        }
545
    }
546

    
547
    @Override
548
    public Object getOrDefault(int index, Object defaultValue) {
549
        if (index < 0 || index >= this.data.getType().size()) {
550
            return defaultValue;
551
        }
552
        try {
553
            return this.get(index);
554
        } catch (Throwable th) {
555
            return defaultValue;
556
        }
557
    }
558

    
559
    @Override
560
    public String getStringOrDefault(int index, String defaultValue) {
561
        if (index < 0 || index >= this.data.getType().size()) {
562
            return defaultValue;
563
        }
564
        try {
565
            return this.getString(index);
566
        } catch (Throwable th) {
567
            return defaultValue;
568
        }
569
    }
570

    
571
    @Override
572
    public boolean getBooleanOrDefault(int index, boolean defaultValue) {
573
        if (index < 0 || index >= this.data.getType().size()) {
574
            return defaultValue;
575
        }
576
        try {
577
            return this.getBoolean(index);
578
        } catch (Throwable th) {
579
            return defaultValue;
580
        }
581
    }
582

    
583
    @Override
584
    public int getIntOrDefault(int index, int defaultValue) {
585
        if (index < 0 || index >= this.data.getType().size()) {
586
            return defaultValue;
587
        }
588
        try {
589
            return this.getInt(index);
590
        } catch (Throwable th) {
591
            return defaultValue;
592
        }
593
    }
594

    
595
    @Override
596
    public long getLongOrDefault(int index, long defaultValue) {
597
        if (index < 0 || index >= this.data.getType().size()) {
598
            return defaultValue;
599
        }
600
        try {
601
            return this.getLong(index);
602
        } catch (Throwable th) {
603
            return defaultValue;
604
        }
605
    }
606

    
607
    @Override
608
    public float getFloatOrDefault(int index, float defaultValue) {
609
        if (index < 0 || index >= this.data.getType().size()) {
610
            return defaultValue;
611
        }
612
        try {
613
            return this.getFloat(index);
614
        } catch (Throwable th) {
615
            return defaultValue;
616
        }
617
    }
618

    
619
    @Override
620
    public double getDoubleOrDefault(int index, double defaultValue) {
621
        if (index < 0 || index >= this.data.getType().size()) {
622
            return defaultValue;
623
        }
624
        try {
625
            return this.getDouble(index);
626
        } catch (Throwable th) {
627
            return defaultValue;
628
        }
629
    }
630

    
631
    @Override
632
    public BigDecimal getDecimalOrDefault(int index, BigDecimal defaultValue) {
633
        if (index < 0 || index >= this.data.getType().size()) {
634
            return defaultValue;
635
        }
636
        try {
637
            return this.getDecimal(index);
638
        } catch (Throwable th) {
639
            return defaultValue;
640
        }
641
    }
642

    
643
    @Override
644
    public Date getDateOrDefault(int index, Date defaultValue) {
645
        if (index < 0 || index >= this.data.getType().size()) {
646
            return defaultValue;
647
        }
648
        try {
649
            return this.getDate(index);
650
        } catch (Throwable th) {
651
            return defaultValue;
652
        }
653
    }
654

    
655
    @Override
656
    public void validate(int check) throws DataException {
657
        ((DefaultFeatureType) this.data.getType()).validateFeature(this, check);
658
    }
659

    
660
    class UnableToGetReferenceException extends BaseRuntimeException {
661

    
662
        /**
663
         *
664
         */
665
        private static final long serialVersionUID = 1812805035204824163L;
666

    
667
        /**
668
         * @param exception
669
         */
670
        @SuppressWarnings("OverridableMethodCallInConstructor")
671
        public UnableToGetReferenceException(BaseException exception) {
672
            super("Unable to get reference", "_UnableToGetReferenceException",
673
                    serialVersionUID);
674
            this.initCause(exception);
675

    
676
        }
677

    
678
    }
679

    
680
    @Override
681
    public List getSRSs() {
682
        // TODO Auto-generated method stub
683
        return null;
684
    }
685

    
686
    @Override
687
    public Envelope getDefaultEnvelope() {
688
        Envelope envelope = this.data.getDefaultEnvelope();
689
        if (envelope == null) {
690
            int i = this.data.getType().getDefaultGeometryAttributeIndex();
691
            if (i < 0) {
692
                return null;
693
            }
694
            Geometry geom = this.getDefaultGeometry();
695
            if (geom != null) {
696
                envelope = geom.getEnvelope();
697
            }
698
        }
699
        return envelope;
700
    }
701

    
702
    @Override
703
    public Geometry getDefaultGeometry() {
704
        Geometry geom = this.data.getDefaultGeometry();
705
        if (geom == null) {
706
            int i = this.data.getType().getDefaultGeometryAttributeIndex();
707
            if (i < 0) {
708
                return null;
709
            }
710
            Object x = this.get(i);
711
            if (x instanceof Geometry) {
712
                geom = (Geometry) x;
713
            } else {
714
                geom = this.getGeometry(i);
715
            }
716
        }
717
        if (geom != null) {
718
            if (geom.getProjection() == null) {
719
                FeatureType type = this.getType();
720
                DefaultFeatureAttributeDescriptor attrdesc = (DefaultFeatureAttributeDescriptor) type.get(type.getDefaultGeometryAttributeIndex());
721
                IProjection proj = attrdesc.getSRS(this.storeRef);
722
                geom.setProjection(proj);
723
            }
724
        }
725
        return geom;
726
    }
727

    
728
//    @Override
729
//    public Time getDefaultTime() {
730
//            Time time = this.data.getDefaultTime();
731
//        if( time == null ) {
732
//            int i = this.data.getType().getDefaultTimeAttributeIndex();
733
//            Object x = this.get(i);
734
//            if( x instanceof Time ) {
735
//                time = (Time) x;
736
//            } else {
737
//                time = this.getTime(i);
738
//            }
739
//        }
740
//        return time;
741
//    }
742
//
743
    @Override
744
    public IProjection getDefaultSRS() {
745
        IProjection srs = this.data.getType().getDefaultSRS();
746
        if (srs == null) {
747
            FeatureType type = this.getType();
748
            DefaultFeatureAttributeDescriptor attrdesc = (DefaultFeatureAttributeDescriptor) type.get(type.getDefaultGeometryAttributeIndex());
749
            srs = attrdesc.getSRS(this.storeRef);
750
        }
751
        return srs;
752
    }
753

    
754
    @Override
755
    public List getGeometries() {
756
        // TODO Auto-generated method stub
757
        return null;
758
    }
759

    
760
    @Override
761
    public Object getFromProfile(int index) {
762
        FeatureAttributeDescriptor descriptor = this.data.getType().getAttributeDescriptor(index);
763
        Object value = this.get(index);
764
        String profileName = descriptor.getDataProfileName();
765
        if (StringUtils.isBlank(profileName)) {
766
            return value;
767
        }
768
        DataProfile profile = DALLocator.getDataManager().getDataProfile(profileName);
769
        if (profile == null) {
770
            return value;
771
        }
772
        return profile.createData(value, descriptor.getTags());
773
    }
774

    
775
    @Override
776
    public Object getFromProfile(String name) {
777
        FeatureAttributeDescriptor descriptor = this.data.getType().getAttributeDescriptor(name);
778
        return this.getFromProfile(descriptor.getIndex());
779
    }
780

    
781
    private Object get(String name, Class theClass, int type) {
782
        Object value = this.get(name);
783
        if (theClass.isInstance(value)) {
784
            return value;
785
        }
786
        try {
787
            return this.getDataTypesManager().coerce(type, value);
788
        } catch (CoercionException e) {
789

    
790
            if (value == null) {
791
                return null;
792
            }
793
            throw new IllegalArgumentException(
794
                    "Can't convert to " + theClass.getName()
795
                    + " from '" + value.getClass().getName()
796
                    + "' with value '" + value.toString() + "'.");
797
        }
798
    }
799

    
800
    @Override
801
    public Object get(String name) {
802
        int index = this.data.getType().getIndex(name);
803
        if (index < 0) {
804
            // buscamos en los extra cols
805
            if (hasExtraColumnValue(name)) {
806
                return getExtraColumnValue(name);
807
            }
808
            if (hasExtraValue(name)) {
809
                return getExtraValue(name);
810
            }
811
            // y si esta ahi return
812
            throw new IllegalArgumentException("Attribute name '" + name + "' not found in the feature.");
813
        }
814
        return this.get(index);
815
    }
816

    
817
    @Override
818
    public boolean isNull(int index) {
819
        FeatureType type = this.data.getType();
820
        if (index < 0 || index >= type.size()) {
821
            throw new IllegalArgumentException("Attribute index '" + index + "' out of range (0 to " + this.data.getType().size() + ".");
822
        }
823
        FeatureAttributeDescriptor attribute = type.getAttributeDescriptor(index);
824
        if (!this.data.getType().hasEvaluators()) {
825
            return this.data.get(index) == null;
826
        }
827
        Evaluator eval = attribute.getEvaluator();
828
        if (eval == null) {
829
            return this.data.get(index) == null;
830
        }
831
        Object value = this.data.get(index);
832
        if (value != null) {
833
            return true;
834
        }
835
        try {
836
            value = eval.evaluate(this);
837
        } catch (EvaluatorException e) {
838
            throw new DataEvaluatorRuntimeException(e);
839
        }
840
        this.data.set(index, value);
841
        return value == null;
842
    }
843

    
844
    @Override
845
    public boolean isNull(String name) {
846
        int index = this.data.getType().getIndex(name);
847
        if (index < 0) {
848
            throw new IllegalArgumentException("Attribute name '" + name + "' not found in the feature.");
849
        }
850
        return this.isNull(index);
851
    }
852

    
853
    public boolean has_key(String key) {
854
        Object x = this.getType().get(key);
855
        return x != null;
856
    }
857

    
858
    public List<String> keys() {
859
        List<String> ks = new ArrayList<>();
860
        for (FeatureAttributeDescriptor attr : this.getType()) {
861
            ks.add(attr.getName());
862
        }
863
        return ks;
864
    }
865

    
866
    public Iterator<String> iterkeys() {
867
        final Iterator it = this.getType().iterator();
868
        return new Iterator<String>() {
869
            @Override
870
            public boolean hasNext() {
871
                return it.hasNext();
872
            }
873

    
874
            @Override
875
            public String next() {
876
                return ((FeatureAttributeDescriptor) it.next()).getName();
877
            }
878

    
879
            @Override
880
            public void remove() {
881
                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
882
            }
883
        };
884
    }
885

    
886
    public Iterator iteritems() {
887
        final Iterator it = this.getType().iterator();
888
        return new Iterator<Map.Entry>() {
889
            @Override
890
            public boolean hasNext() {
891
                return it.hasNext();
892
            }
893

    
894
            @Override
895
            public Map.Entry next() {
896
                final String name = ((FeatureAttributeDescriptor) it.next()).getName();
897
                return new Map.Entry<String, Object>() {
898
                    @Override
899
                    public String getKey() {
900
                        return name;
901
                    }
902

    
903
                    @Override
904
                    public Object getValue() {
905
                        return get(name);
906
                    }
907

    
908
                    @Override
909
                    public Object setValue(Object value) {
910
                        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
911
                    }
912

    
913
                };
914
            }
915

    
916
            @Override
917
            public void remove() {
918
                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
919
            }
920
        };
921
    }
922

    
923
    @Override
924
    public Object get(int index) {
925
        FeatureType type = this.data.getType();
926
        if (index < 0 || index >= type.size()) {
927
            throw new IllegalArgumentException("Attribute index '" + index + "' out of range (0 to " + this.data.getType().size() + ".");
928
        }
929
        Object value = this.data.get(index);
930
        FeatureAttributeDescriptor attribute = type.getAttributeDescriptor(index);
931
        if (type.hasEvaluators()) {
932
            Evaluator eval = attribute.getEvaluator();
933
            if (eval != null) {
934
                if (value == null) { // Ya hemos calculado el campo ?
935
                    // FIXME: para comprobar si esta calculado usar un array especifico.
936
                    try {
937
                        value = eval.evaluate(this);
938
                    } catch (EvaluatorException e) {
939
                        throw new DataEvaluatorRuntimeException(e);
940
                    }
941
                    this.data.set(index, value);
942
                }
943
            }
944
        }
945
        value = get(attribute, value);
946
        return value;
947
    }
948

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

    
996
    @Override
997
    public byte[] getByteArray(String name) {
998
        return (byte[]) this.get(name);
999
    }
1000

    
1001
    @Override
1002
    public byte[] getByteArray(int index) {
1003
        return (byte[]) this.get(index);
1004
    }
1005

    
1006
    @Override
1007
    public Object[] getArray(String name) {
1008
        return (Object[]) this.get(name);
1009
    }
1010

    
1011
    @Override
1012
    public Object[] getArray(int index) {
1013
        return (Object[]) this.get(index);
1014
    }
1015

    
1016
    @Override
1017
    public boolean getBoolean(String name) {
1018
        Boolean value = ((Boolean) this.get(name, Boolean.class, DataTypes.BOOLEAN));
1019
        if (value == null) {
1020
            return false;
1021
        }
1022
        return value;
1023
    }
1024

    
1025
    @Override
1026
    public boolean getBoolean(int index) {
1027
        Boolean value = ((Boolean) this.get(index, Boolean.class, DataTypes.BOOLEAN));
1028
        if (value == null) {
1029
            return false;
1030
        }
1031
        return value;
1032
    }
1033

    
1034
    @Override
1035
    public byte getByte(String name) {
1036
        Byte value = ((Byte) this.get(name, Byte.class, DataTypes.BYTE));
1037
        if (value == null) {
1038
            return 0;
1039
        }
1040
        return value;
1041
    }
1042

    
1043
    @Override
1044
    public byte getByte(int index) {
1045
        Byte value = ((Byte) this.get(index, Byte.class, DataTypes.BYTE));
1046
        if (value == null) {
1047
            return 0;
1048
        }
1049
        return value;
1050
    }
1051

    
1052
    @Override
1053
    public java.sql.Date getDate(String name) {
1054
        java.sql.Date value = ((java.sql.Date) this.get(name, java.sql.Date.class, DataTypes.DATE));
1055
        return value;
1056
    }
1057

    
1058
    @Override
1059
    public java.sql.Date getDate(int index) {
1060
        java.sql.Date value = ((java.sql.Date) this.get(index, java.sql.Date.class, DataTypes.DATE));
1061
        return value;
1062
    }
1063

    
1064
    @Override
1065
    public java.sql.Time getTime(String name) {
1066
        java.sql.Time value = ((java.sql.Time) this.get(name, java.sql.Time.class, DataTypes.TIME));
1067
        return value;
1068
    }
1069

    
1070
    @Override
1071
    public java.sql.Time getTime(int index) {
1072
        java.sql.Time value = ((java.sql.Time) this.get(index, java.sql.Time.class, DataTypes.TIME));
1073
        return value;
1074
    }
1075

    
1076
    @Override
1077
    public java.sql.Timestamp getTimestamp(String name) {
1078
        java.sql.Timestamp value = ((java.sql.Timestamp) this.get(name, java.sql.Timestamp.class, DataTypes.TIMESTAMP));
1079
        return value;
1080
    }
1081

    
1082
    @Override
1083
    public java.sql.Timestamp getTimestamp(int index) {
1084
        java.sql.Timestamp value = ((java.sql.Timestamp) this.get(index, java.sql.Timestamp.class, DataTypes.TIMESTAMP));
1085
        return value;
1086
    }
1087

    
1088
    @Override
1089
    public double getDouble(String name) {
1090
        Double value = ((Double) this.get(name, Double.class, DataTypes.DOUBLE));
1091
        if (value == null) {
1092
            return 0;
1093
        }
1094
        return value;
1095
    }
1096

    
1097
    @Override
1098
    public double getDouble(int index) {
1099

    
1100
        Double value = ((Double) this.get(index, Double.class, DataTypes.DOUBLE));
1101
        if (value == null) {
1102
            return 0;
1103
        }
1104
        return value;
1105
    }
1106

    
1107
    @Override
1108
    public BigDecimal getDecimal(String name) {
1109
        BigDecimal value = ((BigDecimal) this.get(name, BigDecimal.class, DataTypes.DECIMAL));
1110
        return value;
1111
    }
1112

    
1113
    @Override
1114
    public BigDecimal getDecimal(int index) {
1115
        BigDecimal value = ((BigDecimal) this.get(index, BigDecimal.class, DataTypes.DECIMAL));
1116
        return value;
1117
    }
1118

    
1119
    @Override
1120
    public Feature getFeature(String name) {
1121
        return this.getFeature(this.data.getType().getIndex(name));
1122
    }
1123

    
1124
    @Override
1125
    public Feature getFeature(int index) {
1126
        return (Feature) this.get(index);
1127
    }
1128

    
1129
    @Override
1130
    public float getFloat(String name) {
1131
        Float value = ((Float) this.get(name, Float.class, DataTypes.FLOAT));
1132
        if (value == null) {
1133
            return 0;
1134
        }
1135
        return value;
1136
    }
1137

    
1138
    @Override
1139
    public float getFloat(int index) {
1140
        Float value = ((Float) this.get(index, Float.class, DataTypes.FLOAT));
1141
        if (value == null) {
1142
            return 0;
1143
        }
1144
        return value;
1145
    }
1146

    
1147
    @Override
1148
    public Geometry getGeometry(String name) {
1149
        return (Geometry) this.get(name, Geometry.class, DataTypes.GEOMETRY);
1150
    }
1151

    
1152
    @Override
1153
    public Geometry getGeometry(int index) {
1154
        return (Geometry) this.get(index, Geometry.class, DataTypes.GEOMETRY);
1155
    }
1156

    
1157
    @Override
1158
    public int getInt(String name) {
1159
        Integer value = ((Integer) this.get(name, Integer.class, DataTypes.INT));
1160
        if (value == null) {
1161
            return 0;
1162
        }
1163
        return value;
1164
    }
1165

    
1166
    @Override
1167
    public int getInt(int index) {
1168
        Integer value = ((Integer) this.get(index, Integer.class, DataTypes.INT));
1169
        if (value == null) {
1170
            return 0;
1171
        }
1172
        return value;
1173
    }
1174

    
1175
    @Override
1176
    public long getLong(String name) {
1177
        Long value = ((Long) this.get(name, Long.class, DataTypes.LONG));
1178
        if (value == null) {
1179
            return 0;
1180
        }
1181
        return value;
1182
    }
1183

    
1184
    @Override
1185
    public long getLong(int index) {
1186
        Long value = ((Long) this.get(index, Long.class, DataTypes.LONG));
1187
        if (value == null) {
1188
            return 0;
1189
        }
1190
        return value;
1191
    }
1192

    
1193
    @Override
1194
    public String getString(String name) {
1195
        return (String) this.get(name, String.class, DataTypes.STRING);
1196
    }
1197

    
1198
    @Override
1199
    public String getString(int index) {
1200
        return (String) this.get(index, String.class, DataTypes.STRING);
1201
    }
1202

    
1203
    @Override
1204
    public Object getContextValue(String name) {
1205
        name = name.toLowerCase();
1206
        if (name.equals("store")) {
1207
            return this.getStore();
1208
        }
1209

    
1210
        if (name.equals("featuretype")) {
1211
            return this.data.getType();
1212
        }
1213

    
1214
        if (name.equals("feature")) {
1215
            return this;
1216
        }
1217

    
1218
        throw new IllegalArgumentException(name);
1219
    }
1220

    
1221
    @Override
1222
    public Iterator getDataNames() {
1223
        class DataNamesIterator implements Iterator {
1224

    
1225
            Iterator attributeIteraror;
1226

    
1227
            DataNamesIterator(DefaultFeature feature) {
1228
                this.attributeIteraror = feature.getType().iterator();
1229
            }
1230

    
1231
            @Override
1232
            public boolean hasNext() {
1233
                return this.attributeIteraror.hasNext();
1234
            }
1235

    
1236
            @Override
1237
            public Object next() {
1238
                return ((FeatureAttributeDescriptor) this.attributeIteraror
1239
                        .next()).getName();
1240
            }
1241

    
1242
            @Override
1243
            public void remove() {
1244
                throw new UnsupportedOperationException();
1245
            }
1246

    
1247
        }
1248
        return new DataNamesIterator(this);
1249
    }
1250

    
1251
    @Override
1252
    public Object getDataValue(String name) {
1253
        name = name.toLowerCase();
1254
        try {
1255
            return get(name);
1256
        } catch (IllegalArgumentException ex) {
1257
            if ("defaultgeometry".equalsIgnoreCase(name)) {
1258
                return this.getDefaultGeometry();
1259
            }
1260
            throw ex;
1261
        }
1262
    }
1263

    
1264
    @Override
1265
    public Iterator getDataValues() {
1266
        class DataValuesIterator implements Iterator {
1267

    
1268
            DefaultFeature feature;
1269
            int current = 0;
1270

    
1271
            DataValuesIterator(DefaultFeature feature) {
1272
                this.feature = feature;
1273
            }
1274

    
1275
            @Override
1276
            public boolean hasNext() {
1277
                return current < feature.getType().size() - 1;
1278
            }
1279

    
1280
            @Override
1281
            public Object next() {
1282
                return feature.get(current++);
1283
            }
1284

    
1285
            @Override
1286
            public void remove() {
1287
                throw new UnsupportedOperationException();
1288
            }
1289

    
1290
        }
1291
        return new DataValuesIterator(this);
1292
    }
1293

    
1294
    @Override
1295
    public boolean hasContextValue(String name) {
1296
        name = name.toLowerCase();
1297
        if (name.equals("store")) {
1298
            return true;
1299
        }
1300

    
1301
        if (name.equals("featuretype")) {
1302
            return true;
1303
        }
1304

    
1305
        return name.equals("feature");
1306
    }
1307

    
1308
    @Override
1309
    public boolean hasDataValue(String name) {
1310
        return this.hasValue(name);
1311
    }
1312

    
1313
//    @Override
1314
//    public Time getTime(int index) {
1315
//        return ((Time) this.get(index,Time.class,DataTypes.INSTANT));
1316
//    }
1317
//
1318
//    @Override
1319
//    public Time getTime(String name) {
1320
//        return this.getInstant(this.data.getType().getIndex(name));
1321
//    }
1322
//
1323
//    @Override
1324
//    public Instant getInstant(int index) {
1325
//        return ((Instant) this.get(index,Instant.class,DataTypes.INSTANT));
1326
//    }
1327
//
1328
//    @Override
1329
//    public Instant getInstant(String name) {
1330
//        return this.getInstant(this.data.getType().getIndex(name));
1331
//    }
1332
//
1333
//    @Override
1334
//    public Interval getInterval(int index) {
1335
//        return ((Interval) this.get(index,Interval.class,DataTypes.INTERVAL));
1336
//    }
1337
//
1338
//    @Override
1339
//    public Interval getInterval(String name) {
1340
//        return this.getInterval(this.data.getType().getIndex(name));
1341
//    }
1342
//
1343
    @Override
1344
    public DynObject getAsDynObject() {
1345
        DynObjectFeatureFacade facade = new DynObjectFeatureFacade(this);
1346
        return facade;
1347
    }
1348

    
1349
    @Override
1350
    public String toString() {
1351
        StringBuilder builder = new StringBuilder();
1352
        FeatureAttributeDescriptor[] attributeDescriptors
1353
                = getType().getAttributeDescriptors();
1354
        for (int i = 0; i < attributeDescriptors.length; i++) {
1355
            String name = attributeDescriptors[i].getName();
1356
            Object value = get(name);
1357
            builder.append(value);
1358
            if (i < attributeDescriptors.length - 1) {
1359
                builder.append(", ");
1360
            }
1361
        }
1362
        return builder.toString();
1363
    }
1364

    
1365
    /**
1366
     * It is a new feature that has already been inserted into the store but has not yet been saved to disk
1367
     * 
1368
     * @return the inserted
1369
     */
1370
    public boolean isInserted() {
1371
        return inserted;
1372
    }
1373

    
1374
    /**
1375
     * If true, marks the feature as already inserted in the vault but has not yet been saved to disk
1376
     * 
1377
     * @param inserted the inserted to set
1378
     */
1379
    public void setInserted(boolean inserted) {
1380
        this.inserted = inserted;
1381
//        this.data.setNew(!inserted);
1382
    }
1383

    
1384
    @Override
1385
    public EvaluatorData getEvaluatorData() {
1386
        return this;
1387
    }
1388

    
1389
    @Override
1390
    public int size() {
1391
        return this.data.getType().size();
1392
    }
1393

    
1394
    public boolean isEmpty() {
1395
        return false;
1396
    }
1397

    
1398
    public Iterator<String> iterator() {
1399
        final Iterator<FeatureAttributeDescriptor> x = this.data.getType().iterator();
1400
        return new Iterator<String>() {
1401
            @Override
1402
            public boolean hasNext() {
1403
                return x.hasNext();
1404
            }
1405

    
1406
            @Override
1407
            public String next() {
1408
                return x.next().getName();
1409
            }
1410
        };
1411
    }
1412

    
1413
    public boolean containsKey(String key) {
1414
        return this.data.getType().get(key) != null;
1415
    }
1416

    
1417
    @Override
1418
    public String getLabelOfValue(String name) {
1419
        FeatureAttributeDescriptor attrdesc = this.data.getType().getAttributeDescriptor(name);
1420
        Object value;
1421
        if (attrdesc == null) { // extra column
1422
            FeatureExtraColumns extraColumns = this.data.getType().getExtraColumns();
1423
            if (extraColumns==null) {
1424
                return name;
1425
            }
1426
            attrdesc = extraColumns.get(name);
1427
            if(attrdesc==null) {
1428
                return name;
1429
            }
1430
           value = this.get(name);
1431
        } else {
1432
           value = this.get(attrdesc.getIndex());
1433
        }
1434
        String label;
1435
        try {
1436
            label = attrdesc.getLabelOfValue(value);
1437
        } catch(Throwable th) {
1438
            label = Objects.toString(value, "");
1439
        }
1440
        return label;
1441
    }
1442

    
1443
    @Override
1444
    public void setExtraValue(String name, Object value) {
1445
        if (this.extraValues == null) {
1446
            this.extraValues = new HashMap<>();
1447
        }
1448
        this.extraValues.put(name, value);
1449
    }
1450

    
1451
    public Object getExtraColumnValue(String name) {
1452
        Object value;
1453
        FeatureExtraColumns columns = this.getType().getExtraColumns();
1454
        int index = columns.getIndexOf(name);
1455
        if (this.extraValues != null) {
1456
            if (this.extraValues.containsKey(name)) {
1457
                value = this.extraValues.get(name);
1458
                if (index >= 0){
1459
                    EditableFeatureAttributeDescriptor attrdesc = columns.get(index);
1460
                    value = coerce(attrdesc, value);
1461
                }
1462
                return value;
1463
            }
1464
        }
1465
        if (index < 0) {
1466
            throw new RuntimeException("Not extra column value found");
1467
        }
1468
        if (extraValuesData == null) {
1469
            extraValuesData = new Object[columns.size()];
1470
        }
1471
        EditableFeatureAttributeDescriptor attrdesc = columns.get(index);
1472
        value = extraValuesData[index];
1473
        if (value != null) {
1474
            return value;
1475
        }
1476
        value = this.getExtraValue(name);
1477
        if (value == null && !this.hasExtraValue(name) && attrdesc.getFeatureAttributeEmulator() != null) {
1478
            value = attrdesc.getFeatureAttributeEmulator().get(this);
1479
            value = coerce(attrdesc, value);
1480
            extraValuesData[index] = value;
1481
        } else {
1482
            value = coerce(attrdesc, value);
1483
            extraValuesData[index] = value;
1484
        }
1485
        
1486
        return value;
1487
    }
1488

    
1489
    @Override
1490
    public Object getExtraValue(String name) {
1491
        Object value = this.data.getExtraValue(name);
1492
        return value;
1493
    }
1494

    
1495
    @Override
1496
    public boolean hasExtraValue(String name) {
1497
        return this.data.hasExtraValue(name);
1498
    }
1499

    
1500
    private boolean hasExtraColumnValue(String name) {
1501
        if (this.extraValues != null) {
1502
            if (this.extraValues.containsKey(name)) {
1503
                return true;
1504
            }
1505
        }
1506
        FeatureExtraColumns columns = this.getType().getExtraColumns();
1507
        int index = columns.getIndexOf(name);
1508
        if (index >= 0) {
1509
            return true;
1510
        }
1511
        return false;
1512
    }
1513

    
1514
    private EditableFeatureAttributeDescriptor getExtraColumn(String name) {
1515
        FeatureExtraColumns columns = this.getType().getExtraColumns();
1516
        return columns.get(name);
1517
    }
1518

    
1519
    @Override
1520
    public boolean hasValue(String name) {
1521
        name = name.toLowerCase();
1522
        return this.data.getType().getIndex(name) >= 0
1523
                || hasExtraValue(name)
1524
                || hasExtraColumnValue(name);
1525
    }
1526

    
1527
    @Override
1528
    public Object getExtraValue(int index) {
1529
        return this.data.getExtraValue(index);
1530
    }
1531

    
1532
    @Override
1533
    public JsonObject toJson() {
1534
        JsonObjectBuilder builder = this.toJsonBuilder();
1535
        return builder.build();
1536
    }
1537

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

    
1621
    public static final String TOJSON_MODE = "mode";
1622
    public static final int TOJSON_MODE_SHALLOW = 0;
1623
    public static final int TOJSON_MODE_DEEP =        0b00001;
1624
    public static final int TOJSON_MODE_COMPUTEDS =   0b00010;
1625
    public static final int TOJSON_MODE_COLLECTIONS = 0b00100;
1626
    
1627
    @Override
1628
    public JsonObjectBuilder toJsonBuilder(Map<String, Object> props) {    
1629
        Bitmask modemask = null;
1630
        Set<String> visiteds = null;
1631
        int mode = TOJSON_MODE_SHALLOW;
1632
        if( props!=null ) {
1633
            mode = (int) props.getOrDefault(TOJSON_MODE,mode);
1634
            modemask = Bitmask.createBitmask(mode);
1635
            if( modemask.isSetBits(TOJSON_MODE_DEEP) ) {
1636
                visiteds = (Set<String>) props.getOrDefault("visiteds", null);
1637
                if( visiteds == null ) {
1638
                    visiteds = new HashSet<>();
1639
                    props.put("visiteds", visiteds);
1640
                }
1641
            }
1642
        } else {
1643
            modemask = Bitmask.createBitmask(mode);
1644
        }
1645
        return this.toJsonBuilder(props, modemask, visiteds);
1646
    }
1647
    
1648
    private JsonObjectBuilder toJsonBuilder(Map<String, Object> props, Bitmask mode, Set<String> visiteds) {    
1649
        JsonObjectBuilder builder = Json.createObjectBuilder();
1650
        Date date;
1651
        Geometry geom;
1652
        FeatureType ft = this.getType();
1653
        
1654
        boolean hasVisited = false;
1655
        if(visiteds != null){
1656
            FeatureReference ref = this.getReference();
1657
            String code = ref.getCode();
1658
            hasVisited = visiteds.contains(code);
1659
            if(hasVisited){
1660
               return this.featureReferenceToJson(ref);
1661
            }
1662
            visiteds.add(code);
1663
        }
1664
        
1665
        for (FeatureAttributeDescriptor desc : ft) {
1666
            if (desc.isComputed() ) { 
1667
                if( !mode.isSetBits(TOJSON_MODE_COMPUTEDS) ) {
1668
                    if( !(mode.isSetBits(TOJSON_MODE_COLLECTIONS) && desc.getType()==DataTypes.LIST) ) { 
1669
                        continue;
1670
                    }
1671
                }
1672
            }
1673
            if(desc.getType() != DataTypes.LIST && this.isNull(desc.getName()) ){
1674
                builder.addNull(desc.getName());
1675
                continue;
1676
            }
1677
            if( desc.isForeingKey() && !desc.getForeingKey().isClosedList() && mode.isSetBits(TOJSON_MODE_DEEP) ) {
1678
                if( desc.getRelationType()==DynField.RELATION_TYPE_COLLABORATION_WITH_COMPOSITION) {
1679
                    Object value = this.get(desc.getName());
1680
                    Feature f = desc.getForeingKey().getFeature(null, value);
1681
                    String x = f.getReference().getCode();
1682
                    if( visiteds==null ) {
1683
                        if( f instanceof DefaultFeature ) {
1684
                            builder.add(desc.getName(), ((DefaultFeature)f).toJsonBuilder(props, mode, visiteds));
1685
                        } else {
1686
                            builder.add(desc.getName(), f.toJsonBuilder(props));
1687
                        }
1688
                        continue;
1689
                    } else if( !visiteds.contains(x) ) {
1690
                        if( f instanceof DefaultFeature ) {
1691
                            builder.add(desc.getName(), ((DefaultFeature)f).toJsonBuilder(props, mode, visiteds));
1692
                        } else {
1693
                            builder.add(desc.getName(), f.toJsonBuilder(props));
1694
                        }
1695
                        continue;
1696
                    }
1697
                }
1698
            }
1699
            switch (desc.getType()) {
1700
                case DataTypes.GEOMETRY:
1701
                    geom = this.getGeometry(desc.getIndex());
1702
                    if (geom != null) {
1703
                        builder.add(desc.getName(), geom.convertToHexWKBQuietly());
1704
                    }
1705
                    break;
1706
                case DataTypes.BOOLEAN:
1707
                    builder.add(desc.getName(), this.getBoolean(desc.getIndex()));
1708
                    break;
1709
                case DataTypes.BYTE:
1710
                    builder.add(desc.getName(), this.getByte(desc.getIndex()));
1711
                    break;
1712
                case DataTypes.INT:
1713
                    builder.add(desc.getName(), this.getInt(desc.getIndex()));
1714
                    break;
1715
                case DataTypes.LONG:
1716
                    builder.add(desc.getName(), this.getLong(desc.getIndex()));
1717
                    break;
1718
                case DataTypes.DOUBLE:
1719
                    builder.add(desc.getName(), this.getDouble(desc.getIndex()));
1720
                    break;
1721
                case DataTypes.FLOAT:
1722
                    builder.add(desc.getName(), this.getFloat(desc.getIndex()));
1723
                    break;
1724
                case DataTypes.DECIMAL:
1725
                    builder.add(desc.getName(), this.getDecimal(desc.getIndex()));
1726
                    break;
1727
                case DataTypes.DATE:
1728
                    // Format date as ISO 8601
1729
                    date = this.getDate(desc.getIndex());
1730
                    if (date == null) {
1731
                        builder.addNull(desc.getName());
1732
                    } else {
1733
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1734
                        String value = DateTimeFormatter.ISO_DATE.format(localDateTime);
1735
                        builder.add(desc.getName(), value);
1736
                    }
1737
                    break;
1738
                case DataTypes.TIMESTAMP:
1739
                    // Format date as ISO 8601
1740
                    date = this.getTimestamp(desc.getIndex());
1741
                    if (date == null) {
1742
                        builder.addNull(desc.getName());
1743
                    } else {
1744
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1745
                        String value = DateTimeFormatter.ISO_DATE_TIME.format(localDateTime);
1746
                        builder.add(desc.getName(), value);
1747
                    }
1748
                    break;
1749
                case DataTypes.TIME:
1750
                    // Format date as ISO 8601
1751
                    date = this.getTime(desc.getIndex());
1752
                    if (date == null) {
1753
                        builder.addNull(desc.getName());
1754
                    } else {
1755
                        LocalDateTime localDateTime = LocalDateTime.ofInstant(new Date(date.getTime()).toInstant(), ZoneId.systemDefault());
1756
                        String value = DateTimeFormatter.ISO_TIME.format(localDateTime);
1757
                        builder.add(desc.getName(), value);
1758
                    }
1759
                    break;
1760
                case DataTypes.LIST: 
1761
                    if( mode.isSetBits(TOJSON_MODE_COLLECTIONS) ) {
1762
                        if( desc.getRelationType()== DynField.RELATION_TYPE_AGGREGATE_WITH_COMPOSITION) {
1763
                            JsonArrayBuilder arraybuilder = Json.createArrayBuilder();
1764
                            Object x = this.get(desc.getName());
1765
                            if( x instanceof List ) {
1766
                                for (Object v : (List)x) {
1767
                                    if( v instanceof DefaultFeature ) {
1768
                                            arraybuilder.add(((DefaultFeature)v).toJsonBuilder(props, mode, visiteds));
1769
                                    } else if( v instanceof Feature ) {
1770
                                        arraybuilder.add(((Feature)v).toJsonBuilder(props));
1771
                                    }
1772
                                }
1773
                            }
1774
                            DisposeUtils.disposeQuietly(x);
1775
                            builder.add(desc.getName(), arraybuilder);
1776
                        }
1777
                    }
1778
                    break;
1779
                    
1780
                default:
1781
                    builder.add(desc.getName(), this.getStringOrDefault(desc.getIndex(), ""));
1782
            }
1783
        }
1784
        return builder;
1785
    }
1786
    
1787
    private JsonObjectBuilder featureReferenceToJson(FeatureReference ref){
1788
        JsonObjectBuilder builder = Json.createObjectBuilder();
1789
        builder.add("$reference", ref.toJsonBuilder());
1790
        return builder;
1791
    }
1792

    
1793
    @Override
1794
    public List<String> getKeys() {
1795
        List<String> l = new ArrayList<>();
1796
        for (FeatureAttributeDescriptor descriptor : this.getType()) {
1797
            l.add(descriptor.getName());
1798
        }
1799
        return l;
1800
    }
1801

    
1802
    @Override
1803
    public String format(String name) {
1804
        int index = this.data.getType().getIndex(name);
1805
        if (index < 0) {
1806
            // buscamos en los extra cols
1807
            FeatureAttributeDescriptor attribute = this.getExtraColumn(name);
1808
            if( attribute!=null ) {
1809
                Object value = getExtraColumnValue(name);
1810
                return attribute.format(value);
1811
            }
1812
            throw new IllegalArgumentException("Attribute name '" + name + "' not found in the feature.");
1813
        }
1814
        return this.format(index);
1815
    }
1816

    
1817
    @Override
1818
    public String format(int index) {
1819
        Object value = this.get(index);
1820
        FeatureType type = this.data.getType();
1821
        FeatureAttributeDescriptor attribute = type.getAttributeDescriptor(index);
1822
        return attribute.format(value);
1823
    }
1824

    
1825
    
1826
}