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

History | View | Annotate | Download (83.4 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.apache.commons.text.StringEscapeUtils;
44
import org.cresques.cts.IProjection;
45
import org.gvsig.expressionevaluator.Expression;
46
import org.gvsig.expressionevaluator.ExpressionUtils;
47
import org.gvsig.expressionevaluator.MutableSymbolTable;
48
import org.gvsig.fmap.dal.DALLocator;
49
import org.gvsig.fmap.dal.DataStore;
50
import org.gvsig.fmap.dal.DataTypes;
51
import org.gvsig.fmap.dal.expressionevaluator.FeatureAttributeEmulatorExpression;
52
import org.gvsig.fmap.dal.feature.DataProfile;
53
import org.gvsig.fmap.dal.feature.Feature;
54
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
55
import org.gvsig.fmap.dal.feature.FeatureAttributeEmulator;
56
import org.gvsig.fmap.dal.feature.FeatureAttributeGetter;
57
import org.gvsig.fmap.dal.feature.FeatureStore;
58
import org.gvsig.fmap.dal.feature.FeatureType;
59
import org.gvsig.fmap.dal.feature.ForeingKey;
60
import org.gvsig.fmap.dal.feature.ForeingKey.ContextForeingKey;
61
import org.gvsig.fmap.geom.Geometry;
62
import org.gvsig.fmap.geom.GeometryCoercionContext;
63
import org.gvsig.fmap.geom.GeometryException;
64
import org.gvsig.fmap.geom.GeometryLocator;
65
import org.gvsig.fmap.geom.GeometryUtils;
66
import org.gvsig.fmap.geom.type.GeometryType;
67
import org.gvsig.json.Json;
68
import org.gvsig.json.JsonManager;
69
import org.gvsig.json.JsonObjectBuilder;
70
import org.gvsig.json.SupportToJson;
71
import org.gvsig.timesupport.Interval;
72
import org.gvsig.timesupport.RelativeInterval;
73
import org.gvsig.timesupport.TimeSupportLocator;
74
import org.gvsig.tools.ToolsLocator;
75
import org.gvsig.tools.dataTypes.Coercion;
76
import org.gvsig.tools.dataTypes.CoercionContext;
77
import org.gvsig.tools.dataTypes.CoercionException;
78
import org.gvsig.tools.dataTypes.DataType;
79
import org.gvsig.tools.dataTypes.DataType.NumberPrecisionAndScale;
80
import org.gvsig.tools.dataTypes.DataTypeUtils;
81
import org.gvsig.tools.dispose.DisposeUtils;
82
import org.gvsig.tools.dynobject.AbstractDynMethod;
83
import org.gvsig.tools.dynobject.DynField;
84
import org.gvsig.tools.dynobject.DynField_LabelAttribute;
85
import org.gvsig.tools.dynobject.DynField_v2;
86
import org.gvsig.tools.dynobject.DynMethod;
87
import org.gvsig.tools.dynobject.DynObject;
88
import org.gvsig.tools.dynobject.DynObjectValueItem;
89
import org.gvsig.tools.dynobject.DynStruct;
90
import org.gvsig.tools.dynobject.Tags;
91
import org.gvsig.tools.dynobject.exception.DynFieldIsNotAContainerException;
92
import org.gvsig.tools.dynobject.exception.DynFieldValidateException;
93
import org.gvsig.tools.dynobject.exception.DynMethodException;
94
import org.gvsig.tools.dynobject.impl.DefaultTags;
95
import org.gvsig.tools.evaluator.AbstractEvaluator;
96
import org.gvsig.tools.evaluator.Evaluator;
97
import org.gvsig.tools.evaluator.EvaluatorData;
98
import org.gvsig.tools.evaluator.EvaluatorException;
99
import org.gvsig.tools.i18n.I18nManager;
100
import org.gvsig.tools.persistence.PersistenceManager;
101
import org.gvsig.tools.persistence.Persistent;
102
import org.gvsig.tools.persistence.PersistentState;
103
import org.gvsig.tools.persistence.exception.PersistenceException;
104
import org.gvsig.tools.util.GetItemWithSize;
105
import org.gvsig.tools.util.LabeledValue;
106
import org.slf4j.Logger;
107
import org.slf4j.LoggerFactory;
108

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
703
        return true;
704
    }
705

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
982
    private Expression availableValuesFilter;
983

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1351
    @Override
1352
    public boolean isMandatory() {
1353
        if( this.isAutomatic() ) {
1354
            return false;
1355
        }
1356
        return !allowNull() || isPrimaryKey();
1357
    }
