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

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

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

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

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

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

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

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

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

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

    
143
        FeatureType sourceType = sourceFeature.getType();
144

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

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

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

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

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

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

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

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

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

    
227
        }
228

    
229
        if (attribute.getFeatureAttributeGetter() != null) {
230
            value = attribute.getFeatureAttributeGetter().setter(value);
231
        }
232
        this.setforced(i, attribute, value);
233
    }
234

    
235
    private void setforced(int i, FeatureAttributeDescriptor attribute, Object value) {
236

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

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

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

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

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

    
358
    public void clear() {
359
        initializeValues();
360
    }
361

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

    
374
    @Override
375
    public FeatureStore getStore() {
376
        return (FeatureStore) this.storeRef.get();
377
    }
378

    
379
    @Override
380
    public FeatureType getType() {
381
        return this.data.getType();
382
    }
383

    
384
    @Override
385
    public EditableFeature getEditable() {
386
        return new DefaultEditableFeature(this);
387
    }
388

    
389
    @Override
390
    public Feature getCopy() {
391
        return new DefaultFeature(this);
392
    }
393

    
394
    @Override
395
    @SuppressWarnings("CloneDoesntCallSuperClone")
396
    public Object clone() throws CloneNotSupportedException {
397
        return new DefaultFeature(this);
398
    }
399

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
658
    @Override
659
    public void validate(int check) throws DataException {
660
        ((DefaultFeatureType) this.data.getType()).validateFeature(this, check);
661
    }
662

    
663
    @Override
664
    public boolean isBroken() {
665
        return this.data.isBroken();
666
    }
667

    
668
    class UnableToGetReferenceException extends BaseRuntimeException {
669

    
670
        /**
671
         *
672
         */
673
        private static final long serialVersionUID = 1812805035204824163L;
674

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

    
684
        }
685

    
686
    }
687

    
688
    @Override
689
    public List getSRSs() {
690
        // TODO Auto-generated method stub
691
        return null;
692
    }
693

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

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

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

    
762
    @Override
763
    public List getGeometries() {
764
        // TODO Auto-generated method stub
765
        return null;
766
    }
767

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

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

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

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

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

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

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

    
861
    public boolean has_key(String key) {
862
        Object x = this.getType().get(key);
863
        return x != null;
864
    }
865

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

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

    
882
            @Override
883
            public String next() {
884
                return ((FeatureAttributeDescriptor) it.next()).getName();
885
            }
886

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

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

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

    
911
                    @Override
912
                    public Object getValue() {
913
                        return get(name);
914
                    }
915

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

    
921
                };
922
            }
923

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

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

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

    
1007
    @Override
1008
    public byte[] getByteArray(String name) {
1009
        return (byte[]) this.get(name);
1010
    }
1011

    
1012
    @Override
1013
    public byte[] getByteArray(int index) {
1014
        return (byte[]) this.get(index);
1015
    }
1016

    
1017
    @Override
1018
    public Object[] getArray(String name) {
1019
        return (Object[]) this.get(name);
1020
    }
1021

    
1022
    @Override
1023
    public Object[] getArray(int index) {
1024
        return (Object[]) this.get(index);
1025
    }
1026

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

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

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

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

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

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

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

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

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

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

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

    
1108
    @Override
1109
    public double getDouble(int index) {
1110

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

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

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

    
1130
    @Override
1131
    public Feature getFeature(String name) {
1132
        return this.getFeature(this.data.getType().getIndex(name));
1133
    }
1134

    
1135
    @Override
1136
    public Feature getFeature(int index) {
1137
        return (Feature) this.get(index);
1138
    }
1139

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

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

    
1158
    @Override
1159
    public Geometry getGeometry(String name) {
1160
        return (Geometry) this.get(name, Geometry.class, DataTypes.GEOMETRY);
1161
    }
1162

    
1163
    @Override
1164
    public Geometry getGeometry(int index) {
1165
        return (Geometry) this.get(index, Geometry.class, DataTypes.GEOMETRY);
1166
    }
1167

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

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

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

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

    
1204
    @Override
1205
    public String getString(String name) {
1206
        return (String) this.get(name, String.class, DataTypes.STRING);
1207
    }
1208

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

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

    
1221
        if (name.equals("featuretype")) {
1222
            return this.data.getType();
1223
        }
1224

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

    
1229
        throw new IllegalArgumentException(name);
1230
    }
1231

    
1232
    @Override
1233
    public Iterator getDataNames() {
1234
        class DataNamesIterator implements Iterator {
1235

    
1236
            Iterator attributeIteraror;
1237

    
1238
            DataNamesIterator(DefaultFeature feature) {
1239
                this.attributeIteraror = feature.getType().iterator();
1240
            }
1241

    
1242
            @Override
1243
            public boolean hasNext() {
1244
                return this.attributeIteraror.hasNext();
1245
            }
1246

    
1247
            @Override
1248
            public Object next() {
1249
                return ((FeatureAttributeDescriptor) this.attributeIteraror
1250
                        .next()).getName();
1251
            }
1252

    
1253
            @Override
1254
            public void remove() {
1255
                throw new UnsupportedOperationException();
1256
            }
1257

    
1258
        }
1259
        return new DataNamesIterator(this);
1260
    }
