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 / DefaultFeatureAttributeDescriptor.java @ 47404

History | View | Annotate | Download (82 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 modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.dal.feature.impl;
24

    
25
import java.lang.ref.WeakReference;
26
import java.math.BigDecimal;
27
import java.math.MathContext;
28
import java.math.RoundingMode;
29
import java.text.DateFormat;
30
import java.util.ArrayList;
31
import java.util.HashMap;
32
import java.util.LinkedHashMap;
33
import java.util.List;
34
import java.util.Locale;
35
import java.util.Map;
36
import java.util.Map.Entry;
37
import java.util.Objects;
38
import java.util.function.Supplier;
39
import javax.json.JsonObject;
40
import org.apache.commons.lang3.ArrayUtils;
41
import org.apache.commons.lang3.StringUtils;
42
import org.apache.commons.lang3.tuple.Pair;
43
import org.cresques.cts.IProjection;
44
import org.gvsig.expressionevaluator.Expression;
45
import org.gvsig.expressionevaluator.ExpressionUtils;
46
import org.gvsig.expressionevaluator.MutableSymbolTable;
47
import org.gvsig.fmap.dal.DALLocator;
48
import org.gvsig.fmap.dal.DataStore;
49
import org.gvsig.fmap.dal.DataTypes;
50
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
51
import org.gvsig.fmap.dal.feature.DataProfile;
52
import org.gvsig.fmap.dal.feature.Feature;
53
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
54
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
55
import org.gvsig.fmap.dal.feature.FeatureAttributeGetter;
56
import org.gvsig.fmap.dal.feature.FeatureStore;
57
import org.gvsig.fmap.dal.feature.FeatureType;
58
import org.gvsig.fmap.dal.feature.ForeingKey;
59
import org.gvsig.fmap.dal.feature.ForeingKey.ContextForeingKey;
60
import org.gvsig.fmap.geom.Geometry;
61
import org.gvsig.fmap.geom.GeometryCoercionContext;
62
import org.gvsig.fmap.geom.GeometryException;
63
import org.gvsig.fmap.geom.GeometryLocator;
64
import org.gvsig.fmap.geom.GeometryUtils;
65
import org.gvsig.fmap.geom.type.GeometryType;
66
import org.gvsig.json.Json;
67
import org.gvsig.json.JsonManager;
68
import org.gvsig.json.JsonObjectBuilder;
69
import org.gvsig.json.SupportToJson;
70
import org.gvsig.timesupport.Interval;
71
import org.gvsig.timesupport.RelativeInterval;
72
import org.gvsig.timesupport.TimeSupportLocator;
73
import org.gvsig.tools.ToolsLocator;
74
import org.gvsig.tools.dataTypes.Coercion;
75
import org.gvsig.tools.dataTypes.CoercionContext;
76
import org.gvsig.tools.dataTypes.CoercionException;
77
import org.gvsig.tools.dataTypes.DataType;
78
import org.gvsig.tools.dataTypes.DataType.NumberPrecisionAndScale;
79
import org.gvsig.tools.dataTypes.DataTypeUtils;
80
import org.gvsig.tools.dispose.DisposeUtils;
81
import org.gvsig.tools.dynobject.AbstractDynMethod;
82
import org.gvsig.tools.dynobject.DynField;
83
import org.gvsig.tools.dynobject.DynField_LabelAttribute;
84
import org.gvsig.tools.dynobject.DynField_v2;
85
import org.gvsig.tools.dynobject.DynMethod;
86
import org.gvsig.tools.dynobject.DynObject;
87
import org.gvsig.tools.dynobject.DynObjectValueItem;
88
import org.gvsig.tools.dynobject.DynStruct;
89
import org.gvsig.tools.dynobject.Tags;
90
import org.gvsig.tools.dynobject.exception.DynFieldIsNotAContainerException;
91
import org.gvsig.tools.dynobject.exception.DynFieldValidateException;
92
import org.gvsig.tools.dynobject.exception.DynMethodException;
93
import org.gvsig.tools.dynobject.impl.DefaultTags;
94
import org.gvsig.tools.evaluator.AbstractEvaluator;
95
import org.gvsig.tools.evaluator.Evaluator;
96
import org.gvsig.tools.evaluator.EvaluatorData;
97
import org.gvsig.tools.evaluator.EvaluatorException;
98
import org.gvsig.tools.i18n.I18nManager;
99
import org.gvsig.tools.persistence.PersistenceManager;
100
import org.gvsig.tools.persistence.Persistent;
101
import org.gvsig.tools.persistence.PersistentState;
102
import org.gvsig.tools.persistence.exception.PersistenceException;
103
import org.gvsig.tools.util.GetItemWithSize;
104
import org.gvsig.tools.util.LabeledValue;
105
import org.slf4j.Logger;
106
import org.slf4j.LoggerFactory;
107

    
108
@SuppressWarnings("UseSpecificCatch")
109
public class DefaultFeatureAttributeDescriptor implements
110
        FeatureAttributeDescriptor, Persistent, DynField_v2, DynField_LabelAttribute {
111

    
112
    protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultFeatureAttributeDescriptor.class);
113

    
114
    protected boolean allowNull;
115
    protected DataType dataType;
116
    protected String dataProfile;
117
    protected DateFormat dateFormat;
118
    protected Object defaultValue;
119
    protected int index;
120
    protected int maximumOccurrences;
121
    protected int minimumOccurrences;
122
    protected int size;
123
    protected String name;
124
    protected Class objectClass;
125
    protected int precision;
126
    protected int scale;
127
    protected int roundMode;
128
    protected Evaluator evaluator;
129
    protected boolean primaryKey;
130
    protected boolean readOnly;
131
    protected IProjection SRS;
132
    protected GeometryType geomType;
133
    protected int geometryType;
134
    protected int geometrySubType;
135
    protected Map<String, String> additionalInfo;
136
    protected boolean isAutomatic;
137
    protected boolean isTime = false;
138
    protected Interval interval;
139
    protected FeatureAttributeGetter featureAttributeGetter = null;
140
    protected FeatureAttributeEmulator featureAttributeEmulator = null;
141
    protected boolean indexed = false;
142
    protected boolean isIndexAscending = true;
143
    protected boolean allowIndexDuplicateds = true;
144

    
145
    protected DynObjectValueItem[] availableValues;
146
    protected DynObjectValueItem[] availableValuesCache; // No persistente
147
    protected Expression availableValuesExpression;
148
    protected boolean avoidCachingAvailableValues;
149
    private Map<Object, String> labelOfValueMap; // No persistente
150
    protected String description;
151
    protected Object minValue;
152
    protected Object maxValue;
153
    protected String label;
154
    protected String shortLabel;
155
    protected int order;
156
    protected boolean hidden;
157
    protected String groupName;
158
    protected Tags tags = new DefaultTags();
159
    private DynMethod availableValuesMethod;
160
    private DynMethod calculateMethod;
161
    private WeakReference typeRef;
162
    protected DefaultForeingKey foreingKey = null;
163

    
164
    protected CoercionContext coerceContext = null; // not persistent
165
    protected MathContext mathContext = null; // not persistent
166

    
167
    private int relationType = RELATION_TYPE_NONE;
168
    protected Locale locale;
169
    protected int displaySize;
170
    
171
    protected String defaultFormat;
172

    
173
    public DefaultFeatureAttributeDescriptor() {
174
        // Usada en la persistencia
175
        this.precision = DataType.PRECISION_NONE;
176
        this.scale = DataType.SCALE_NONE;
177
        this.roundMode = BigDecimal.ROUND_HALF_UP;
178
    }
179

    
180
    protected DefaultFeatureAttributeDescriptor(FeatureType type) {
181
        this();
182
//        LOGGER.info(String.format("Created FeatureAttributeDescriptor [%08x].", this.hashCode()));
183
        setFeatureType(type);
184
        this.allowNull = true;
185
        this.dataType = null;
186
        this.dateFormat = null;
187
        this.defaultValue = null;
188
        this.defaultFormat = null;
189
        this.index = -1;
190
        this.maximumOccurrences = 0;
191
        this.minimumOccurrences = 0;
192
        this.size = 0;
193
        this.name = null;
194
        this.objectClass = null;
195
        this.precision = DataType.PRECISION_NONE;
196
        this.scale = DataType.SCALE_NONE;
197
        this.roundMode = BigDecimal.ROUND_HALF_UP;
198
        this.evaluator = null;
199
        this.primaryKey = false;
200
        this.readOnly = false;
201
        this.SRS = null;
202
        this.geometryType = Geometry.TYPES.NULL;
203
        this.geometrySubType = Geometry.SUBTYPES.UNKNOWN;
204
        this.additionalInfo = null;
205
        this.isAutomatic = false;
206
        this.hidden = false;
207
        this.relationType = RELATION_TYPE_NONE;
208
        this.locale = null;
209
        this.displaySize = 0;
210
        this.avoidCachingAvailableValues = false;
211
    }
212

    
213
    protected DefaultFeatureAttributeDescriptor(
214
            DefaultFeatureAttributeDescriptor other
215
    ) {
216
        this();
217
        copyFrom(other);
218
//        LOGGER.info(String.format("Created FeatureAttributeDescriptor [%08x] [%s] (copy).", this.hashCode(), this.name));
219
    }
220

    
221
    @Override
222
    public void copyFrom(DynField other1) {
223
        if (!(other1 instanceof DefaultFeatureAttributeDescriptor)) {
224
            throw new IllegalArgumentException("Can't copy from a non DefaultFeatureAttributeDescriptor");
225
        }
226
        DefaultFeatureAttributeDescriptor other = (DefaultFeatureAttributeDescriptor) other1;
227
        this.typeRef = other.typeRef;
228
        this.allowNull = other.allowNull;
229
        this.dataType = other.dataType;
230
        this.dateFormat = other.dateFormat;
231
        this.defaultValue = other.defaultValue;
232
        this.defaultFormat = other.defaultFormat;
233
        this.index = other.index;
234
        this.maximumOccurrences = other.maximumOccurrences;
235
        this.minimumOccurrences = other.minimumOccurrences;
236
        this.size = other.size;
237
        this.name = other.name;
238
        this.objectClass = other.objectClass;
239
        this.precision = other.precision;
240
        this.scale = other.scale;
241
        this.roundMode = other.roundMode;
242
        this.evaluator = other.evaluator;
243
        this.primaryKey = other.primaryKey;
244
        this.readOnly = other.readOnly;
245
        this.SRS = other.SRS;
246
        this.geometryType = other.geometryType;
247
        this.geometrySubType = other.geometrySubType;
248
        this.geomType = other.geomType;
249
        if (other.additionalInfo != null) {
250
            this.additionalInfo = new HashMap();
251
            for (Entry<String, String> entry : other.additionalInfo.entrySet()) {
252
                this.additionalInfo.put(entry.getKey(), entry.getValue());
253
            }
254
        } else {
255
            this.additionalInfo = null;
256
        }
257
        this.isAutomatic = other.isAutomatic;
258
        this.isTime = other.isTime;
259
        this.featureAttributeEmulator = other.featureAttributeEmulator;
260
        this.indexed = other.indexed;
261
        this.isIndexAscending = other.isIndexAscending;
262
        this.allowIndexDuplicateds = other.allowIndexDuplicateds;
263
        this.hidden = other.hidden;
264
        this.dataProfile = other.dataProfile;
265

    
266
        this.availableValues = other.availableValues;
267
        this.availableValuesExpression = other.availableValuesExpression;
268
        this.description = other.description;
269
        this.minValue = other.minValue;
270
        this.maxValue = other.maxValue;
271
        this.label = other.label;
272
        this.order = other.order;
273
        this.groupName = other.groupName;
274
        if (other.tags == null) {
275
            this.tags = null;
276
        } else {
277
            try {
278
                this.tags = (Tags) other.tags.clone();
279
            } catch (Exception ex) {
280
            }
281
        }
282
        this.foreingKey = null;
283
        if (other.foreingKey != null) {
284
            try {
285
                this.foreingKey = (DefaultForeingKey) other.foreingKey.clone();
286
            } catch (CloneNotSupportedException ex) {
287
            }
288
        }
289
        if (this.foreingKey != null) {
290
            this.foreingKey.setDescriptor(this);
291
        }
292

    
293
        // TODO: ? Habria que clonarlos ?
294
        this.availableValuesMethod = other.availableValuesMethod;
295
        this.calculateMethod = other.calculateMethod;
296
        this.relationType = other.relationType;
297
        this.locale = other.locale;
298
        this.displaySize = other.displaySize;
299
        this.avoidCachingAvailableValues = other.avoidCachingAvailableValues;
300
    }
301

    
302
    public void setFeatureType(FeatureType type) {
303
        // Usada en la persistencia
304
        if (type == null) {
305
            this.typeRef = null;
306
        } else {
307
            this.typeRef = new WeakReference(type);
308
//            LOGGER.info(String.format("FeatureAttributeDescriptor[%08x] set FeatureType [%08x], ref [%08x].", this.hashCode(), type.hashCode(), typeRef.hashCode()));
309
        }
310
    }
311

    
312
    @Override
313
    public String getDataTypeName() {
314
        if (this.getDataType() == null) {
315
            return "(unknow)";
316
        }
317
        return this.getDataType().getName();
318
    }
319

    
320
    @Override
321
    public DefaultFeatureAttributeDescriptor getCopy() {
322
        return new DefaultFeatureAttributeDescriptor(this);
323
    }
324

    
325
    @Override
326
    public Object clone() throws CloneNotSupportedException {
327
        return new DefaultFeatureAttributeDescriptor(this);
328
    }
329

    
330
    @Override
331
    public boolean allowNull() {
332
//        TODO: Habria que meter este cambio en proximos builds 2022/11/16.
333
//        if( this.isPrimaryKey() ) {
334
//            return false;
335
//        }
336
        return allowNull;
337
    }
338

    
339
    @Override
340
    public Locale getLocale() {
341
        // FIXME: Debe devolver el locale que toca ENGLISH o default, 
342
        // pero no asignarlo a "locale", manteniendo el null en la variable.
343
        // Ojo que tambien habria que tocar el setLocale.
344
        if (this.locale == null) {
345
            if (this.dataType.isNumeric()) {
346
                this.locale = Locale.ENGLISH; //return
347
            } else {
348
                this.locale = Locale.getDefault();
349
            }
350
        }
351
        return this.locale;
352
    }
353

    
354
    @Override
355
    public DataType getDataType() {
356
        if (featureAttributeGetter != null) {
357
            return featureAttributeGetter.getDataType();
358
        }
359
        return this.dataType;
360
    }
361

    
362
    public FeatureAttributeDescriptor setDataType(int type) {
363
        this.dataType = ToolsLocator.getDataTypesManager().get(type);
364
        return this;
365
    }
366

    
367
    @Override
368
    public DateFormat getDateFormat() {
369
        return this.dateFormat;
370
    }
371

    
372
    @Override
373
    public Object getDefaultValue() {
374
        return this.defaultValue;
375
    }
376

    
377
    @Override
378
    @Deprecated
379
    public Object getDefaultValueCoerced() {
380
        return getCoercedDefaultValue();
381
    }
382

    
383
    @Override
384
    public Object getCoercedDefaultValue() {
385
        try {
386
            Object value = this.defaultValue;
387
            if (value == null) {
388
                return null;
389
            }
390
            if (ExpressionUtils.isDynamicText(value.toString())) {
391
                value = ExpressionUtils.evaluateDynamicText(value.toString());
392
            }
393
            return this.getDataType().coerce(value);
394
        } catch (CoercionException ex) {
395
            return null;
396
        }
397
    }
398

    
399
    @Override
400
    public Supplier getDefaultValueSupplier() {
401
        return (Supplier) this::getDefaultValueCoerced;
402
    }
403

    
404
    @Override
405
    public DynField setDefaultValueSupplier(Supplier supplier) {
406
        //Do nothing
407
        return this;
408
    }
409

    
410
    @Override
411
    public Evaluator getEvaluator() {
412
        return this.evaluator;
413
    }
414

    
415
    @Override
416
    public int getGeometryType() {
417
        if (this.dataType.getType() != DataTypes.GEOMETRY) {
418
            return Geometry.TYPES.UNKNOWN;
419
        }
420
        return this.geometryType;
421
    }
422

    
423
    @Override
424
    public int getGeometrySubType() {
425
        if (this.dataType.getType() != DataTypes.GEOMETRY) {
426
            return Geometry.SUBTYPES.UNKNOWN;
427
        }
428
        return this.geometrySubType;
429
    }
430

    
431
    @Override
432
    public GeometryType getGeomType() {
433
        if (this.dataType.getType() != DataTypes.GEOMETRY) {
434
            return null;
435
        }
436
        if (this.geomType == null) {
437
            if((this.geometryType == Geometry.TYPES.UNKNOWN || this.geometryType == Geometry.TYPES.NULL) && 
438
                    (this.geometrySubType == Geometry.SUBTYPES.UNKNOWN)){
439
                return null;
440
            }
441
            try {
442
                this.geomType
443
                        = GeometryLocator.getGeometryManager().getGeometryType(
444
                                this.geometryType, this.geometrySubType);
445
            } catch (GeometryException e) {
446
                throw new RuntimeException(
447
                        "Error getting geometry type with type = "
448
                        + this.geometryType + ", subtype = "
449
                        + this.geometrySubType, e);
450
            }
451
        }
452
        return this.geomType;
453
    }
454

    
455
    @Override
456
    public int getIndex() {
457
        return this.index;
458
    }
459

    
460
    protected FeatureAttributeDescriptor setIndex(int index) {
461
        this.index = index;
462
        return this;
463
    }
464

    
465
    @Override
466
    public int getMaximumOccurrences() {
467
        return this.maximumOccurrences;
468
    }
469

    
470
    @Override
471
    public int getMinimumOccurrences() {
472
        return this.minimumOccurrences;
473
    }
474

    
475
    @Override
476
    public String getName() {
477
        return this.name;
478
    }
479

    
480
    public FeatureAttributeDescriptor setName(String name) {
481
//        LOGGER.info(String.format("FeatureAttributeDescriptor[%08x] set name [%s].", this.hashCode(), name));
482
        this.name = name;
483
        return this;
484
    }
485

    
486
    @Override
487
    public Class getObjectClass() {
488
        if (getDataType().getType() == DataTypes.OBJECT) {
489
            return objectClass;
490
        }
491
        return getDataType().getDefaultClass();
492
    }
493

    
494
    @Override
495
    public int getPrecision() {
496
        return this.precision;
497
    }
498

    
499
    @Override
500
    public int getScale() {
501
        return this.scale;
502
    }
503

    
504
    @Override
505
    public Coercion getCoercion() {
506
        return this.getDataType().getCoercion();
507
    }
508

    
509
    @Override
510
    public MathContext getMathContext() {
511
        if (this.mathContext == null) {
512
            if (this.getDataType().isNumeric()) {
513
                this.mathContext = new MathContext(
514
                        this.getPrecision(),
515
                        RoundingMode.valueOf(this.getRoundMode())
516
                );
517
            } else {
518
                this.mathContext = MathContext.UNLIMITED;
519
            }
520
        }
521
        return this.mathContext;
522
    }
523

    
524
    @Override
525
    public CoercionContext getCoercionContext() {
526
        if (this.coerceContext == null) {
527
            if (this.getDataType().isNumeric()) {
528
                this.coerceContext = DataTypeUtils.coerceContextDecimal(
529
                        this.getLocale(),
530
                        this.getPrecision(),
531
                        this.getScale(),
532
                        this.getRoundMode()
533
                );
534
            } else if (this.getType() == DataTypes.GEOMETRY) {
535
                GeometryCoercionContext context = GeometryLocator.getGeometryManager().createGeometryCoercionContext();
536
                context.setGeometryType(this.getGeomType());
537
                context.setMode(GeometryCoercionContext.MODE_ONERROR_DONTCONVERT);
538
                this.coerceContext = context;
539
            } else if (this.getType() == DataTypes.TIMESTAMP) {
540
//                if(this.locale != null){
541
                    this.coerceContext = DataTypeUtils.coerceContextLocale(
542
                            this.locale
543
                    );
544
//                }
545
            } else {
546
                this.coerceContext = DataTypeUtils.coerceContextLocale(
547
                        this.getLocale()
548
                );
549
            }
550
        }
551
        return this.coerceContext;
552
    }
553

    
554
    @Override
555
    public int getRoundMode() {
556
        return this.roundMode;
557
    }
558

    
559
    @Override
560
    public IProjection getSRS() {
561
        return this.SRS;
562
    }
563

    
564
    @Override
565
    public Interval getInterval() {
566
        return this.interval;
567
    }
568

    
569
    public IProjection getSRS(WeakReference storeRef) {
570
        if (this.SRS == null) {
571
            FeatureStore store = (FeatureStore) storeRef.get();
572
            this.SRS = (IProjection) store.getDynValue(DataStore.METADATA_CRS);
573
        }
574
        return this.SRS;
575
    }
576

    
577
    @Override
578
    public int getSize() {
579
        return this.size;
580
    }
581

    
582
    @Override
583
    public boolean isPrimaryKey() {
584
        return this.primaryKey;
585
    }
586

    
587
    @Override
588
    public boolean isReadOnly() {
589
        if (this.readOnly) {
590
            return true;
591
        }
592
        return this.isComputed();
593
    }
594

    
595
    @Override
596
    public String getAdditionalInfo(String infoName) {
597
        if (this.additionalInfo == null) {
598
            return null;
599
        }
600
        return this.additionalInfo.get(infoName);
601
    }
602

    
603
    @Override
604
    public boolean isAutomatic() {
605
        return this.isAutomatic;
606
    }
607

    
608
    @Override
609
    public boolean equals(Object obj) {
610
        if (this == obj) {
611
            return true;
612
        }
613
        if (!(obj instanceof DefaultFeatureAttributeDescriptor)) {
614
            return false;
615
        }
616
        DefaultFeatureAttributeDescriptor other
617
                = (DefaultFeatureAttributeDescriptor) obj;
618

    
619
        if (this.allowNull != other.allowNull) {
620
            return false;
621
        }
622

    
623
        if (this.index != other.index) {
624
            return false;
625
        }
626

    
627
        if (!Objects.equals(this.name, other.name)) {
628
            return false;
629
        }
630

    
631
        if (this.getDataType() != other.getDataType()) {
632
            return false;
633
        }
634

    
635
        if (this.size != other.size) {
636
            return false;
637
        }
638

    
639
        if (!Objects.equals(this.defaultValue, other.defaultValue)) {
640
            return false;
641
        }
642
        if (!Objects.equals(this.defaultFormat, other.defaultFormat)) {
643
            return false;
644
        }
645

    
646
        if (this.primaryKey != other.primaryKey) {
647
            return false;
648
        }
649

    
650
        if (this.isAutomatic != other.isAutomatic) {
651
            return false;
652
        }
653

    
654
        if (this.readOnly != other.readOnly) {
655
            return false;
656
        }
657

    
658
        if (this.precision != other.precision) {
659
            return false;
660
        }
661

    
662
        if (this.maximumOccurrences != other.maximumOccurrences) {
663
            return false;
664
        }
665

    
666
        if (this.minimumOccurrences != other.minimumOccurrences) {
667
            return false;
668
        }
669

    
670
        if (this.geometryType != other.geometryType) {
671
            return false;
672
        }
673

    
674
        if (this.geometrySubType != other.geometrySubType) {
675
            return false;
676
        }
677

    
678
        if (!Objects.equals(this.evaluator, other.evaluator)) {
679
            return false;
680
        }
681

    
682
        if (!Objects.equals(this.featureAttributeEmulator, other.featureAttributeEmulator)) {
683
            return false;
684
        }
685

    
686
        if (!Objects.equals(this.SRS, other.SRS)) {
687
            return false;
688
        }
689

    
690
        if (!Objects.equals(this.dateFormat, other.dateFormat)) {
691
            return false;
692
        }
693

    
694
        if (!Objects.equals(this.objectClass, other.objectClass)) {
695
            return false;
696
        }
697

    
698
        if (!Objects.equals(this.dataProfile, other.dataProfile)) {
699
            return false;
700
        }
701

    
702
        return true;
703
    }
704

    
705
    @Override
706
    public void loadFromState(PersistentState state)
707
            throws PersistenceException {
708
        allowNull = state.getBoolean("allowNull");
709
        dataType = ToolsLocator.getDataTypesManager().get(state.getInt("dataType"));
710
        dataProfile = state.getString("dataProfile");
711

    
712
//        FIXME: dateFormat;
713
        try {
714
            defaultValue = state.getString("defaultValue");
715
            if( !ExpressionUtils.isDynamicText((String) defaultValue) ) {
716
                defaultValue = dataType.coerce(defaultValue);
717
            }
718
        } catch (CoercionException ex) {
719
        }
720

    
721
        index = state.getInt("index");
722
        maximumOccurrences = state.getInt("maximumOccurrences");
723
        minimumOccurrences = state.getInt("minimumOccurrences");
724
        size = state.getInt("size");
725
        name = state.getString("name");
726
        try {
727
            String objectClassName = state.getString("objectClass");
728
            if (!StringUtils.isBlank(objectClassName)) {
729
                objectClass = Class.forName(objectClassName);
730
            }
731
        } catch (Throwable e) {
732
            LOGGER.warn("Can't restore the objectClass of the FeatureAttributreDescriptor", e);
733
        }
734
        precision = state.getInt("precision");
735
        scale = state.getInt("scale");
736
        roundMode = state.getInt("roundMode");
737
        String locale_s = state.getString("locale");
738
        locale = (StringUtils.isBlank(locale_s) || "null".equalsIgnoreCase(locale_s)) ? null : Locale.forLanguageTag(locale_s);
739
        evaluator = (Evaluator) state.get("evaluator");
740
        primaryKey = state.getBoolean("primaryKey");
741
        readOnly = state.getBoolean("readOnly");
742
        SRS = (IProjection) state.get("SRS");
743
        geometryType = state.getInt("geometryType");
744
        geometrySubType = state.getInt("geometrySubType");
745
        if (geometryType != Geometry.TYPES.UNKNOWN
746
                && geometrySubType != Geometry.SUBTYPES.UNKNOWN) {
747
            geomType = GeometryUtils.getGeometryType(
748
                    geometryType,
749
                    geometrySubType
750
            );
751
        }
752
//        additionalInfo = (Map) state.get("aditionalInfo");
753
        isAutomatic = state.getBoolean("isAutomatic");
754
        isTime = state.getBoolean("isTime");
755
        if (state.hasValue("intervalStart")) {
756
            long intervalStart = state.getLong("interval_start");
757
            long intervalEnd = state.getLong("interval_end");
758
            interval = TimeSupportLocator.getManager().createRelativeInterval(intervalStart, intervalEnd);
759
        } else {
760
            interval = null;
761
        }
762
        featureAttributeEmulator = (FeatureAttributeEmulator) state.get("featureAttributeEmulator");
763
        indexed = state.getBoolean("indexed");
764
        isIndexAscending = state.getBoolean("isIndexAscending");
765
        allowIndexDuplicateds = state.getBoolean("allowIndexDuplicateds");
766

    
767
        Map<String, Object> values = state.getMap("availableValues");
768
        if (values == null || values.isEmpty()) {
769
            this.availableValues = null;
770
        } else {
771
            this.availableValues = new DynObjectValueItem[values.size()];
772
            int n = 0;
773
            Coercion coercion = this.getCoercion();
774
            for (Entry<String, Object> entry : values.entrySet()) {
775
                Object value;
776
                try {
777
                    value = coercion.coerce(entry.getValue());
778
                } catch (CoercionException ex) {
779
                    value = entry.getValue();
780
                }
781
                this.availableValues[n++] = new DynObjectValueItem(value, entry.getKey());
782
            }
783
        }
784

    
785
        description = state.getString("description");
786
        minValue = state.get("minValue");
787
        maxValue = state.get("maxValue");
788
        label = state.getString("label");
789
        order = state.getInt("order");
790
        hidden = state.getBoolean("hidden");
791
        groupName = state.getString("groupName");
792
        relationType = state.getInt("relationType", RELATION_TYPE_NONE);
793

    
794
        foreingKey = (DefaultForeingKey) state.get("foreingKey");
795
        if (foreingKey != null) {
796
            this.foreingKey.setDescriptor(this);
797
        }
798
        tags = (Tags) state.get("tags");
799
        if (tags == null) {
800
            this.tags = new DefaultTags();
801
        }
802
        displaySize = state.getInt("displaySize", 0);
803
        availableValuesExpression = (Expression) state.get("availableValuesExpression");
804
        avoidCachingAvailableValues = state.getBoolean("avoidCachingAvailableValues", false);
805
        availableValuesCache = null;
806
        defaultFormat = state.getString("defaultFormat");
807
    }
808

    
809
    @Override
810
    public void saveToState(PersistentState state) throws PersistenceException {
811
        Coercion toString = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.STRING);
812

    
813
        state.set("allowNull", allowNull);
814
        state.set("dataType", dataType.getType());
815
        state.set("dataProfile", dataProfile);
816

    
817
//        FIXME: dateFormat;
818
        state.set("defaultValue", Objects.toString(defaultValue, null));
819

    
820
        state.set("index", index);
821
        state.set("maximumOccurrences", maximumOccurrences);
822
        state.set("minimumOccurrences", minimumOccurrences);
823
        state.set("size", size);
824
        state.set("name", name);
825
        state.set("objectClass", objectClass == null ? null : objectClass.getName());
826
        state.set("precision", precision);
827
        state.set("scale", scale);
828
        state.set("roundMode", roundMode);
829
        if (this.locale == null) {
830
            state.setNull("locale");
831
        } else {
832
            state.set("locale", this.locale.toLanguageTag()); //toString.coerce(this.locale));
833
        }
834
        state.set("evaluator", evaluator);
835

    
836
        state.set("primaryKey", primaryKey);
837
        state.set("readOnly", readOnly);
838
        state.set("SRS", SRS);
839
        GeometryType theGeomType = this.getGeomType();
840
        if (theGeomType == null) {
841
            state.set("geometryType", Geometry.TYPES.UNKNOWN);
842
            state.set("geometrySubType", Geometry.SUBTYPES.UNKNOWN);
843
        } else {
844
            state.set("geometryType", theGeomType.getType());
845
            state.set("geometrySubType", theGeomType.getSubType());
846
        }
847

    
848
//      FIXME: additionalInfo
849
        state.set("isAutomatic", isAutomatic);
850
        state.set("isTime", isTime);
851
        if (this.interval == null) {
852
            state.setNull("interval_start");
853
            state.setNull("interval_end");
854
        } else {
855
            state.set("interval_start", ((RelativeInterval) interval).getStart().toMillis());
856
            state.set("interval_end", ((RelativeInterval) interval).getEnd().toMillis());
857
        }
858
        state.set("SRS", SRS);
859

    
860
//      FIXME: featureAttributeGetter
861
        if (featureAttributeEmulator instanceof Persistent) {
862
            state.set("featureAttributeEmulator", featureAttributeEmulator);
863
        } else {
864
            state.setNull("featureAttributeEmulator");
865
        }
866

    
867
        state.set("indexed", indexed);
868
        state.set("isIndexAscending", isIndexAscending);
869
        state.set("allowIndexDuplicateds", allowIndexDuplicateds);
870

    
871
        if (this.availableValues == null) {
872
            state.setNull("availableValues");
873
        } else {
874
            Map<String, Object> values = new LinkedHashMap<>();
875
            for (DynObjectValueItem value : availableValues) {
876
                if( value!=null ) {
877
                    values.put(value.getLabel(), value.getValue());
878
                }
879
            }
880
            state.set("availableValues", values);
881
        }
882
        state.set("description", description);
883
        state.set("minValue", minValue);
884
        state.set("maxValue", maxValue);
885
        state.set("label", label);
886
        state.set("order", order);
887
        state.set("hidden", hidden);
888
        state.set("groupName", groupName);
889
        state.set("relationType", relationType);
890

    
891
        state.set("foreingKey", this.foreingKey);
892
        state.set("tags", this.tags);
893

    
894
        state.set("displaySize", displaySize);
895
        state.set("availableValuesExpression", availableValuesExpression);
896
        state.set("avoidCachingAvailableValues", avoidCachingAvailableValues);
897
        state.set("defaultFormat", defaultFormat);
898
    }