1358

    
1359
    @Override
1360
    public boolean isPersistent() {
1361
        return false;
1362
    }
1363

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

    
1385
    @Override
1386
    public DynField setDescription(String description) {
1387
        this.description = description;
1388
        return this;
1389
    }
1390

    
1391
    @Override
1392
    public DynField setMandatory(boolean mandatory) {
1393
        throw new UnsupportedOperationException();
1394
    }
1395

    
1396
    @Override
1397
    public DynField setMaxValue(Object maxValue) {
1398
        try {
1399
            this.maxValue = this.coerce(maxValue);
1400
        } catch (CoercionException e) {
1401
            throw new IllegalArgumentException(e);
1402
        }
1403
        return this;
1404
    }
1405

    
1406
    @Override
1407
    public DynField setMinValue(Object minValue) {
1408
        try {
1409
            this.maxValue = this.coerce(minValue);
1410
        } catch (CoercionException e) {
1411
            throw new IllegalArgumentException(e);
1412
        }
1413
        return this;
1414
    }
1415

    
1416
    @Override
1417
    public DynField setPersistent(boolean persistent) {
1418
        throw new UnsupportedOperationException();
1419
    }
1420

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

    
1426
    @Override
1427
    public DynField setType(int type) {
1428
        throw new UnsupportedOperationException();
1429
    }
1430

    
1431
    @Override
1432
    @Deprecated
1433
    public DynField setDefaultDynValue(Object defaultValue) {
1434
        return this.setDefaultFieldValue(defaultValue);
1435
    }
1436

    
1437
    @Override
1438
    public DynField setDefaultFieldValue(Object defaultValue) {
1439
        this.defaultValue = defaultValue;
1440
        return this;
1441
    }
1442

    
1443
    @Override
1444
    public Class getClassOfValue() {
1445
        return null;
1446
    }
1447

    
1448
    @Override
1449
    public DynField getElementsType() {
1450
        return null;
1451
    }
1452

    
1453
    @Override
1454
    public DynField setClassOfValue(Class theClass)
1455
            throws DynFieldIsNotAContainerException {
1456
        throw new UnsupportedOperationException();
1457
    }
1458

    
1459
    @Override
1460
    public DynField setElementsType(DynStruct type)
1461
            throws DynFieldIsNotAContainerException {
1462
        throw new UnsupportedOperationException();
1463
    }
1464

    
1465
    @Override
1466
    public DynField setElementsType(int type)
1467
            throws DynFieldIsNotAContainerException {
1468
        throw new UnsupportedOperationException();
1469
    }
1470

    
1471
    public FeatureAttributeDescriptor setDataProfileName(String dataProfile) {
1472
        this.dataProfile = dataProfile;
1473
        return this;
1474
    }
1475

    
1476
    @Override
1477
    public String getDataProfileName() {
1478
        return dataProfile;
1479
    }
1480

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

    
1495
    @Override
1496
    public void validate(Object value) throws DynFieldValidateException {
1497

    
1498
        if (value == null && !this.allowNull()) {
1499
            throw new DynFieldValidateException(value, this, null);
1500
        }
1501

    
1502
        try {
1503
            this.dataType.coerce(value);
1504
        } catch (CoercionException e) {
1505
            throw new DynFieldValidateException(value, this, e);
1506
        }
1507

    
1508
        /*
1509
         * Other checks will be needed
1510
         */
1511
    }
1512

    
1513
    @Override
1514
    public String getSubtype() {
1515
        if (featureAttributeGetter != null) {
1516
            return featureAttributeGetter.getDataType().getSubtype();
1517
        }
1518
        return this.dataType.getSubtype();
1519
    }
1520

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

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

    
1545
    @Override
1546
    public String getGroup() {
1547
        return this.groupName;
1548
    }
1549

    
1550
    @Override
1551
    public int getOder() {
1552
        return this.order;
1553
    }
1554

    
1555
    @Override
1556
    public String getLabel() {
1557
        if (this.label == null) {
1558
            return this.getName();
1559
        }
1560
        return this.label;
1561
    }
1562

    
1563
    @Override
1564
    public String getLocalizedLabel() {
1565
        if (StringUtils.isBlank(this.label)) {
1566
            return this.getName();
1567
        }
1568
        I18nManager i18n = ToolsLocator.getI18nManager();
1569
        return i18n.getTranslation(this.label);
1570
    }
1571

    
1572
    @Override
1573
    public DynField setLabel(String label) {
1574
        this.label = label;
1575
        return this;
1576
    }