1261

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

    
1275
    @Override
1276
    public Iterator getDataValues() {
1277
        class DataValuesIterator implements Iterator {
1278

    
1279
            DefaultFeature feature;
1280
            int current = 0;
1281

    
1282
            DataValuesIterator(DefaultFeature feature) {
1283
                this.feature = feature;
1284
            }
1285

    
1286
            @Override
1287
            public boolean hasNext() {
1288
                return current < feature.getType().size() - 1;
1289
            }
1290

    
1291
            @Override
1292
            public Object next() {
1293
                return feature.get(current++);
1294
            }
1295

    
1296
            @Override
1297
            public void remove() {
1298
                throw new UnsupportedOperationException();
1299
            }
1300

    
1301
        }
1302
        return new DataValuesIterator(this);
1303
    }
1304

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

    
1312
        if (name.equals("featuretype")) {
1313
            return true;
1314
        }
1315

    
1316
        return name.equals("feature");
1317
    }
1318

    
1319
    @Override
1320
    public boolean hasDataValue(String name) {
1321
        return this.hasValue(name);
1322
    }
1323

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

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

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

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

    
1395
    @Override
1396
    public EvaluatorData getEvaluatorData() {
1397
        return this;
1398
    }
1399

    
1400
    @Override
1401
    public int size() {
1402
        return this.data.getType().size();
1403
    }
1404

    
1405
    public boolean isEmpty() {
1406
        return false;
1407
    }
1408

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

    
1417
            @Override
1418
            public String next() {
1419
                return x.next().getName();
1420
            }
1421
        };
1422
    }
1423

    
1424
    public boolean containsKey(String key) {
1425
        return this.data.getType().get(key) != null;
1426
    }
1427

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

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

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

    
1500
    @Override
1501
    public Object getExtraValue(String name) {
1502
        Object value = this.data.getExtraValue(name);
1503
        return value;
1504
    }
1505

    
1506
    @Override
1507
    public boolean hasExtraValue(String name) {
1508
        return this.data.hasExtraValue(name);
1509
    }
1510

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

    
1525
    private EditableFeatureAttributeDescriptor getExtraColumn(String name) {
1526
        FeatureExtraColumns columns = this.getType().getExtraColumns();
1527
        return columns.get(name);
1528
    }
1529

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

    
1538
    @Override
1539
    public Object getExtraValue(int index) {
1540
        return this.data.getExtraValue(index);
1541
    }
1542

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

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

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

    
1754
                    
1755
                default:
1756
                    properties.put(desc.getName(), this.getStringOrDefault(desc.getIndex(), ""));
1757
            }
1758
        }
1759
        String json_s;
1760
        try {
1761
            json_s = geom.convertToGeoJson(properties, (boolean) props.getOrDefault("encodecrs", true));
1762
        } catch (Exception ex) {
1763
            throw new ConvertFeatureToGeoJsonException(ex);
1764
        }
1765
        JsonObjectBuilder json = Json.createObjectBuilder(json_s);
1766
        return json;
1767
        
1768
    }
1769
    
1770
    private static class ConvertFeatureToGeoJsonException extends DataRuntimeException {
1771

    
1772
        private static final long serialVersionUID = -1;
1773
        private final static String MESSAGE_FORMAT = "Create GeoJson builder exception.";
1774
        private final static String MESSAGE_KEY = "_ConvertFeatureToGeoJsonException";
1775

    
1776
        public ConvertFeatureToGeoJsonException(Throwable cause) {
1777
            super(MESSAGE_FORMAT, cause, MESSAGE_KEY, serialVersionUID);
1778
        }
1779

    
1780
    }
1781

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

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

    
1947
    @Override
1948
    public List<String> getKeys() {
1949
        List<String> l = new ArrayList<>();
1950
        for (FeatureAttributeDescriptor descriptor : this.getType()) {
1951
            l.add(descriptor.getName());
1952
        }
1953
        return l;
1954
    }
1955

    
1956
    @Override
1957
    public String format(String name) {
1958
        int index = this.data.getType().getIndex(name);
1959
        if (index < 0) {
1960
            // buscamos en los extra cols
1961
            FeatureAttributeDescriptor attribute = this.getExtraColumn(name);
1962
            if( attribute!=null ) {
1963
                Object value = getExtraColumnValue(name);
1964
                return attribute.format(value);
1965
            }
1966
            throw new IllegalArgumentException("Attribute name '" + name + "' not found in the feature.");
1967
        }
1968
        return this.format(index);
1969
    }
1970

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

    
2008

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