899

    
900
    private static final String FEATATTRDESC_PERSISTENCE_DEFINITION_NAME = "FeatureAttributeDescriptor";
901

    
902
    public static void registerPersistenceDefinition() {
903
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
904

    
905
        if (manager.getDefinition(FEATATTRDESC_PERSISTENCE_DEFINITION_NAME)
906
                == null) {
907
            DynStruct definition = manager.addDefinition(DefaultFeatureAttributeDescriptor.class,
908
                    FEATATTRDESC_PERSISTENCE_DEFINITION_NAME,
909
                    FEATATTRDESC_PERSISTENCE_DEFINITION_NAME
910
                    + " persistent definition",
911
                    null,
912
                    null
913
            );
914
            definition.addDynFieldBoolean("allowNull");
915
            definition.addDynFieldInt("dataType");
916
            definition.addDynFieldString("dataProfile");
917
//            definition.addDynFieldString("dateFormat");
918
            definition.addDynFieldString("defaultValue");
919
            definition.addDynFieldInt("index");
920
            definition.addDynFieldInt("maximumOccurrences");
921
            definition.addDynFieldInt("minimumOccurrences");
922
            definition.addDynFieldInt("size");
923
            definition.addDynFieldString("name");
924
            definition.addDynFieldString("objectClass");
925
            definition.addDynFieldInt("precision");
926
            definition.addDynFieldInt("scale").setMandatory(false).setDefaultDynValue(DataType.SCALE_NONE);
927
            definition.addDynFieldInt("roundMode").setMandatory(false).setDefaultDynValue(BigDecimal.ROUND_HALF_UP);
928
            definition.addDynFieldString("locale").setMandatory(false).setDefaultDynValue("null");
929
            definition.addDynFieldObject("evaluator").setClassOfValue(Evaluator.class);
930
            definition.addDynFieldBoolean("primaryKey");
931
            definition.addDynFieldBoolean("readOnly");
932
            definition.addDynFieldObject("SRS")
933
                    .setClassOfValue(IProjection.class);
934
            definition.addDynFieldInt("geometryType");
935
            definition.addDynFieldInt("geometrySubType");
936
//            definition.addDynFieldMap("additionalInfo");
937
            definition.addDynFieldBoolean("isAutomatic");
938
            definition.addDynFieldBoolean("isTime");
939
            definition.addDynFieldLong("interval_start");
940
            definition.addDynFieldLong("interval_end");
941
            definition.addDynFieldObject("featureAttributeEmulator")
942
                    .setClassOfValue(FeatureAttributeEmulator.class);
943
            definition.addDynFieldBoolean("indexed");
944
            definition.addDynFieldBoolean("isIndexAscending");
945
            definition.addDynFieldBoolean("allowIndexDuplicateds");
946
            definition.addDynFieldMap("availableValues")
947
                    .setClassOfItems(Object.class);
948
            definition.addDynFieldString("description");
949
            definition.addDynFieldObject("minValue");
950
            definition.addDynFieldObject("maxValue");
951
            definition.addDynFieldString("label");
952
            definition.addDynFieldInt("order");
953
            definition.addDynFieldBoolean("hidden");
954
            definition.addDynFieldBoolean("avoidCachingAvailableValues");
955
            definition.addDynFieldString("groupName");
956
            definition.addDynFieldInt("relationType");
957

    
958
            definition.addDynFieldObject("foreingKey")
959
                    .setClassOfValue(DefaultForeingKey.class);
960

    
961
            definition.addDynFieldObject("tags")
962
                    .setClassOfValue(Tags.class);
963

    
964
            definition.addDynFieldInt("displaySize").setMandatory(false);
965
            definition.addDynFieldObject("availableValuesExpression")
966
                    .setClassOfValue(Expression.class)
967
                    .setMandatory(false);
968
            definition.addDynFieldString("defaultFormat").setMandatory(false);
969
        }
970
    }