1577

    
1578
    @Override
1579
    public DynField setShortLabel(String shortLabel) {
1580
        this.shortLabel = shortLabel;
1581
        return this;
1582
    }
1583

    
1584
    @Override
1585
    public String getShortLabel() {
1586
        return StringUtils.isBlank(shortLabel) ? getLabel() : shortLabel;
1587
    }
1588

    
1589
    @Override
1590
    public String getLocalizedShortLabel() {
1591
        if (StringUtils.isBlank(shortLabel)) {
1592
            return this.getLocalizedLabel();
1593
        }
1594
        I18nManager i18n = ToolsLocator.getI18nManager();
1595
        return i18n.getTranslation(shortLabel);
1596
    }
1597

    
1598
    @Override
1599
    public DynField setGroup(String groupName) {
1600
        this.groupName = groupName;
1601
        return this;
1602
    }
1603

    
1604
    @Override
1605
    public DynField setOrder(int order) {
1606
        this.order = order;
1607
        return this;
1608
    }
1609

    
1610
    @Override
1611
    public DynField setHidden(boolean hidden) {
1612
        this.hidden = hidden;
1613
        return this;
1614
    }
1615

    
1616
    @Override
1617
    public boolean isHidden() {
1618
        return this.hidden;
1619
    }
1620

    
1621
    @Override
1622
    public DynField setReadOnly(boolean readOnly) {
1623
        this.readOnly = readOnly;
1624
        return this;
1625
    }
1626

    
1627
    @Override
1628
    public boolean isContainer() {
1629
        return false;
1630
    }
1631

    
1632
    @Override
1633
    public Class getClassOfItems() {
1634
        return null;
1635
    }
1636

    
1637
    @Override
1638
    public DynField setClassOfItems(Class theClass) {
1639
        throw new UnsupportedOperationException();
1640
    }
1641

    
1642
    @Override
1643
    public DynField setType(DataType type) {
1644
        throw new UnsupportedOperationException();
1645
    }
1646

    
1647
    @Override
1648
    public DynField setSubtype(String subtype) {
1649
        throw new UnsupportedOperationException();
1650
    }
1651

    
1652
    @Override
1653
    public boolean isTime() {
1654
        return isTime;
1655
    }
1656

    
1657
    @Override
1658
    public FeatureAttributeGetter getFeatureAttributeGetter() {
1659
        return featureAttributeGetter;
1660
    }
1661

    
1662
    @Override
1663
    public void setFeatureAttributeGetter(
1664
            FeatureAttributeGetter featureAttributeTransform) {
1665
        this.featureAttributeGetter = featureAttributeTransform;
1666
    }
1667

    
1668
    @Override
1669
    public FeatureAttributeEmulator getFeatureAttributeEmulator() {
1670
        return this.featureAttributeEmulator;
1671
    }
1672

    
1673
    public FeatureAttributeDescriptor setFeatureAttributeEmulator(FeatureAttributeEmulator featureAttributeEmulator) {
1674
        this.featureAttributeEmulator = featureAttributeEmulator;
1675
        return this;
1676
    }
1677

    
1678
    @Override
1679
    public boolean isIndexed() {
1680
        return this.indexed;
1681
    }
1682

    
1683
    @Override
1684
    public boolean isForeingKey() {
1685
        return this.foreingKey != null && this.foreingKey.isForeingKey();
1686
    }
1687

    
1688
    @Override
1689
    public ForeingKey getForeingKey() {
1690
        return this.foreingKey;
1691
    }
1692

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

    
1698
    @Override
1699
    public boolean isIndexAscending() {
1700
        return this.isIndexAscending;
1701
    }
1702

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

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

    
1713
    @Override
1714
    public String getClassNameOfValue() {
1715
        return null;
1716
    }
1717

    
1718
    @Override
1719
    public DynStruct getDynClassOfValue() {
1720
        return null;
1721
    }
1722

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

    
1728
    @Override
1729
    public int getTypeOfItems() {
1730
        return DataTypes.INVALID;
1731
    }
1732

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

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

    
1743
    @Override
1744
    public String getClassNameOfItems() {
1745
        return null;
1746
    }
1747

    
1748
    @Override
1749
    public DynStruct getDynClassOfItems() {
1750
        return null;
1751
    }
1752

    
1753
    @Override
1754
    public DynField setRelationType(int relationType) {
1755
        this.relationType = relationType;
1756
        return this;
1757
    }
1758

    
1759
    @Override