971

    
972
    /*
973
     * Start of DynField interface Implementation
974
     *
975
     */
976
    @Override
977
    public Tags getTags() {
978
        return tags;
979
    }
980

    
981
    private Expression availableValuesFilter;
982

    
983
    @Override
984
    public Expression getAvailableValuesFilter() {
985
        return this.availableValuesFilter;
986
    }
987

    
988
    public FeatureAttributeDescriptor setAvailableValuesFilter(Expression filter) {
989
        this.availableValuesFilter = filter;
990
        return this;
991
    }
992

    
993
    public FeatureAttributeDescriptor setAvailableValuesFilter(String filter) {
994
        this.availableValuesFilter = ExpressionUtils.createExpression(filter);
995
        return this;
996
    }
997

    
998
    @Override
999
    public boolean hasConstantAvailableValues() {
1000
        return this.availableValues != null;
1001
    }
1002

    
1003
    @Override
1004
    public boolean isAvoidCachingAvailableValues() {
1005
        return this.avoidCachingAvailableValues;
1006
    }
1007

    
1008
    @Override
1009
    public boolean hasAvailableValues() {
1010
        return getAvailableValues() != null;
1011
    }
1012

    
1013
    @Override
1014
    public DynObjectValueItem[] getAvailableValues(DynObject context) {
1015
        if (this.availableValuesMethod != null) {
1016
            DynObjectValueItem[] values;
1017
            try {
1018
                values = (DynObjectValueItem[]) this.availableValuesMethod.invoke(context, new Object[]{this});
1019
            } catch (DynMethodException ex) {
1020
                return this.getAvailableValues();
1021
            }
1022
            if (values != null) {
1023
                return values;
1024
            }
1025
        }
1026
        Expression filter = this.availableValuesFilter;
1027
        if (!ExpressionUtils.isEmpty(filter)) {
1028
            if (this.isForeingKey() && this.foreingKey.isClosedList()) {
1029
                ContextForeingKey foreingkeyContext = null;
1030
                try {
1031
                    foreingkeyContext = this.foreingKey.createContext();
1032
                    foreingkeyContext.setContextValues(context);
1033
                    DynObjectValueItem[] values = this.foreingKey.getAvailableValues(foreingkeyContext);
1034
                    return values;
1035
                } finally {
1036
                    DisposeUtils.disposeQuietly(foreingkeyContext);
1037
                }
1038
            }
1039
            MutableSymbolTable contextSymbolTable = ExpressionUtils.createSymbolTable("feature", context);
1040
            MutableSymbolTable symbolTable = ExpressionUtils.createSymbolTable();
1041
            symbolTable.addSymbolTable(contextSymbolTable);
1042

    
1043
            DynObjectValueItem[] allValues = this.getAvailableValues();
1044
            List<DynObjectValueItem> filteredValues = new ArrayList<>();
1045
            for (DynObjectValueItem value : allValues) {
1046
                symbolTable.setVar("$value", value.getValue());
1047
                symbolTable.setVar("$label", value.getLabel());
1048
                Object include = filter.execute(symbolTable);
1049
                if (DataTypeUtils.isFalse(include, false)) {
1050
                    continue;
1051
                }
1052
                filteredValues.add(value);
1053
            }
1054
            DynObjectValueItem[] values = filteredValues.toArray(
1055
                    new DynObjectValueItem[filteredValues.size()]
1056
            );
1057
            return values;
1058
        }
1059
        return this.getAvailableValues();
1060
    }
1061

    
1062
    @Override
1063
    public boolean isAvailableValuesCalculated() {
1064
        if (this.availableValuesMethod != null) {
1065
            return true;
1066
        }
1067
        if (!ExpressionUtils.isEmpty(this.availableValuesFilter)) {
1068
            return true;
1069
        }
1070
        if( this.isForeingKey() && this.foreingKey.isClosedList() ) {
1071
            return true;
1072
        }
1073
        return false;
1074
    }
1075

    
1076
    @Override
1077
    public DynObjectValueItem[] getAvailableValues() {
1078
        DynObjectValueItem[] values = this.availableValues;
1079

    
1080
        if (values != null) {
1081
            return values;
1082
        }
1083
        if (this.isForeingKey() && this.foreingKey.isClosedList()) {
1084
            values = this.foreingKey.getAvailableValues(null);
1085
            return values;
1086
        }
1087
        values = this.availableValuesCache;
1088
        if (values != null) {
1089
            return values;
1090
        }
1091
//        if (this.isForeingKey() && this.foreingKey.isClosedList()) {
1092
//            values = this.foreingKey.getAvailableValues(null);
1093
//
1094
//        } else 
1095
        if (this.availableValuesExpression != null) {
1096
            values = this.getAvailableValuesFromExpression();
1097
        }
1098
        if (!this.avoidCachingAvailableValues) {
1099
            this.availableValuesCache = values;
1100
        }
1101
        return values;
1102
    }
1103

    
1104
    private DynObjectValueItem[] getAvailableValuesFrom(GetItemWithSize values) {
1105
        if (values.size() == 0) {
1106
            return null;
1107
        }
1108
        DynObjectValueItem[] r = null;
1109
        Object firstelement = values.get(0);
1110
        if (firstelement instanceof LabeledValue) {
1111
            r = new DynObjectValueItem[values.size()];
1112
            for (int i = 0; i < values.size(); i++) {
1113
                LabeledValue v = (LabeledValue) values.get(i);
1114
                r[i] = new DynObjectValueItem(
1115
                        v.getValue(),
1116
                        Objects.toString(v.getLabel(), Objects.toString(v.getValue(), String.valueOf(i)))
1117
                );
1118
            }
1119
        } else if (firstelement instanceof Pair) {
1120
            r = new DynObjectValueItem[values.size()];
1121
            for (int i = 0; i < values.size(); i++) {
1122
                Pair v = (Pair) values.get(i);
1123
                r[i] = new DynObjectValueItem(
1124
                        v.getValue(),
1125
                        Objects.toString(v.getKey(), Objects.toString(v.getValue(), String.valueOf(i)))
1126
                );
1127
            }
1128
        } else if (firstelement instanceof JsonObject) {
1129
            JsonObject v = (JsonObject) firstelement;
1130
            String labelname = null;
1131
            for (String theName : new String[]{
1132
                "name", "label", "key", "description"
1133
            }) {
1134
                if (v.containsKey(theName)) {
1135
                    labelname = theName;
1136
                    break;
1137
                }
1138
                if (v.containsKey(theName.toUpperCase())) {
1139
                    labelname = theName.toLowerCase();
1140
                    break;
1141
                }
1142
            }
1143
            String valuename = null;
1144
            if (v.containsKey("value")) {
1145
                valuename = "value";
1146
            }
1147
            r = new DynObjectValueItem[values.size()];
1148
            for (int i = 0; i < values.size(); i++) {
1149
                v = (JsonObject) values.get(i);
1150
                String theLabel;
1151
                Object theValue;
1152
                if (labelname == null) {
1153
                    theLabel = v.toString();
1154
                } else {
1155
                    theLabel = v.getString(labelname);
1156
                }
1157
                if (valuename == null) {
1158
                    theValue = v.getString(valuename);
1159
                } else {
1160
                    theValue = v;
1161
                }
1162
                r[i] = new DynObjectValueItem(theValue, theLabel);
1163
            }
1164
        } else if (firstelement instanceof Map) {
1165
            Map<String, Object> v = (Map<String, Object>) firstelement;
1166
            String labelname = null;
1167
            for (String theName : new String[]{
1168
                "name", "label", "key", "description"
1169
            }) {
1170
                if (v.containsKey(theName)) {
1171
                    labelname = theName;
1172
                    break;
1173
                }
1174
                if (v.containsKey(theName.toUpperCase())) {
1175
                    labelname = theName.toLowerCase();
1176
                    break;
1177
                }
1178
            }
1179
            String valuename = null;
1180
            if (v.containsKey("value")) {
1181
                valuename = "value";
1182
            }
1183
            r = new DynObjectValueItem[values.size()];
1184
            for (int i = 0; i < values.size(); i++) {
1185
                v = (Map<String, Object>) values.get(i);
1186
                String theLabel;
1187
                Object theValue;
1188
                if (labelname == null) {
1189
                    theLabel = v.toString();
1190
                } else {
1191
                    theLabel = (String) v.get(labelname);
1192
                }
1193
                if (valuename == null) {
1194
                    theValue = v;
1195
                } else {
1196
                    theValue = v.get(valuename);
1197
                }
1198
                r[i] = new DynObjectValueItem(theValue, theLabel);
1199
            }
1200
        } else if (firstelement instanceof Feature) {
1201
            Feature v = (Feature) firstelement;
1202
            FeatureType featureType = v.getType();
1203
            String valuename = null;
1204
            FeatureAttributeDescriptor[] pks = featureType.getPrimaryKey();
1205
            if (pks != null && pks.length == 1) {
1206
                valuename = pks[0].getName();
1207
            }
1208
            String labelname = null;
1209
            for (String theName : new String[]{
1210
                "name", "label", "key", "description"
1211
            }) {
1212
                if (featureType.get(theName) != null) {
1213
                    labelname = theName;
1214
                    break;
1215
                }
1216
            }
1217
            r = new DynObjectValueItem[values.size()];
1218
            for (int i = 0; i < values.size(); i++) {
1219
                v = (Feature) values.get(i);
1220
                String theLabel;
1221
                Object theValue;
1222
                if (labelname == null) {
1223
                    theLabel = v.toString();
1224
                } else {
1225
                    theLabel = v.getString(labelname);
1226
                }
1227
                if (valuename == null) {
1228
                    theValue = v.getReference().getCode();
1229
                } else {
1230
                    theValue = v.get(valuename);
1231
                }
1232
                r[i] = new DynObjectValueItem(theValue, theLabel);
1233
            }
1234
        }
1235
        return r;
1236
    }