1760
    public int getRelationType() {
1761
        return this.relationType;
1762
    }
1763

    
1764
    @Override
1765
    public DynField setAvailableValues(DynMethod availableValuesMethod) {
1766
        this.availableValuesMethod = availableValuesMethod;
1767
        return this;
1768
    }
1769

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

    
1787
    @Override
1788
    public DynMethod getCalculateMethod() {
1789
        return this.calculateMethod;
1790
    }
1791

    
1792
    @Override
1793
    public DynField setCalculateMethod(DynMethod method) {
1794
        this.calculateMethod = method;
1795
        return this;
1796
    }
1797

    
1798
    @Override
1799
    public boolean isCalculated() {
1800
        return this.calculateMethod != null;
1801
    }
1802

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

    
1812
    @Override
1813
    public DynField setValidateElements(boolean validate) {
1814
        return this;
1815
    }
1816

    
1817
    @Override
1818
    public boolean getValidateElements() {
1819
        return false;
1820
    }
1821

    
1822
    @Override
1823
    public boolean hasLabel() {
1824
        return StringUtils.isNotBlank(this.label);
1825
    }
1826

    
1827
    @Override
1828
    public boolean hasShortLabel() {
1829
        return StringUtils.isNotBlank(this.shortLabel);
1830
    }
1831

    
1832
    @Override
1833
    public boolean hasDescription() {
1834
        return StringUtils.isNotBlank(this.description);
1835
    }
1836

    
1837
    @Override
1838
    public FeatureAttributeDescriptor getValue() {
1839
        return this;
1840
    }
1841

    
1842
    @Override
1843
    public int getDisplaySize() {
1844
        return this.displaySize;
1845
    }
1846

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

    
1859
    private class ConstantValueEvaluator extends AbstractEvaluator {
1860

    
1861
        @Override
1862
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
1863
            return defaultValue;
1864
        }
1865

    
1866
        @Override
1867
        public String getName() {
1868
            return "Constant attribute " + name;
1869
        }
1870
    }
1871

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

    
1886
    @Override
1887
    public boolean isComputed() {
1888
        return featureAttributeEmulator != null || evaluator != null || isCalculated();
1889
    }
1890

    
1891
    @Override
1892
    public FeatureStore getStore() {
1893
        FeatureType ftype = this.getFeatureType();
1894
        if (ftype == null) {
1895
            return null;
1896
        }
1897
        return ftype.getStore();
1898
    }
1899

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

    
1910
    public FeatureAttributeDescriptor setInterval(Interval interval) {
1911
        this.interval = interval;
1912
        return this;
1913
    }