1237

    
1238
    private DynObjectValueItem[] getAvailableValuesFromExpression() {
1239
        if (this.availableValuesExpression == null || this.availableValuesExpression.isEmpty()) {
1240
            return null;
1241
        }
1242
        try {
1243
            Object value = this.availableValuesExpression.execute(null);
1244
            if (value instanceof DynObjectValueItem[]) {
1245
                return (DynObjectValueItem[]) value;
1246
            }
1247
            if (value instanceof List) {
1248
                return this.getAvailableValuesFrom(new GetItemWithSize() {
1249
                    @Override
1250
                    public Object get(int i) {
1251
                        return ((List) value).get(i);
1252
                    }
1253

    
1254
                    @Override
1255
                    public int size() {
1256
                        return ((List) value).size();
1257
                    }
1258
                });
1259
            }
1260
        } catch (Throwable th) {
1261
            LOGGER.warn("Can't get available values from expression", th);
1262
        }
1263
        return null;
1264
    }
1265

    
1266
    @Override
1267
    public Expression getAvailableValuesExpression() {
1268
        return this.availableValuesExpression;
1269
    }
1270

    
1271
    @Override
1272
    public FeatureAttributeDescriptor setAvailableValuesExpression(String expression) {
1273
        if (StringUtils.isBlank(expression)) {
1274
            this.availableValuesExpression = null;
1275
            return this;
1276
        }
1277
        this.availableValuesExpression = ExpressionUtils.createExpression(expression);
1278
        return this;
1279
    }
1280

    
1281
    @Override
1282
    public FeatureAttributeDescriptor setAvailableValuesExpression(Expression expression) {
1283
        this.availableValuesExpression = expression;
1284
        return this;
1285
    }
1286

    
1287
    @Override
1288
    public String getLabelOfValue(Object value) {
1289
        String theLabel = null;
1290
        if( this.isForeingKey() && !this.getForeingKey().isClosedList() ) {
1291
            theLabel = this.getForeingKey().getLabel(null, value);
1292
            if( StringUtils.isNotBlank(theLabel) ) {
1293
                return theLabel;
1294
            }
1295
        }
1296
        if (this.labelOfValueMap != null) {
1297
            theLabel = this.labelOfValueMap.get(value);
1298
            if (theLabel == null) {
1299
                theLabel = Objects.toString(value, "");
1300
            }
1301
            return theLabel;
1302
        }
1303
        DynObjectValueItem[] values = this.getAvailableValues();
1304
        if (values == null) {
1305
            return this.format(value);
1306
        }
1307
        Map<Object, String> map = new LinkedHashMap<>();
1308
        for (DynObjectValueItem theValue : values) {
1309
            map.put(theValue.getValue(), theValue.getLabel());
1310
        }
1311
        this.labelOfValueMap = map;
1312
        theLabel = this.labelOfValueMap.get(value);
1313
        if (theLabel == null) {
1314
            return this.format(value);
1315
        }
1316
        return theLabel;
1317
    }
1318

    
1319
    @Override
1320
    public String getDescription() {
1321
        if (this.description == null) {
1322
            return getName();
1323
        }
1324
        return this.description;
1325
    }
1326

    
1327
    @Override
1328
    public Object getMaxValue() {
1329
        return this.maxValue;
1330
    }
1331

    
1332
    @Override
1333
    public Object getMinValue() {
1334
        return this.minValue;
1335
    }
1336

    
1337
    @Override
1338
    public int getTheTypeOfAvailableValues() {
1339
        return 1;
1340
    }
1341

    
1342
    @Override
1343
    public int getType() {
1344
        if (featureAttributeGetter != null) {
1345
            return featureAttributeGetter.getDataType().getType();
1346
        }
1347
        return getDataType().getType();
1348
    }
1349

    
1350
    @Override
1351
    public boolean isMandatory() {
1352
        return !allowNull() || isPrimaryKey();
1353
    }
1354

    
1355
    @Override
1356
    public boolean isPersistent() {
1357
        return false;
1358
    }
1359

    
1360
    @Override
1361
    public DynField setAvailableValues(DynObjectValueItem[] values) {
1362
        if (ArrayUtils.isEmpty(values)) {
1363
            this.availableValues = null;
1364
        } else {
1365
            Coercion coercion = this.getCoercion();
1366
            this.availableValues = new DynObjectValueItem[values.length];
1367
            for (int i = 0; i < values.length; i++) {
1368
                DynObjectValueItem element = values[i];
1369
                Object value;
1370
                try {
1371
                    value = coercion.coerce(element.getValue());
1372
                } catch (CoercionException ex) {
1373
                    value = element.getValue();
1374
                }
1375
                this.availableValues[i] = new DynObjectValueItem(value, element.getLabel());
1376
            }
1377
        }
1378
        return this;
1379
    }
1380

    
1381
    @Override
1382
    public DynField setDescription(String description) {
1383
        this.description = description;
1384
        return this;
1385
    }
1386

    
1387
    @Override
1388
    public DynField setMandatory(boolean mandatory) {
1389
        throw new UnsupportedOperationException();
1390
    }
1391

    
1392
    @Override
1393
    public DynField setMaxValue(Object maxValue) {
1394
        try {
1395
            this.maxValue = this.coerce(maxValue);
1396
        } catch (CoercionException e) {
1397
            throw new IllegalArgumentException(e);
1398
        }
1399
        return this;
1400
    }
1401

    
1402
    @Override
1403
    public DynField setMinValue(Object minValue) {
1404
        try {
1405
            this.maxValue = this.coerce(minValue);
1406
        } catch (CoercionException e) {
1407
            throw new IllegalArgumentException(e);
1408
        }
1409
        return this;
1410
    }
1411

    
1412
    @Override
1413
    public DynField setPersistent(boolean persistent) {
1414
        throw new UnsupportedOperationException();
1415
    }
1416

    
1417
    @Override
1418
    public DynField setTheTypeOfAvailableValues(int type) {
1419
        throw new UnsupportedOperationException();
1420
    }
1421

    
1422
    @Override
1423
    public DynField setType(int type) {
1424
        throw new UnsupportedOperationException();
1425
    }
1426

    
1427
    @Override
1428
    @Deprecated
1429
    public DynField setDefaultDynValue(Object defaultValue) {
1430
        return this.setDefaultFieldValue(defaultValue);
1431
    }
1432

    
1433
    @Override
1434
    public DynField setDefaultFieldValue(Object defaultValue) {
1435
        this.defaultValue = defaultValue;
1436
        return this;
1437
    }
1438

    
1439
    @Override
1440
    public Class getClassOfValue() {
1441
        return null;
1442
    }
1443

    
1444
    @Override
1445
    public DynField getElementsType() {
1446
        return null;
1447
    }
1448

    
1449
    @Override
1450
    public DynField setClassOfValue(Class theClass)
1451
            throws DynFieldIsNotAContainerException {
1452
        throw new UnsupportedOperationException();
1453
    }
1454

    
1455
    @Override
1456
    public DynField setElementsType(DynStruct type)
1457
            throws DynFieldIsNotAContainerException {
1458
        throw new UnsupportedOperationException();
1459
    }
1460

    
1461
    @Override
1462
    public DynField setElementsType(int type)
1463
            throws DynFieldIsNotAContainerException {
1464
        throw new UnsupportedOperationException();
1465
    }
1466

    
1467
    public FeatureAttributeDescriptor setDataProfileName(String dataProfile) {
1468
        this.dataProfile = dataProfile;
1469
        return this;
1470
    }
1471

    
1472
    @Override
1473
    public String getDataProfileName() {
1474
        return dataProfile;
1475
    }
1476

    
1477
    @Override
1478
    public boolean hasDataProfile() {
1479
        return StringUtils.isNotBlank(dataProfile);
1480
    }
1481
    
1482
    @Override
1483
    public DataProfile getDataProfile() {
1484
        if (StringUtils.isBlank(dataProfile)) {
1485
            return null;
1486
        }
1487
        DataProfile profile = DALLocator.getDataManager().getDataProfile(dataProfile);
1488
        return profile;
1489
    }
1490

    
1491
    @Override
1492
    public void validate(Object value) throws DynFieldValidateException {
1493

    
1494
        if (value == null && !this.allowNull()) {
1495
            throw new DynFieldValidateException(value, this, null);
1496
        }
1497

    
1498
        try {
1499
            this.dataType.coerce(value);
1500
        } catch (CoercionException e) {
1501
            throw new DynFieldValidateException(value, this, e);
1502
        }
1503

    
1504
        /*
1505
         * Other checks will be needed
1506
         */
1507
    }
1508

    
1509
    @Override
1510
    public String getSubtype() {
1511
        if (featureAttributeGetter != null) {
1512
            return featureAttributeGetter.getDataType().getSubtype();
1513
        }
1514
        return this.dataType.getSubtype();
1515
    }
1516

    
1517
    @Override
1518
    public Object coerce(Object value) throws CoercionException {
1519
        if (value == null) {
1520
            return value; // O debe devolver this.defaultValue
1521
        }
1522
        try {
1523
            return this.getDataType().coerce(value, this.getCoercionContext());
1524
        } catch (Exception ex) {
1525
            throw new RuntimeException(ex);
1526
        }
1527
    }
1528

    
1529
    @Override
1530
    public DynField setAvailableValues(List values) {
1531
        if (values == null || values.isEmpty()) {
1532
            this.availableValues = null;
1533
        } else {
1534
            this.availableValues = (DynObjectValueItem[]) values.toArray(
1535
                    new DynObjectValueItem[values.size()]
1536
            );
1537
        }
1538
        return this;
1539
    }
1540

    
1541
    @Override
1542
    public String getGroup() {
1543
        return this.groupName;
1544
    }
1545

    
1546
    @Override
1547
    public int getOder() {
1548
        return this.order;
1549
    }
1550

    
1551
    @Override
1552
    public String getLabel() {
1553
        if (this.label == null) {
1554
            return this.getName();
1555
        }
1556
        return this.label;
1557
    }
1558

    
1559
    @Override
1560
    public String getLocalizedLabel() {
1561
        if (StringUtils.isBlank(this.label)) {
1562
            return this.getName();
1563
        }
1564
        I18nManager i18n = ToolsLocator.getI18nManager();
1565
        return i18n.getTranslation(this.label);
1566
    }
1567

    
1568
    @Override
1569
    public DynField setLabel(String label) {
1570
        this.label = label;
1571
        return this;
1572
    }
1573

    
1574
    @Override
1575
    public DynField setShortLabel(String shortLabel) {
1576
        this.shortLabel = shortLabel;
1577
        return this;
1578
    }
1579

    
1580
    @Override
1581
    public String getShortLabel() {
1582
        return StringUtils.isBlank(shortLabel) ? getLabel() : shortLabel;
1583
    }
1584

    
1585
    @Override
1586
    public String getLocalizedShortLabel() {
1587
        if (StringUtils.isBlank(shortLabel)) {
1588
            return this.getLocalizedLabel();
1589
        }
1590
        I18nManager i18n = ToolsLocator.getI18nManager();
1591
        return i18n.getTranslation(shortLabel);
1592
    }
1593

    
1594
    @Override
1595
    public DynField setGroup(String groupName) {
1596
        this.groupName = groupName;
1597
        return this;
1598
    }
1599

    
1600
    @Override
1601
    public DynField setOrder(int order) {
1602
        this.order = order;
1603
        return this;
1604
    }
1605

    
1606
    @Override
1607
    public DynField setHidden(boolean hidden) {
1608
        this.hidden = hidden;
1609
        return this;
1610
    }
1611

    
1612
    @Override
1613
    public boolean isHidden() {
1614
        return this.hidden;
1615
    }
1616

    
1617
    @Override
1618
    public DynField setReadOnly(boolean readOnly) {
1619
        this.readOnly = readOnly;
1620
        return this;
1621
    }
1622

    
1623
    @Override
1624
    public boolean isContainer() {
1625
        return false;
1626
    }
1627

    
1628
    @Override
1629
    public Class getClassOfItems() {
1630
        return null;
1631
    }
1632

    
1633
    @Override
1634
    public DynField setClassOfItems(Class theClass) {
1635
        throw new UnsupportedOperationException();
1636
    }
1637

    
1638
    @Override
1639
    public DynField setType(DataType type) {
1640
        throw new UnsupportedOperationException();
1641
    }
1642

    
1643
    @Override
1644
    public DynField setSubtype(String subtype) {
1645
        throw new UnsupportedOperationException();
1646
    }
1647

    
1648
    @Override
1649
    public boolean isTime() {
1650
        return isTime;
1651
    }
1652

    
1653
    @Override
1654
    public FeatureAttributeGetter getFeatureAttributeGetter() {
1655
        return featureAttributeGetter;
1656
    }
1657

    
1658
    @Override
1659
    public void setFeatureAttributeGetter(
1660
            FeatureAttributeGetter featureAttributeTransform) {
1661
        this.featureAttributeGetter = featureAttributeTransform;
1662
    }
1663

    
1664
    @Override
1665
    public FeatureAttributeEmulator getFeatureAttributeEmulator() {
1666
        return this.featureAttributeEmulator;
1667
    }
1668

    
1669
    public FeatureAttributeDescriptor setFeatureAttributeEmulator(FeatureAttributeEmulator featureAttributeEmulator) {
1670
        this.featureAttributeEmulator = featureAttributeEmulator;
1671
        return this;
1672
    }
1673

    
1674
    @Override
1675
    public boolean isIndexed() {
1676
        return this.indexed;
1677
    }
1678

    
1679
    @Override
1680
    public boolean isForeingKey() {
1681
        return this.foreingKey != null && this.foreingKey.isForeingKey();
1682
    }
1683

    
1684
    @Override
1685
    public ForeingKey getForeingKey() {
1686
        return this.foreingKey;
1687
    }
1688

    
1689
    @Override
1690
    public boolean allowIndexDuplicateds() {
1691
        return this.allowIndexDuplicateds;
1692
    }
1693

    
1694
    @Override
1695
    public boolean isIndexAscending() {
1696
        return this.isIndexAscending;
1697
    }
1698

    
1699
    @Override
1700
    public DynField setClassOfValue(DynStruct dynStrct) {
1701
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1702
    }
1703

    
1704
    @Override
1705
    public DynField setClassOfValue(String theClassNameOfValue) {
1706
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1707
    }
1708

    
1709
    @Override
1710
    public String getClassNameOfValue() {
1711
        return null;
1712
    }
1713

    
1714
    @Override
1715
    public DynStruct getDynClassOfValue() {
1716
        return null;
1717
    }
1718

    
1719
    @Override
1720
    public DynField setTypeOfItems(int type) {
1721
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1722
    }
1723

    
1724
    @Override
1725
    public int getTypeOfItems() {
1726
        return DataTypes.INVALID;
1727
    }
1728

    
1729
    @Override
1730
    public DynField setClassOfItems(DynStruct dynStrct) {
1731
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1732
    }
1733

    
1734
    @Override
1735
    public DynField setClassOfItems(String theClassNameOfValue) {
1736
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1737
    }
1738

    
1739
    @Override
1740
    public String getClassNameOfItems() {
1741
        return null;
1742
    }
1743

    
1744
    @Override
1745
    public DynStruct getDynClassOfItems() {
1746
        return null;
1747
    }
1748

    
1749
    @Override
1750
    public DynField setRelationType(int relationType) {
1751
        this.relationType = relationType;
1752
        return this;
1753
    }
1754

    
1755
    @Override
1756
    public int getRelationType() {
1757
        return this.relationType;
1758
    }
1759

    
1760
    @Override
1761
    public DynField setAvailableValues(DynMethod availableValuesMethod) {
1762
        this.availableValuesMethod = availableValuesMethod;
1763
        return this;
1764
    }
1765

    
1766
    @Override
1767
    public DynMethod getAvailableValuesMethod() {
1768
        if (this.availableValuesMethod != null) {
1769
            return this.availableValuesMethod;
1770
        }
1771
        if (this.availableValuesFilter == null && !this.isForeingKey()) {
1772
            return null;
1773
        }
1774
        DynMethod method = new AbstractDynMethod("getAvailableValuesOf" + this.getName()) {
1775
            @Override
1776
            public Object invoke(DynObject context, Object[] args) throws DynMethodException {
1777
                return getAvailableValues(context);
1778
            }
1779
        };
1780
        return method;
1781
    }
1782

    
1783
    @Override
1784
    public DynMethod getCalculateMethod() {
1785
        return this.calculateMethod;
1786
    }
1787

    
1788
    @Override
1789
    public DynField setCalculateMethod(DynMethod method) {
1790
        this.calculateMethod = method;
1791
        return this;
1792
    }
1793

    
1794
    @Override
1795
    public boolean isCalculated() {
1796
        return this.calculateMethod != null;
1797
    }
1798

    
1799
    @Override
1800
    public Object getCalculatedValue(DynObject self) {
1801
        try {
1802
            return this.calculateMethod.invoke(self, new Object[]{this});
1803
        } catch (DynMethodException ex) {
1804
            throw new RuntimeException(ex);
1805
        }
1806
    }
1807

    
1808
    @Override
1809
    public DynField setValidateElements(boolean validate) {
1810
        return this;
1811
    }
1812

    
1813
    @Override
1814
    public boolean getValidateElements() {
1815
        return false;
1816
    }
1817

    
1818
    @Override
1819
    public boolean hasLabel() {
1820
        return StringUtils.isNotBlank(this.label);
1821
    }
1822

    
1823
    @Override
1824
    public boolean hasShortLabel() {
1825
        return StringUtils.isNotBlank(this.shortLabel);
1826
    }
1827

    
1828
    @Override
1829
    public boolean hasDescription() {
1830
        return StringUtils.isNotBlank(this.description);
1831
    }
1832

    
1833
    @Override
1834
    public FeatureAttributeDescriptor getValue() {
1835
        return this;
1836
    }
1837

    
1838
    @Override
1839
    public int getDisplaySize() {
1840
        return this.displaySize;
1841
    }
1842

    
1843
    @Override
1844
    public boolean isInAvailableValues(Object valueToCheck) {
1845
        if (this.getAvailableValues() != null) {
1846
            for (DynObjectValueItem availableValue : this.getAvailableValues()) {
1847
                if (Objects.equals(valueToCheck, availableValue.getValue())) {
1848
                    return true;
1849
                }
1850
            }
1851
        }
1852
        return false;
1853
    }
1854

    
1855
    private class ConstantValueEvaluator extends AbstractEvaluator {
1856

    
1857
        @Override
1858
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
1859
            return defaultValue;
1860
        }
1861

    
1862
        @Override
1863
        public String getName() {
1864
            return "Constant attribute " + name;
1865
        }
1866
    }
1867

    
1868
    public void setConstantValue(boolean isConstantValue) {
1869
        if (isConstantValue) {
1870
            /* Cuando un attributo tiene asociado un evaluador, este se interpreta
1871
             * como que no debe cargarse de la fuente de datos subyacente, siendo
1872
             * el evaluador el que se encarga de proporcionar su valor.
1873
             * Nos limitamos a asignar un evaluador que retorna simpre el valor
1874
             * por defecto para ese attributo.
1875
             */
1876
            this.evaluator = new ConstantValueEvaluator();
1877
        } else {
1878
            this.evaluator = null;
1879
        }
1880
    }
1881

    
1882
    @Override
1883
    public boolean isComputed() {
1884
        return featureAttributeEmulator != null || evaluator != null || isCalculated();
1885
    }
1886

    
1887
    @Override
1888
    public FeatureStore getStore() {
1889
        FeatureType ftype = this.getFeatureType();
1890
        if (ftype == null) {
1891
            return null;
1892
        }
1893
        return ftype.getStore();
1894
    }
1895

    
1896
    @Override
1897
    public FeatureType getFeatureType() {
1898
        if (this.typeRef == null) {
1899
            return null;
1900
        }
1901
        FeatureType ftype = (FeatureType) this.typeRef.get();
1902
//        LOGGER.info(String.format("FeatureAttributeDescriptor[%08x] get FeatureType [%08x], ref [%08x].", this.hashCode(), ftype.hashCode(), typeRef.hashCode()));
1903
        return ftype;
1904
    }