1914

    
1915
    public void fixAll() {
1916
        if (!this.getDataType().supportSize()) {
1917
            this.size = 0;
1918
        }
1919
        NumberPrecisionAndScale ps = this.getDataType().fixPrecisionAndScale(this.precision, this.scale);
1920
        this.precision = ps.getPrecision();
1921
        this.scale = ps.getScale();
1922

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

    
1941
    @Override
1942
    public String[] getRequiredFieldNames() {
1943
        FeatureAttributeEmulator emulator = this.getFeatureAttributeEmulator();
1944
        if (emulator == null) {
1945
            return null;
1946
        }
1947
        return emulator.getRequiredFieldNames();
1948
    }
1949

    
1950
    @Override
1951
    public void recentUsed() {
1952
        DefaultFeatureType.RECENTS_USEDS.add(this);
1953
    }
1954

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

    
1991
    private class PropertiesBuilder {
1992

    
1993
        private String name;
1994
        private DataType type;
1995
        private final Map<String, String> sets;
1996
        private Map<String, String> tags;
1997
        private String sep;
1998

    
1999
        public PropertiesBuilder() {
2000
            this.sets = new LinkedHashMap<>();
2001
        }
2002

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

    
2007
        public void name(String name) {
2008
            this.name = name;
2009
        }
2010

    
2011
        public void type(DataType type) {
2012
            this.type = type;
2013
        }
2014

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

    
2029
        public void set(String name, FeatureAttributeEmulator value) {
2030
            if (value == null) {
2031
                return;
2032
            }
2033
            if (value instanceof FeatureAttributeEmulatorExpression) {
2034
                this.set(name, ((FeatureAttributeEmulatorExpression) value).getExpression().getPhrase());
2035
            }
2036
        }
2037

    
2038
        public void set(String name, IProjection value) {
2039
            if (value == null) {
2040
                return;
2041
            }
2042
            this.set(name, value.getAbrev());
2043
        }
2044

    
2045
        public void set(String name, GeometryType value) {
2046
            if (value == null) {
2047
                return;
2048
            }
2049
            this.set(name, value.getFullName().replace(":", "@"));
2050
        }
2051

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

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

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

    
2087
        @Override
2088
        public String toString() {
2089
            StringBuilder builder = new StringBuilder();
2090
            builder.append(this.name);
2091
            builder.append(sep);
2092
            builder.append(this.type.getName());
2093
            for (String key : this.sets.keySet()) {
2094
                builder.append(sep);
2095
                String s = this.sets.get(key);
2096
                if(s.contains(sep)){
2097
                    builder.append("setesc");
2098
                    builder.append(sep);
2099
                    builder.append("html");
2100
                    builder.append(sep);
2101
                    builder.append(key);
2102
                    builder.append("=");
2103
                    s = StringEscapeUtils.escapeHtml3(s);
2104
                    s = StringUtils.replace(s, sep, "&#"+((int)(sep.charAt(0)))+";");
2105
                    builder.append(s);
2106
                } else {
2107
                    builder.append("set");
2108
                    builder.append(sep);
2109
                    builder.append(key);
2110
                    builder.append("=");
2111
                    builder.append(s);
2112
                }
2113
                
2114
            }
2115
            if( this.tags!=null ) {
2116
                for (String key : this.tags.keySet()) {
2117
                    builder.append(sep);
2118
                    String s = this.tags.get(key);
2119
                    if(s.contains(sep)){
2120
                        builder.append("tagesc");
2121
                        builder.append(sep);
2122
                        builder.append("html");
2123
                        builder.append(sep);
2124
                        builder.append(key);
2125
                        builder.append("=");
2126
                        s = StringEscapeUtils.escapeHtml3(s);
2127
                        s = StringUtils.replace(s, sep, "&#"+((int)(sep.charAt(0)))+";");
2128
                        builder.append(s);
2129
                    } else {
2130
                        builder.append("tag");
2131
                        builder.append(sep);
2132
                        builder.append(key);
2133
                        builder.append("=");
2134
                        builder.append(s);
2135
                    }
2136
                }
2137
            }
2138
            return builder.toString();
2139
        }
2140
    }
2141

    
2142
    private String getAll() {
2143
        PropertiesBuilder builder = new PropertiesBuilder();
2144
        builder.separator("/");
2145
        builder.name(this.name);
2146
        builder.type(this.dataType);
2147
        builder.set("size", this.size, 0);
2148
        switch (this.getType()) {
2149
            case DataTypes.BYTE:
2150
            case DataTypes.INTEGER:
2151
            case DataTypes.LONG:
2152
                break;
2153
            case DataTypes.FLOAT:
2154
            case DataTypes.DOUBLE:
2155
                if(this.locale != null){
2156
                    builder.set("locale", this.getLocale());
2157
                }
2158
                break;
2159
            case DataTypes.DECIMAL:
2160
                builder.set("precision", this.precision);
2161
                builder.set("scale", this.scale);
2162
                builder.set("roundMode", this.getRoundMode());
2163
                if(this.locale != null){
2164
                    builder.set("locale", this.getLocale());
2165
                }
2166
                break;
2167
            case DataTypes.DATE:
2168
            case DataTypes.TIME:
2169
            case DataTypes.TIMESTAMP:
2170
                if(this.locale != null){
2171
                    builder.set("locale", this.getLocale());
2172
                }
2173
                break;
2174
            case DataTypes.GEOMETRY:
2175
                IProjection proj = this.getSRS();
2176
                if (proj != null) {
2177
                    builder.set("srs", proj.getAbrev().replace(":", "@"));
2178
                }
2179
                GeometryType theGeomType = this.getGeomType();
2180
                if (theGeomType != null) {
2181
                    String geomTypeName = GeometryUtils.getGeometryTypeName(this.getGeomType().getType());
2182
                    String geomSubtypeName = GeometryUtils.getGeometrySubtypeName(this.getGeomType().getSubType());
2183
                    builder.set("geomtype", geomTypeName + "@" + geomSubtypeName);
2184
                }
2185
                break;
2186
        }
2187
        builder.set("hidden", this.isHidden(), false);
2188
        builder.set("readOnly", this.isReadOnly(), false);
2189
        builder.set("allowNull", this.allowNull(), true);
2190
        builder.set("pk", this.isPrimaryKey(), false);
2191
        builder.set("automatic", this.isAutomatic(), false);
2192
        builder.set("isindexed", this.isIndexed(), false);
2193
        builder.set("isindexascending", this.isIndexAscending(), false);
2194
        builder.set("allowindexduplicateds", this.allowIndexDuplicateds(), false);
2195
        builder.set("isTime", this.isTime(), false);
2196
        builder.set("profile", this.getDataProfileName());
2197
        builder.set("group", this.getGroup());
2198
        builder.set("description", this.description);
2199
        builder.set("label", this.label);
2200
        builder.set("shortLabel", this.shortLabel);
2201
        builder.set("defaultvalue", this.getDefaultValue());
2202
        builder.set("order", this.getOder());
2203
        if (this.getFeatureAttributeEmulator() instanceof FeatureAttributeEmulatorExpression) {
2204
            Expression exp = ((FeatureAttributeEmulatorExpression) this.getFeatureAttributeEmulator()).getExpression();
2205
            if (exp != null) {
2206
                builder.set("expression", exp.getPhrase());
2207
            }
2208
        }
2209
        builder.set("isAvoidCachingAvailableValues", this.isAvoidCachingAvailableValues(), false);
2210
        if (this.isForeingKey()) {
2211
            builder.set("fk", this.isForeingKey());
2212
            builder.set("fk_table", this.getForeingKey().getTableName());
2213
            builder.set("fk_code", this.getForeingKey().getCodeName());
2214
            builder.set("fk_label", this.getForeingKey().getLabelFormula());
2215
            builder.set("fk.closedlist", this.getForeingKey().isClosedList());
2216
        }
2217
        Tags theTags = this.getTags();
2218
        for (String tagname : theTags) {
2219
            String value = theTags.getString(tagname,null);
2220
            if( value!=null ) {
2221
                builder.tag(tagname, value);
2222
            }
2223
        }
2224
        return builder.toString();
2225
    }
2226

    
2227
    @Override
2228
    public Object get(String name) {
2229
        if (StringUtils.isBlank(name)) {
2230
            throw new IllegalArgumentException("Name can't be empty");
2231
        }
2232
        switch (name.trim().toLowerCase()) {
2233
            case "all":
2234
                return this.getAll();
2235
            case "isreadonly":
2236
            case "readonly":
2237
                return this.isReadOnly();
2238
            case "hidden":
2239
                return this.isHidden();
2240
            case "allownull":
2241
                return this.allowNull();
2242
            case "pk":
2243
            case "ispk":
2244
            case "primarykey":
2245
            case "isprimarykey":
2246
                return this.isPrimaryKey();
2247
            case "isindexed":
2248
                return this.isIndexed();
2249
            case "isindexascending":
2250
                return this.isIndexAscending();
2251
            case "allowindexduplicateds":
2252
                return this.allowIndexDuplicateds();
2253
            case "isautomatic":
2254
            case "automatic":
2255
                return this.isAutomatic();
2256
            case "time":
2257
            case "istime":
2258
                return this.isTime();
2259
            case "profile":
2260
                return this.getDataProfile();
2261
            case "group":
2262
                return this.getGroup();
2263
            case "description":
2264
                return this.getDescription();
2265
            case "label":
2266
                return this.getLabel();
2267
            case "shortlabel":
2268
                return this.getShortLabel();
2269
            case "isavoidcachingavailablevalues":
2270
            case "avoidcachingavailablevalues":
2271
            case "nocachingavailablevalues":
2272
                return this.isAvoidCachingAvailableValues();
2273
            case "expression":
2274
                return this.getFeatureAttributeEmulator();
2275
            case "size":
2276
                return this.getSize();
2277
            case "precision":
2278
                return this.getPrecision();
2279
            case "scale":
2280
                return this.getScale();
2281
            case "roundmode":
2282
                return this.getRoundMode();
2283
            case "locale":
2284
                return this.getLocale();
2285
            case "order":
2286
                return this.getOder();
2287
            case "isfk":
2288
            case "isforeingkey":
2289
                return this.isForeingKey();
2290
            case "fk":
2291
            case "foreingkey":
2292
                return this.getForeingKey();
2293
            case "fk_code":
2294
            case "foreingkey_code":
2295
            case "foreingkey.code":
2296
                return this.getForeingKey().getCodeName();
2297
            case "fk_label":
2298
            case "foreingkey_label":
2299
            case "foreingkey.label":
2300
                return this.getForeingKey().getLabelFormula();
2301
            case "fk.closedlist":
2302
            case "foreingkey_closedlist":
2303
            case "foreingkey.closedlist":
2304
                return this.getForeingKey().isClosedList();
2305
            case "fk_table":
2306
            case "foreingkey_table":
2307
            case "foreingkey.table":
2308
                return this.getForeingKey().getTableName();
2309
            case "interval":
2310
                return this.getInterval();
2311
            case "geomtype":
2312
            case "geometrytype":
2313
                return this.getGeomType();
2314
            case "srs":
2315
                return this.getSRS();
2316
            case "defaultvalue":
2317
                return this.getDefaultValue();
2318
            default:
2319
                throw new IllegalArgumentException("Name attribute '" + name + "' not valid.");
2320
        }
2321
    }
2322

    
2323
    public void setSRSForced(IProjection SRS) {
2324
        this.SRS = SRS;
2325
    }
2326

    
2327
    @Override
2328
    public JsonObject toJson() {
2329
        return this.toJsonBuilder().build();
2330
    }
2331

    
2332
    @Override
2333
    public JsonObjectBuilder toJsonBuilder() {
2334
        JsonObjectBuilder builder = Json.createObjectBuilder();
2335
        builder.add_class(this);
2336
        builder.add("name", this.name);
2337
        builder.add("description", this.description);
2338
        builder.add("label", this.label);
2339
        builder.add("shortLabel", this.shortLabel);
2340
        builder.add("order", this.order);
2341
        builder.add("groupName", this.groupName);
2342
        builder.add("dataType", this.dataType);
2343
        builder.add("size", this.size);
2344
        builder.add("precision", this.precision);
2345
        builder.add("scale", this.scale);
2346
        builder.add("roundMode", this.roundMode);
2347

    
2348
        builder.add("allowNull", this.allowNull);
2349
        builder.add("primaryKey", this.primaryKey);
2350
        builder.add("readOnly", this.readOnly);
2351
        builder.add("isAutomatic", this.isAutomatic);
2352
        builder.add("isTime", this.isTime);
2353
        builder.add("indexed", this.indexed);
2354
        builder.add("isIndexAscending", this.isIndexAscending);
2355
        builder.add("allowIndexDuplicateds", this.allowIndexDuplicateds);
2356
        builder.add("hidden", this.hidden);
2357
        builder.add("avoidCachingAvailableValues", this.avoidCachingAvailableValues);
2358

    
2359
        builder.add("geometryType", this.getGeomType());
2360
        builder.add("srs", this.getSRS());
2361

    
2362
        builder.add("relationType", this.relationType);
2363
        builder.add("displaySize", this.displaySize);
2364
        if(this.locale == null){ //!this.hasLocale()
2365
            builder.addNull("locale");
2366
        } else {
2367
            builder.add("locale", this.getLocale());
2368
        }
2369
        builder.add("expression", this.getFeatureAttributeEmulator());
2370
        if (this.isForeingKey()) {
2371
            builder.add("fk", this.isForeingKey());
2372
            builder.add("fk_table", this.getForeingKey().getTableName());
2373
            builder.add("fk_code", this.getForeingKey().getCodeName());
2374
            builder.add("fk_label", this.getForeingKey().getLabelFormula());
2375
            builder.add("fk_closedlist", this.getForeingKey().isClosedList());
2376
        }
2377
        builder.add("availableValuesExpression", this.availableValuesExpression);
2378
        builder.add("defaultValue", Objects.toString(this.defaultValue, null));
2379
        builder.add("dataProfile", this.getDataProfileName());
2380
        builder.add("tags", tags);
2381
        builder.add("availableValues", availableValues);
2382
        builder.add("additionalInfo", this.additionalInfo);
2383
        builder.add("defaultFormat", this.defaultFormat);
2384
        return builder;
2385
    }
2386

    
2387
    public void fromJson(JsonObject json) {
2388
        this.name = json.getString("name");
2389
        this.description = json.getString("description");
2390
        this.label = json.getString("label");
2391
        this.shortLabel = json.getString("shortLabel");
2392
        this.order = json.getInt("order");
2393
        this.groupName = json.getString("groupName");
2394
        this.precision = json.getInt("precision");
2395
        this.size = json.getInt("size");
2396
        this.scale = json.getInt("scale");
2397
        this.roundMode = json.getInt("roundMode");
2398

    
2399
        this.allowNull = json.getBoolean("allowNull");
2400
        this.primaryKey = json.getBoolean("primaryKey");
2401
        this.readOnly = json.getBoolean("readOnly");
2402
        this.isAutomatic = json.getBoolean("isAutomatic");
2403
        this.isTime = json.getBoolean("isTime");
2404
        this.indexed = json.getBoolean("indexed");
2405
        this.isIndexAscending = json.getBoolean("isIndexAscending");
2406
        this.allowIndexDuplicateds = json.getBoolean("allowIndexDuplicateds");
2407
        this.hidden = json.getBoolean("hidden");
2408
        this.avoidCachingAvailableValues = json.getBoolean("avoidCachingAvailableValues");
2409

    
2410
        this.relationType = json.getInt("relationType");
2411
        this.displaySize = json.getInt("displaySize");
2412

    
2413
        this.dataType = (DataType) Json.toObject(json, "dataType");
2414
        this.geomType = (GeometryType) Json.toObject(json, "geometryType");
2415
        this.SRS = (IProjection) Json.toObject(json, "srs");
2416
        this.locale = (Locale) Json.toObject(json, "locale");
2417
        this.featureAttributeEmulator = (FeatureAttributeEmulator) Json.toObject(json, "expression");
2418

    
2419
        this.tags = (Tags) Json.toObject(json, "tags");
2420
        this.additionalInfo = Json.toMap(json, "additionalInfo");
2421
        this.availableValues = (DynObjectValueItem[]) Json.toArray(json, "availableValues", new DynObjectValueItem[0]);
2422
        this.dataProfile = json.getString("dataProfile", null);
2423
        try {
2424
            this.defaultValue = json.getString("defaultValue", null);
2425
            if(!(this.defaultValue instanceof String && ExpressionUtils.isDynamicText((String) this.defaultValue))){
2426
                this.defaultValue = this.coerce(this.defaultValue);
2427
            }
2428
        } catch (Exception ex) {
2429
            LOGGER.warn("Can't retrive default value for attribute '" + this.name + "'.", ex);
2430
        }
2431
        this.availableValuesExpression = (Expression) Json.toObject(json, "availableValuesExpression");
2432
        if (json.getBoolean("fk", false)) {
2433
            this.foreingKey = new DefaultForeingKey();
2434
            this.foreingKey.setForeingKey(true);
2435
            this.foreingKey.setTableName(json.getString("fk_table", null));
2436
            this.foreingKey.setCodeName(json.getString("fk_code", null));
2437
            this.foreingKey.setLabelFormula(json.getString("fk_label", null));
2438
            this.foreingKey.setClosedList(json.getBoolean("fk_closedlist", false));
2439
        } else {
2440
            this.foreingKey = null;
2441
        }
2442
        this.defaultFormat = json.getString("defaultFormat",null);
2443
    }
2444

    
2445
    private static class TheJsonSerializer implements JsonManager.JsonSerializer {
2446

    
2447
        public TheJsonSerializer() {
2448
        }
2449

    
2450
        @Override
2451
        public Class getObjectClass() {
2452
            return DefaultFeatureAttributeDescriptor.class;
2453
        }
2454

    
2455
        @Override
2456
        public Object toObject(JsonObject json) {
2457
            DefaultFeatureAttributeDescriptor o = new DefaultFeatureAttributeDescriptor();
2458
            o.fromJson(json);
2459
            return o;
2460
        }
2461

    
2462
        @Override
2463
        public JsonObjectBuilder toJsonBuilder(Object value) {
2464
            return ((SupportToJson) value).toJsonBuilder();
2465
        }
2466

    
2467
    }
2468

    
2469
    public static void selfRegister() {
2470
        Json.registerSerializer(new TheJsonSerializer());
2471
    }
2472

    
2473
    @Override
2474
    public String getDefaultFormat() {
2475
        return this.defaultFormat;
2476
    }
2477

    
2478
    @Override
2479
    public String format(Object value) {
2480
        if(value == null){
2481
            return "";
2482
        }
2483
        try {
2484
            if( StringUtils.isBlank(this.defaultFormat)) {
2485
                if( this.locale==null ) { // !this.hasLocale()
2486
                    return DataTypeUtils.toString(Locale.getDefault(), value, Objects.toString(value, ""));
2487
                } else {
2488
                    return DataTypeUtils.toString(this.locale, value, Objects.toString(value, ""));
2489
                }
2490
            }
2491
            return String.format(this.defaultFormat, value);
2492
        } catch(Exception ex) {
2493
            return Objects.toString(value, "");
2494
        }
2495
    }
2496

    
2497
    @Override
2498
    public String toString() {
2499
        try {
2500
            String s = this.getAll();
2501
            return s;
2502
        } catch(Exception ex) {
2503
            return super.toString();
2504
        }
2505
    }
2506

    
2507
    
2508
}