1905

    
1906
    public FeatureAttributeDescriptor setInterval(Interval interval) {
1907
        this.interval = interval;
1908
        return this;
1909
    }
1910

    
1911
    public void fixAll() {
1912
        if (!this.getDataType().supportSize()) {
1913
            this.size = 0;
1914
        }
1915
        NumberPrecisionAndScale ps = this.getDataType().fixPrecisionAndScale(this.precision, this.scale);
1916
        this.precision = ps.getPrecision();
1917
        this.scale = ps.getScale();
1918

    
1919
        switch (this.getType()) {
1920
            case DataTypes.INSTANT:
1921
            case DataTypes.INTERVAL:
1922
            case DataTypes.DATE:
1923
            case DataTypes.TIME:
1924
            case DataTypes.TIMESTAMP:
1925
                if (this.getInterval() != null) {
1926
                    this.isTime = true;
1927
                }
1928
                break;
1929
        }
1930
//        TODO: Habria que meter este cambio en proximos builds 2022/11/16.
1931
//        if( this.isPrimaryKey() ) {
1932
//            this.allowNull = false;
1933
//        }
1934
        
1935
    }
1936

    
1937
    @Override
1938
    public String[] getRequiredFieldNames() {
1939
        FeatureAttributeEmulator emulator = this.getFeatureAttributeEmulator();
1940
        if (emulator == null) {
1941
            return null;
1942
        }
1943
        return emulator.getRequiredFieldNames();
1944
    }
1945

    
1946
    @Override
1947
    public void recentUsed() {
1948
        DefaultFeatureType.RECENTS_USEDS.add(this);
1949
    }
1950

    
1951
    @Override
1952
    public boolean hasOnlyMetadataChanges(FeatureAttributeDescriptor other) {
1953
        if (other == null) {
1954
            throw new NullPointerException();
1955
        }
1956
        DefaultFeatureAttributeDescriptor old = (DefaultFeatureAttributeDescriptor) other;
1957
        if (!StringUtils.equalsIgnoreCase(old.name, this.name)) {
1958
            return false;
1959
        }
1960
        if (old.isComputed() && old.isComputed() == this.isComputed()) {
1961
            return true;
1962
        }
1963
        if (this.dataType != old.dataType) {
1964
            return false;
1965
        }
1966
        if (this.size != old.size) {
1967
            return false;
1968
        }
1969
        if (this.precision != old.precision) {
1970
            return false;
1971
        }
1972
//        if( this.primaryKey != old.primaryKey ) {
1973
//            return false;
1974
//        }
1975
        if (this.geomType != old.geomType) {
1976
            return false;
1977
        }
1978
        if (this.SRS != old.SRS) {
1979
            return false;
1980
        }
1981
        if (this.isAutomatic != old.isAutomatic) {
1982
            return false;
1983
        }
1984
        return true;
1985
    }
1986

    
1987
    private class PropertiesBuilder {
1988

    
1989
        private String name;
1990
        private DataType type;
1991
        private final Map<String, String> sets;
1992
        private Map<String, String> tags;
1993
        private String sep;
1994

    
1995
        public PropertiesBuilder() {
1996
            this.sets = new LinkedHashMap<>();
1997
        }
1998

    
1999
        public void separator(String sep) {
2000
            this.sep = sep;
2001
        }
2002

    
2003
        public void name(String name) {
2004
            this.name = name;
2005
        }
2006

    
2007
        public void type(DataType type) {
2008
            this.type = type;
2009
        }
2010

    
2011
        public void set(String name, ForeingKey fk) {
2012
            if (fk == null) {
2013
                return;
2014
            }
2015
            if (!fk.isForeingKey()) {
2016
                return;
2017
            }
2018
            this.set(name, fk.isForeingKey());
2019
            this.set(name + "_code", fk.getCodeName());
2020
            this.set(name + "_label", fk.getLabelFormula());
2021
            this.set(name + "_closedlist", fk.isClosedList());
2022
            this.set(name + "_table", fk.getTableName());
2023
        }
2024

    
2025
        public void set(String name, FeatureAttributeEmulator value) {
2026
            if (value == null) {
2027
                return;
2028
            }
2029
            if (value instanceof FeatureAttributeEmulatorExpression) {
2030
                this.set(name, ((FeatureAttributeEmulatorExpression) value).getExpression().getPhrase());
2031
            }
2032
        }
2033

    
2034
        public void set(String name, IProjection value) {
2035
            if (value == null) {
2036
                return;
2037
            }
2038
            this.set(name, value.getAbrev());
2039
        }
2040

    
2041
        public void set(String name, GeometryType value) {
2042
            if (value == null) {
2043
                return;
2044
            }
2045
            this.set(name, value.getFullName().replace(":", "@"));
2046
        }
2047

    
2048
        public void set(String name, Object value) {
2049
            if (value == null) {
2050
                return;
2051
            }
2052
            String s = Objects.toString(value, "");
2053
            if (StringUtils.isBlank(s)) {
2054
                return;
2055
            }
2056
            this.sets.put(name, s);
2057
        }
2058

    
2059
        public void set(String name, Object value, Object skipValue) {
2060
            if (value == null || value == skipValue) {
2061
                return;
2062
            }
2063
            String s = Objects.toString(value, "");
2064
            if (StringUtils.isBlank(s)) {
2065
                return;
2066
            }
2067
            this.sets.put(name, s);
2068
        }
2069

    
2070
        public void tag(String name, String value) {
2071
            if (value == null) {
2072
                return;
2073
            }
2074
            if (StringUtils.isBlank(value)) {
2075
                return;
2076
            }
2077
            if( this.tags == null ) {
2078
                this.tags = new HashMap<>();
2079
            }
2080
            this.tags.put(name, value);
2081
        }
2082

    
2083
        @Override
2084
        public String toString() {
2085
            StringBuilder builder = new StringBuilder();
2086
            builder.append(this.name);
2087
            builder.append(sep);
2088
            builder.append(this.type.getName());
2089
            for (String key : this.sets.keySet()) {
2090
                builder.append(sep);
2091
                builder.append("set");
2092
                builder.append(sep);
2093
                builder.append(key);
2094
                builder.append("=");
2095
                builder.append(this.sets.get(key));
2096
            }
2097
            if( this.tags!=null ) {
2098
                for (String key : this.tags.keySet()) {
2099
                    builder.append(sep);
2100
                    builder.append("tag");
2101
                    builder.append(sep);
2102
                    builder.append(key);
2103
                    builder.append("=");
2104
                    builder.append(this.tags.get(key));
2105
                }
2106
            }
2107
            return builder.toString();
2108
        }
2109
    }
2110

    
2111
    private String getAll() {
2112
        PropertiesBuilder builder = new PropertiesBuilder();
2113
        builder.separator("/");
2114
        builder.name(this.name);
2115
        builder.type(this.dataType);
2116
        builder.set("size", this.size, 0);
2117
        switch (this.getType()) {
2118
            case DataTypes.BYTE:
2119
            case DataTypes.INTEGER:
2120
            case DataTypes.LONG:
2121
                break;
2122
            case DataTypes.FLOAT:
2123
            case DataTypes.DOUBLE:
2124
                if(this.locale != null){
2125
                    builder.set("locale", this.getLocale());
2126
                }
2127
                break;
2128
            case DataTypes.DECIMAL:
2129
                builder.set("precision", this.precision);
2130
                builder.set("scale", this.scale);
2131
                builder.set("roundMode", this.getRoundMode());
2132
                if(this.locale != null){
2133
                    builder.set("locale", this.getLocale());
2134
                }
2135
                break;
2136
            case DataTypes.DATE:
2137
            case DataTypes.TIME:
2138
            case DataTypes.TIMESTAMP:
2139
                if(this.locale != null){
2140
                    builder.set("locale", this.getLocale());
2141
                }
2142
                break;
2143
            case DataTypes.GEOMETRY:
2144
                IProjection proj = this.getSRS();
2145
                if (proj != null) {
2146
                    builder.set("srs", proj.getAbrev().replace(":", "@"));
2147
                }
2148
                GeometryType theGeomType = this.getGeomType();
2149
                if (theGeomType != null) {
2150
                    String geomTypeName = GeometryUtils.getGeometryTypeName(this.getGeomType().getType());
2151
                    String geomSubtypeName = GeometryUtils.getGeometrySubtypeName(this.getGeomType().getSubType());
2152
                    builder.set("geomtype", geomTypeName + "@" + geomSubtypeName);
2153
                }
2154
                break;
2155
        }
2156
        builder.set("hidden", this.isHidden(), false);
2157
        builder.set("readOnly", this.isReadOnly(), false);
2158
        builder.set("allowNull", this.allowNull(), true);
2159
        builder.set("pk", this.isPrimaryKey(), false);
2160
        builder.set("automatic", this.isAutomatic(), false);
2161
        builder.set("isindexed", this.isIndexed(), false);
2162
        builder.set("isindexascending", this.isIndexAscending(), false);
2163
        builder.set("allowindexduplicateds", this.allowIndexDuplicateds(), false);
2164
        builder.set("isTime", this.isTime(), false);
2165
        builder.set("profile", this.getDataProfileName());
2166
        builder.set("group", this.getGroup());
2167
        builder.set("description", this.description);
2168
        builder.set("label", this.label);
2169
        builder.set("shortLabel", this.shortLabel);
2170
        builder.set("defaultvalue", this.getDefaultValue());
2171
        builder.set("order", this.getOder());
2172
        if (this.getFeatureAttributeEmulator() instanceof FeatureAttributeEmulatorExpression) {
2173
            Expression exp = ((FeatureAttributeEmulatorExpression) this.getFeatureAttributeEmulator()).getExpression();
2174
            if (exp != null) {
2175
                builder.set("expression", exp.getPhrase());
2176
            }
2177
        }
2178
        builder.set("isAvoidCachingAvailableValues", this.isAvoidCachingAvailableValues(), false);
2179
        if (this.isForeingKey()) {
2180
            builder.set("fk", this.isForeingKey());
2181
            builder.set("fk_table", this.getForeingKey().getTableName());
2182
            builder.set("fk_code", this.getForeingKey().getCodeName());
2183
            builder.set("fk_label", this.getForeingKey().getLabelFormula());
2184
            builder.set("fk.closedlist", this.getForeingKey().isClosedList());
2185
        }
2186
        Tags theTags = this.getTags();
2187
        for (String tagname : theTags) {
2188
            String value = theTags.getString(tagname,null);
2189
            if( value!=null ) {
2190
                builder.tag(tagname, value);
2191
            }
2192
        }
2193
        return builder.toString();
2194
    }
2195

    
2196
    @Override
2197
    public Object get(String name) {
2198
        if (StringUtils.isBlank(name)) {
2199
            throw new IllegalArgumentException("Name can't be empty");
2200
        }
2201
        switch (name.trim().toLowerCase()) {
2202
            case "all":
2203
                return this.getAll();
2204
            case "isreadonly":
2205
            case "readonly":
2206
                return this.isReadOnly();
2207
            case "hidden":
2208
                return this.isHidden();
2209
            case "allownull":
2210
                return this.allowNull();
2211
            case "pk":
2212
            case "ispk":
2213
            case "primarykey":
2214
            case "isprimarykey":
2215
                return this.isPrimaryKey();
2216
            case "isindexed":
2217
                return this.isIndexed();
2218
            case "isindexascending":
2219
                return this.isIndexAscending();
2220
            case "allowindexduplicateds":
2221
                return this.allowIndexDuplicateds();
2222
            case "isautomatic":
2223
            case "automatic":
2224
                return this.isAutomatic();
2225
            case "time":
2226
            case "istime":
2227
                return this.isTime();
2228
            case "profile":
2229
                return this.getDataProfile();
2230
            case "group":
2231
                return this.getGroup();
2232
            case "description":
2233
                return this.getDescription();
2234
            case "label":
2235
                return this.getLabel();
2236
            case "shortlabel":
2237
                return this.getShortLabel();
2238
            case "isavoidcachingavailablevalues":
2239
            case "avoidcachingavailablevalues":
2240
            case "nocachingavailablevalues":
2241
                return this.isAvoidCachingAvailableValues();
2242
            case "expression":
2243
                return this.getFeatureAttributeEmulator();
2244
            case "size":
2245
                return this.getSize();
2246
            case "precision":
2247
                return this.getPrecision();
2248
            case "scale":
2249
                return this.getScale();
2250
            case "roundmode":
2251
                return this.getRoundMode();
2252
            case "locale":
2253
                return this.getLocale();
2254
            case "order":
2255
                return this.getOder();
2256
            case "isfk":
2257
            case "isforeingkey":
2258
                return this.isForeingKey();
2259
            case "fk":
2260
            case "foreingkey":
2261
                return this.getForeingKey();
2262
            case "fk_code":
2263
            case "foreingkey_code":
2264
            case "foreingkey.code":
2265
                return this.getForeingKey().getCodeName();
2266
            case "fk_label":
2267
            case "foreingkey_label":
2268
            case "foreingkey.label":
2269
                return this.getForeingKey().getLabelFormula();
2270
            case "fk.closedlist":
2271
            case "foreingkey_closedlist":
2272
            case "foreingkey.closedlist":
2273
                return this.getForeingKey().isClosedList();
2274
            case "fk_table":
2275
            case "foreingkey_table":
2276
            case "foreingkey.table":
2277
                return this.getForeingKey().getTableName();
2278
            case "interval":
2279
                return this.getInterval();
2280
            case "geomtype":
2281
            case "geometrytype":
2282
                return this.getGeomType();
2283
            case "srs":
2284
                return this.getSRS();
2285
            case "defaultvalue":
2286
                return this.getDefaultValue();
2287
            default:
2288
                throw new IllegalArgumentException("Name attribute '" + name + "' not valid.");
2289
        }
2290
    }
2291

    
2292
    public void setSRSForced(IProjection SRS) {
2293
        this.SRS = SRS;
2294
    }
2295

    
2296
    @Override
2297
    public JsonObject toJson() {
2298
        return this.toJsonBuilder().build();
2299
    }
2300

    
2301
    @Override
2302
    public JsonObjectBuilder toJsonBuilder() {
2303
        JsonObjectBuilder builder = Json.createObjectBuilder();
2304
        builder.add_class(this);
2305
        builder.add("name", this.name);
2306
        builder.add("description", this.description);
2307
        builder.add("label", this.label);
2308
        builder.add("shortLabel", this.shortLabel);
2309
        builder.add("order", this.order);
2310
        builder.add("groupName", this.groupName);
2311
        builder.add("dataType", this.dataType);
2312
        builder.add("size", this.size);
2313
        builder.add("precision", this.precision);
2314
        builder.add("scale", this.scale);
2315
        builder.add("roundMode", this.roundMode);
2316

    
2317
        builder.add("allowNull", this.allowNull);
2318
        builder.add("primaryKey", this.primaryKey);
2319
        builder.add("readOnly", this.readOnly);
2320
        builder.add("isAutomatic", this.isAutomatic);
2321
        builder.add("isTime", this.isTime);
2322
        builder.add("indexed", this.indexed);
2323
        builder.add("isIndexAscending", this.isIndexAscending);
2324
        builder.add("allowIndexDuplicateds", this.allowIndexDuplicateds);
2325
        builder.add("hidden", this.hidden);
2326
        builder.add("avoidCachingAvailableValues", this.avoidCachingAvailableValues);
2327

    
2328
        builder.add("geometryType", this.getGeomType());
2329
        builder.add("srs", this.getSRS());
2330

    
2331
        builder.add("relationType", this.relationType);
2332
        builder.add("displaySize", this.displaySize);
2333
        if(this.locale == null){ //!this.hasLocale()
2334
            builder.addNull("locale");
2335
        } else {
2336
            builder.add("locale", this.getLocale());
2337
        }
2338
        builder.add("expression", this.getFeatureAttributeEmulator());
2339
        if (this.isForeingKey()) {
2340
            builder.add("fk", this.isForeingKey());
2341
            builder.add("fk_table", this.getForeingKey().getTableName());
2342
            builder.add("fk_code", this.getForeingKey().getCodeName());
2343
            builder.add("fk_label", this.getForeingKey().getLabelFormula());
2344
            builder.add("fk_closedlist", this.getForeingKey().isClosedList());
2345
        }
2346
        builder.add("availableValuesExpression", this.availableValuesExpression);
2347
        builder.add("defaultValue", Objects.toString(this.defaultValue, null));
2348
        builder.add("dataProfile", this.getDataProfileName());
2349
        builder.add("tags", tags);
2350
        builder.add("availableValues", availableValues);
2351
        builder.add("additionalInfo", this.additionalInfo);
2352
        builder.add("defaultFormat", this.defaultFormat);
2353
        return builder;
2354
    }
2355

    
2356
    public void fromJson(JsonObject json) {
2357
        this.name = json.getString("name");
2358
        this.description = json.getString("description");
2359
        this.label = json.getString("label");
2360
        this.shortLabel = json.getString("shortLabel");
2361
        this.order = json.getInt("order");
2362
        this.groupName = json.getString("groupName");
2363
        this.precision = json.getInt("precision");
2364
        this.size = json.getInt("size");
2365
        this.scale = json.getInt("scale");
2366
        this.roundMode = json.getInt("roundMode");
2367

    
2368
        this.allowNull = json.getBoolean("allowNull");
2369
        this.primaryKey = json.getBoolean("primaryKey");
2370
        this.readOnly = json.getBoolean("readOnly");
2371
        this.isAutomatic = json.getBoolean("isAutomatic");
2372
        this.isTime = json.getBoolean("isTime");
2373
        this.indexed = json.getBoolean("indexed");
2374
        this.isIndexAscending = json.getBoolean("isIndexAscending");
2375
        this.allowIndexDuplicateds = json.getBoolean("allowIndexDuplicateds");
2376
        this.hidden = json.getBoolean("hidden");
2377
        this.avoidCachingAvailableValues = json.getBoolean("avoidCachingAvailableValues");
2378

    
2379
        this.relationType = json.getInt("relationType");
2380
        this.displaySize = json.getInt("displaySize");
2381

    
2382
        this.dataType = (DataType) Json.toObject(json, "dataType");
2383
        this.geomType = (GeometryType) Json.toObject(json, "geometryType");
2384
        this.SRS = (IProjection) Json.toObject(json, "srs");
2385
        this.locale = (Locale) Json.toObject(json, "locale");
2386
        this.featureAttributeEmulator = (FeatureAttributeEmulator) Json.toObject(json, "expression");
2387

    
2388
        this.tags = (Tags) Json.toObject(json, "tags");
2389
        this.additionalInfo = Json.toMap(json, "additionalInfo");
2390
        this.availableValues = (DynObjectValueItem[]) Json.toArray(json, "availableValues", new DynObjectValueItem[0]);
2391
        this.dataProfile = json.getString("dataProfile", null);
2392
        try {
2393
            this.defaultValue = json.getString("defaultValue", null);
2394
            if(!(this.defaultValue instanceof String && ExpressionUtils.isDynamicText((String) this.defaultValue))){
2395
                this.defaultValue = this.coerce(this.defaultValue);
2396
            }
2397
        } catch (Exception ex) {
2398
            LOGGER.warn("Can't retrive default value for attribute '" + this.name + "'.", ex);
2399
        }
2400
        this.availableValuesExpression = (Expression) Json.toObject(json, "availableValuesExpression");
2401
        if (json.getBoolean("fk", false)) {
2402
            this.foreingKey = new DefaultForeingKey();
2403
            this.foreingKey.setForeingKey(true);
2404
            this.foreingKey.setTableName(json.getString("fk_table", null));
2405
            this.foreingKey.setCodeName(json.getString("fk_code", null));
2406
            this.foreingKey.setLabelFormula(json.getString("fk_label", null));
2407
            this.foreingKey.setClosedList(json.getBoolean("fk_closedlist", false));
2408
        } else {
2409
            this.foreingKey = null;
2410
        }
2411
        this.defaultFormat = json.getString("defaultFormat",null);
2412
    }
2413

    
2414
    private static class TheJsonSerializer implements JsonManager.JsonSerializer {
2415

    
2416
        public TheJsonSerializer() {
2417
        }
2418

    
2419
        @Override
2420
        public Class getObjectClass() {
2421
            return DefaultFeatureAttributeDescriptor.class;
2422
        }
2423

    
2424
        @Override
2425
        public Object toObject(JsonObject json) {
2426
            DefaultFeatureAttributeDescriptor o = new DefaultFeatureAttributeDescriptor();
2427
            o.fromJson(json);
2428
            return o;
2429
        }
2430

    
2431
        @Override
2432
        public JsonObjectBuilder toJsonBuilder(Object value) {
2433
            return ((SupportToJson) value).toJsonBuilder();
2434
        }
2435

    
2436
    }
2437

    
2438
    public static void selfRegister() {
2439
        Json.registerSerializer(new TheJsonSerializer());
2440
    }
2441

    
2442
    @Override
2443
    public String getDefaultFormat() {
2444
        return this.defaultFormat;
2445
    }
2446

    
2447
    @Override
2448
    public String format(Object value) {
2449
        if(value == null){
2450
            return "";
2451
        }
2452
        try {
2453
            if( StringUtils.isBlank(this.defaultFormat)) {
2454
                if( this.locale==null ) { // !this.hasLocale()
2455
                    return DataTypeUtils.toString(Locale.getDefault(), value, Objects.toString(value, ""));
2456
                } else {
2457
                    return DataTypeUtils.toString(this.locale, value, Objects.toString(value, ""));
2458
                }
2459
            }
2460
            return String.format(this.defaultFormat, value);
2461
        } catch(Exception ex) {
2462
            return Objects.toString(value, "");
2463
        }
2464
    }
2465

    
2466
    @Override
2467
    public String toString() {
2468
        try {
2469
            String s = this.getAll();
2470
            return s;
2471
        } catch(Exception ex) {
2472
            return super.toString();
2473
        }
2474
    }
2475

    
2476
    
2477
}