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

History | View | Annotate | Download (85.3 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.observer.BaseNotification;
101
import org.gvsig.tools.observer.Observable;
102
import org.gvsig.tools.observer.ObservableHelper;
103
import org.gvsig.tools.observer.Observer;
104
import org.gvsig.tools.persistence.PersistenceManager;
105
import org.gvsig.tools.persistence.Persistent;
106
import org.gvsig.tools.persistence.PersistentState;
107
import org.gvsig.tools.persistence.exception.PersistenceException;
108
import org.gvsig.tools.util.GetItemWithSize;
109
import org.gvsig.tools.util.LabeledValue;
110
import org.slf4j.Logger;
111
import org.slf4j.LoggerFactory;
112

    
113
@SuppressWarnings("UseSpecificCatch")
114
public class DefaultFeatureAttributeDescriptor implements
115
        FeatureAttributeDescriptor, Persistent, DynField_v2, DynField_LabelAttribute, Observable {
116

    
117
    protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultFeatureAttributeDescriptor.class);
118

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

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

    
170
    protected CoercionContext coerceContext = null; // not persistent
171
    protected MathContext mathContext = null; // not persistent
172

    
173
    private int relationType = RELATION_TYPE_NONE;
174
    protected Locale locale;
175
    protected int displaySize;
176
    
177
    protected String defaultFormat;
178
    protected boolean fixed;
179
    protected ObservableHelper observableHelper;
180

    
181
    public DefaultFeatureAttributeDescriptor() {
182
        // Usada en la persistencia
183
        this.precision = DataType.PRECISION_NONE;
184
        this.scale = DataType.SCALE_NONE;
185
        this.roundMode = BigDecimal.ROUND_HALF_UP;
186
        this.fixed = false;
187
        this.observableHelper = new ObservableHelper();
188
        
189
    }
190

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

    
224
    protected DefaultFeatureAttributeDescriptor(
225
            DefaultFeatureAttributeDescriptor other
226
    ) {
227
        this();
228
        copyFrom(other);
229
//        LOGGER.info(String.format("Created FeatureAttributeDescriptor [%08x] [%s] (copy).", this.hashCode(), this.name));
230
    }
231

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

    
277
        this.availableValues = other.availableValues;
278
        this.availableValuesExpression = other.availableValuesExpression;
279
        this.description = other.description;
280
        this.minValue = other.minValue;
281
        this.maxValue = other.maxValue;
282
        this.label = other.label;
283
        this.order = other.order;
284
        this.groupName = other.groupName;
285
        if (other.tags == null) {
286
            this.tags = null;
287
        } else {
288
            try {
289
                this.tags = (Tags) other.tags.clone();
290
            } catch (Exception ex) {
291
            }
292
        }
293
        this.foreingKey = null;
294
        if (other.foreingKey != null) {
295
            try {
296
                this.foreingKey = (DefaultForeingKey) other.foreingKey.clone();
297
            } catch (CloneNotSupportedException ex) {
298
            }
299
        }
300
        if (this.foreingKey != null) {
301
            this.foreingKey.setDescriptor(this);
302
        }
303

    
304
        // TODO: ? Habria que clonarlos ?
305
        this.availableValuesMethod = other.availableValuesMethod;
306
        this.calculateMethod = other.calculateMethod;
307
        this.relationType = other.relationType;
308
        this.locale = other.locale;
309
        this.displaySize = other.displaySize;
310
        this.avoidCachingAvailableValues = other.avoidCachingAvailableValues;
311
    }
312

    
313
    public void setFeatureType(FeatureType type) {
314
        // Usada en la persistencia
315
        if (type == null) {
316
            this.typeRef = null;
317
        } else {
318
            this.typeRef = new WeakReference(type);
319
//            LOGGER.info(String.format("FeatureAttributeDescriptor[%08x] set FeatureType [%08x], ref [%08x].", this.hashCode(), type.hashCode(), typeRef.hashCode()));
320
        }
321
        this.setFixed(false);
322
    }
323

    
324
    @Override
325
    public String getDataTypeName() {
326
        if (this.getDataType() == null) {
327
            return "(unknow)";
328
        }
329
        return this.getDataType().getName();
330
    }
331

    
332
    @Override
333
    public DefaultFeatureAttributeDescriptor getCopy() {
334
        return new DefaultFeatureAttributeDescriptor(this);
335
    }
336

    
337
    @Override
338
    public Object clone() throws CloneNotSupportedException {
339
        return new DefaultFeatureAttributeDescriptor(this);
340
    }
341

    
342
    @Override
343
    public boolean allowNull() {
344
//        TODO: Habria que meter este cambio en proximos builds 2022/11/16.
345
//        if( this.isPrimaryKey() ) {
346
//            return false;
347
//        }
348
        return allowNull;
349
    }
350

    
351
    @Override
352
    public Locale getLocale() {
353
        // FIXME: Debe devolver el locale que toca ENGLISH o default, 
354
        // pero no asignarlo a "locale", manteniendo el null en la variable.
355
        // Ojo que tambien habria que tocar el setLocale.
356
        if (this.locale == null) {
357
            if (this.dataType.isNumeric()) {
358
                this.locale = Locale.ENGLISH; //return
359
            } else {
360
                this.locale = Locale.getDefault();
361
            }
362
        }
363
        return this.locale;
364
    }
365

    
366
    @Override
367
    public DataType getDataType() {
368
        if (featureAttributeGetter != null) {
369
            return featureAttributeGetter.getDataType();
370
        }
371
        return this.dataType;
372
    }
373

    
374
    public FeatureAttributeDescriptor setDataType(int type) {
375
        this.dataType = ToolsLocator.getDataTypesManager().get(type);
376
        this.setFixed(false);
377
        return this;
378
    }
379

    
380
    @Override
381
    public DateFormat getDateFormat() {
382
        return this.dateFormat;
383
    }
384

    
385
    @Override
386
    public Object getDefaultValue() {
387
        return this.defaultValue;
388
    }
389

    
390
    @Override
391
    @Deprecated
392
    public Object getDefaultValueCoerced() {
393
        return getCoercedDefaultValue();
394
    }
395

    
396
    @Override
397
    public Object getCoercedDefaultValue() {
398
        try {
399
            Object value = this.defaultValue;
400
            if (value == null) {
401
                return null;
402
            }
403
            if (ExpressionUtils.isDynamicText(value.toString())) {
404
                value = ExpressionUtils.evaluateDynamicText(value.toString());
405
            }
406
            return this.getDataType().coerce(value);
407
        } catch (CoercionException ex) {
408
            return null;
409
        }
410
    }
411

    
412
    @Override
413
    public Supplier getDefaultValueSupplier() {
414
        return (Supplier) this::getDefaultValueCoerced;
415
    }
416

    
417
    @Override
418
    public DynField setDefaultValueSupplier(Supplier supplier) {
419
        //Do nothing
420
        return this;
421
    }
422

    
423
    @Override
424
    public Evaluator getEvaluator() {
425
        return this.evaluator;
426
    }
427

    
428
    @Override
429
    public int getGeometryType() {
430
        if (this.dataType.getType() != DataTypes.GEOMETRY) {
431
            return Geometry.TYPES.UNKNOWN;
432
        }
433
        return this.geometryType;
434
    }
435

    
436
    @Override
437
    public int getGeometrySubType() {
438
        if (this.dataType.getType() != DataTypes.GEOMETRY) {
439
            return Geometry.SUBTYPES.UNKNOWN;
440
        }
441
        return this.geometrySubType;
442
    }
443

    
444
    @Override
445
    public GeometryType getGeomType() {
446
        if (this.dataType.getType() != DataTypes.GEOMETRY) {
447
            return null;
448
        }
449
        if (this.geomType == null) {
450
            if((this.geometryType == Geometry.TYPES.UNKNOWN || this.geometryType == Geometry.TYPES.NULL) && 
451
                    (this.geometrySubType == Geometry.SUBTYPES.UNKNOWN)){
452
                return null;
453
            }
454
            try {
455
                this.geomType
456
                        = GeometryLocator.getGeometryManager().getGeometryType(
457
                                this.geometryType, this.geometrySubType);
458
            } catch (GeometryException e) {
459
                throw new RuntimeException(
460
                        "Error getting geometry type with type = "
461
                        + this.geometryType + ", subtype = "
462
                        + this.geometrySubType, e);
463
            }
464
        }
465
        return this.geomType;
466
    }
467

    
468
    @Override
469
    public int getIndex() {
470
        return this.index;
471
    }
472

    
473
    protected FeatureAttributeDescriptor setIndex(int index) {
474
        this.index = index;
475
        this.setFixed(false);
476
        return this;
477
    }
478

    
479
    @Override
480
    public int getMaximumOccurrences() {
481
        return this.maximumOccurrences;
482
    }
483

    
484
    @Override
485
    public int getMinimumOccurrences() {
486
        return this.minimumOccurrences;
487
    }
488

    
489
    @Override
490
    public String getName() {
491
        return this.name;
492
    }
493

    
494
    public FeatureAttributeDescriptor setName(String name) {
495
//        LOGGER.info(String.format("FeatureAttributeDescriptor[%08x] set name [%s].", this.hashCode(), name));
496
        this.name = name;
497
        return this;
498
    }
499

    
500
    @Override
501
    public Class getObjectClass() {
502
        if (getDataType().getType() == DataTypes.OBJECT) {
503
            return objectClass;
504
        }
505
        return getDataType().getDefaultClass();
506
    }
507

    
508
    @Override
509
    public int getPrecision() {
510
        return this.precision;
511
    }
512

    
513
    @Override
514
    public int getScale() {
515
        return this.scale;
516
    }
517

    
518
    @Override
519
    public Coercion getCoercion() {
520
        return this.getDataType().getCoercion();
521
    }
522

    
523
    @Override
524
    public MathContext getMathContext() {
525
        if (this.mathContext == null) {
526
            if (this.getDataType().isNumeric()) {
527
                this.mathContext = new MathContext(
528
                        this.getPrecision(),
529
                        RoundingMode.valueOf(this.getRoundMode())
530
                );
531
            } else {
532
                this.mathContext = MathContext.UNLIMITED;
533
            }
534
        }
535
        return this.mathContext;
536
    }
537

    
538
    @Override
539
    public CoercionContext getCoercionContext() {
540
        if (this.coerceContext == null) {
541
            if (this.getDataType().isNumeric()) {
542
                this.coerceContext = DataTypeUtils.coerceContextDecimal(
543
                        this.getLocale(),
544
                        this.getPrecision(),
545
                        this.getScale(),
546
                        this.getRoundMode()
547
                );
548
            } else if (this.getType() == DataTypes.GEOMETRY) {
549
                GeometryCoercionContext context = GeometryLocator.getGeometryManager().createGeometryCoercionContext();
550
                context.setGeometryType(this.getGeomType());
551
                context.setMode(GeometryCoercionContext.MODE_ONERROR_DONTCONVERT);
552
                this.coerceContext = context;
553
            } else if (this.getType() == DataTypes.TIMESTAMP) {
554
//                if(this.locale != null){
555
                    this.coerceContext = DataTypeUtils.coerceContextLocale(
556
                            this.locale
557
                    );
558
//                }
559
            } else {
560
                this.coerceContext = DataTypeUtils.coerceContextLocale(
561
                        this.getLocale()
562
                );
563
            }
564
        }
565
        return this.coerceContext;
566
    }
567

    
568
    @Override
569
    public int getRoundMode() {
570
        return this.roundMode;
571
    }
572

    
573
    @Override
574
    public IProjection getSRS() {
575
        return this.SRS;
576
    }
577

    
578
    @Override
579
    public Interval getInterval() {
580
        return this.interval;
581
    }
582

    
583
    public IProjection getSRS(WeakReference storeRef) {
584
        if (this.SRS == null) {
585
            FeatureStore store = (FeatureStore) storeRef.get();
586
            this.SRS = (IProjection) store.getDynValue(DataStore.METADATA_CRS);
587
        }
588
        return this.SRS;
589
    }
590

    
591
    @Override
592
    public int getSize() {
593
        return this.size;
594
    }
595

    
596
    @Override
597
    public boolean isPrimaryKey() {
598
        return this.primaryKey;
599
    }
600

    
601
    @Override
602
    public boolean isReadOnly() {
603
        if (this.readOnly) {
604
            return true;
605
        }
606
        return this.isAutomatic() || this.isComputed();
607
    }
608

    
609
    @Override
610
    public String getAdditionalInfo(String infoName) {
611
        if (this.additionalInfo == null) {
612
            return null;
613
        }
614
        return this.additionalInfo.get(infoName);
615
    }
616

    
617
    @Override
618
    public boolean isAutomatic() {
619
        return this.isAutomatic;
620
    }
621

    
622
    @Override
623
    public boolean equals(Object obj) {
624
        if (this == obj) {
625
            return true;
626
        }
627
        if (!(obj instanceof DefaultFeatureAttributeDescriptor)) {
628
            return false;
629
        }
630
        DefaultFeatureAttributeDescriptor other
631
                = (DefaultFeatureAttributeDescriptor) obj;
632

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

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

    
641
        if (!Objects.equals(this.name, other.name)) {
642
            return false;
643
        }
644

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

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

    
653
        if (!Objects.equals(this.defaultValue, other.defaultValue)) {
654
            return false;
655
        }
656
        if (!Objects.equals(this.defaultFormat, other.defaultFormat)) {
657
            return false;
658
        }
659

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

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

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

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

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

    
680
        if (this.minimumOccurrences != other.minimumOccurrences) {
681
            return false;
682
        }
683

    
684
        if (this.geometryType != other.geometryType) {
685
            return false;
686
        }
687

    
688
        if (this.geometrySubType != other.geometrySubType) {
689
            return false;
690
        }
691

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

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

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

    
704
        if (!Objects.equals(this.dateFormat, other.dateFormat)) {
705
            return false;
706
        }
707

    
708
        if (!Objects.equals(this.objectClass, other.objectClass)) {
709
            return false;
710
        }
711

    
712
        if (!Objects.equals(this.dataProfile, other.dataProfile)) {
713
            return false;
714
        }
715

    
716
        return true;
717
    }
718

    
719
    @Override
720
    public void loadFromState(PersistentState state)
721
            throws PersistenceException {
722
        allowNull = state.getBoolean("allowNull");
723
        dataType = ToolsLocator.getDataTypesManager().get(state.getInt("dataType"));
724
        dataProfile = state.getString("dataProfile");
725

    
726
//        FIXME: dateFormat;
727
        try {
728
            defaultValue = state.getString("defaultValue");
729
            if( !ExpressionUtils.isDynamicText((String) defaultValue) ) {
730
                defaultValue = dataType.coerce(defaultValue);
731
            }
732
        } catch (CoercionException ex) {
733
        }
734

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

    
781
        Map<String, Object> values = state.getMap("availableValues");
782
        if (values == null || values.isEmpty()) {
783
            this.availableValues = null;
784
        } else {
785
            this.availableValues = new DynObjectValueItem[values.size()];
786
            int n = 0;
787
            Coercion coercion = this.getCoercion();
788
            for (Entry<String, Object> entry : values.entrySet()) {
789
                Object value;
790
                try {
791
                    value = coercion.coerce(entry.getValue());
792
                } catch (CoercionException ex) {
793
                    value = entry.getValue();
794
                }
795
                this.availableValues[n++] = new DynObjectValueItem(value, entry.getKey());
796
            }
797
        }
798

    
799
        description = state.getString("description");
800
        minValue = state.get("minValue");
801
        maxValue = state.get("maxValue");
802
        label = state.getString("label");
803
        order = state.getInt("order");
804
        hidden = state.getBoolean("hidden");
805
        groupName = state.getString("groupName");
806
        relationType = state.getInt("relationType", RELATION_TYPE_NONE);
807

    
808
        foreingKey = (DefaultForeingKey) state.get("foreingKey");
809
        if (foreingKey != null) {
810
            this.foreingKey.setDescriptor(this);
811
        }
812
        tags = (Tags) state.get("tags");
813
        if (tags == null) {
814
            this.tags = new DefaultTags();
815
        }
816
        displaySize = state.getInt("displaySize", 0);
817
        availableValuesExpression = (Expression) state.get("availableValuesExpression");
818
        avoidCachingAvailableValues = state.getBoolean("avoidCachingAvailableValues", false);
819
        availableValuesCache = null;
820
        defaultFormat = state.getString("defaultFormat");
821
        this.setFixed(false);
822
    }
823

    
824
    @Override
825
    public void saveToState(PersistentState state) throws PersistenceException {
826
        Coercion toString = ToolsLocator.getDataTypesManager().getCoercion(DataTypes.STRING);
827

    
828
        state.set("allowNull", allowNull);
829
        state.set("dataType", dataType.getType());
830
        state.set("dataProfile", dataProfile);
831

    
832
//        FIXME: dateFormat;
833
        state.set("defaultValue", Objects.toString(defaultValue, null));
834

    
835
        state.set("index", index);
836
        state.set("maximumOccurrences", maximumOccurrences);
837
        state.set("minimumOccurrences", minimumOccurrences);
838
        state.set("size", size);
839
        state.set("name", name);
840
        state.set("objectClass", objectClass == null ? null : objectClass.getName());
841
        state.set("precision", precision);
842
        state.set("scale", scale);
843
        state.set("roundMode", roundMode);
844
        if (this.locale == null) {
845
            state.setNull("locale");
846
        } else {
847
            state.set("locale", this.locale.toLanguageTag()); //toString.coerce(this.locale));
848
        }
849
        state.set("evaluator", evaluator);
850

    
851
        state.set("primaryKey", primaryKey);
852
        state.set("readOnly", readOnly);
853
        state.set("SRS", SRS);
854
        GeometryType theGeomType = this.getGeomType();
855
        if (theGeomType == null) {
856
            state.set("geometryType", Geometry.TYPES.UNKNOWN);
857
            state.set("geometrySubType", Geometry.SUBTYPES.UNKNOWN);
858
        } else {
859
            state.set("geometryType", theGeomType.getType());
860
            state.set("geometrySubType", theGeomType.getSubType());
861
        }
862

    
863
//      FIXME: additionalInfo
864
        state.set("isAutomatic", isAutomatic);
865
        state.set("isTime", isTime);
866
        if (this.interval == null) {
867
            state.setNull("interval_start");
868
            state.setNull("interval_end");
869
        } else {
870
            state.set("interval_start", ((RelativeInterval) interval).getStart().toMillis());
871
            state.set("interval_end", ((RelativeInterval) interval).getEnd().toMillis());
872
        }
873
        state.set("SRS", SRS);
874

    
875
//      FIXME: featureAttributeGetter
876
        if (featureAttributeEmulator instanceof Persistent) {
877
            state.set("featureAttributeEmulator", featureAttributeEmulator);
878
        } else {
879
            state.setNull("featureAttributeEmulator");
880
        }
881

    
882
        state.set("indexed", indexed);
883
        state.set("isIndexAscending", isIndexAscending);
884
        state.set("allowIndexDuplicateds", allowIndexDuplicateds);
885

    
886
        if (this.availableValues == null) {
887
            state.setNull("availableValues");
888
        } else {
889
            Map<String, Object> values = new LinkedHashMap<>();
890
            for (DynObjectValueItem value : availableValues) {
891
                if( value!=null ) {
892
                    values.put(value.getLabel(), value.getValue());
893
                }
894
            }
895
            state.set("availableValues", values);
896
        }
897
        state.set("description", description);
898
        state.set("minValue", minValue);
899
        state.set("maxValue", maxValue);
900
        state.set("label", label);
901
        state.set("order", order);
902
        state.set("hidden", hidden);
903
        state.set("groupName", groupName);
904
        state.set("relationType", relationType);
905

    
906
        state.set("foreingKey", this.foreingKey);
907
        state.set("tags", this.tags);
908

    
909
        state.set("displaySize", displaySize);
910
        state.set("availableValuesExpression", availableValuesExpression);
911
        state.set("avoidCachingAvailableValues", avoidCachingAvailableValues);
912
        state.set("defaultFormat", defaultFormat);
913
    }
914

    
915
    private static final String FEATATTRDESC_PERSISTENCE_DEFINITION_NAME = "FeatureAttributeDescriptor";
916

    
917
    public static void registerPersistenceDefinition() {
918
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
919

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

    
973
            definition.addDynFieldObject("foreingKey")
974
                    .setClassOfValue(DefaultForeingKey.class);
975

    
976
            definition.addDynFieldObject("tags")
977
                    .setClassOfValue(Tags.class);
978

    
979
            definition.addDynFieldInt("displaySize").setMandatory(false);
980
            definition.addDynFieldObject("availableValuesExpression")
981
                    .setClassOfValue(Expression.class)
982
                    .setMandatory(false);
983
            definition.addDynFieldString("defaultFormat").setMandatory(false);
984
        }
985
    }
986

    
987
    /*
988
     * Start of DynField interface Implementation
989
     *
990
     */
991
    @Override
992
    public Tags getTags() {
993
        return tags;
994
    }
995

    
996
    private Expression availableValuesFilter;
997

    
998
    @Override
999
    public Expression getAvailableValuesFilter() {
1000
        return this.availableValuesFilter;
1001
    }
1002

    
1003
    public FeatureAttributeDescriptor setAvailableValuesFilter(Expression filter) {
1004
        this.availableValuesFilter = filter;
1005
        this.setFixed(false);
1006
        return this;
1007
    }
1008

    
1009
    public FeatureAttributeDescriptor setAvailableValuesFilter(String filter) {
1010
        this.availableValuesFilter = ExpressionUtils.createExpression(filter);
1011
        this.setFixed(false);
1012
        return this;
1013
    }
1014

    
1015
    @Override
1016
    public boolean hasConstantAvailableValues() {
1017
        return this.availableValues != null;
1018
    }
1019

    
1020
    @Override
1021
    public boolean isAvoidCachingAvailableValues() {
1022
        return this.avoidCachingAvailableValues;
1023
    }
1024

    
1025
    @Override
1026
    public boolean hasAvailableValues() {
1027
        return getAvailableValues() != null;
1028
    }
1029

    
1030
    @Override
1031
    public DynObjectValueItem[] getAvailableValues(DynObject context) {
1032
        if (this.availableValuesMethod != null) {
1033
            DynObjectValueItem[] values;
1034
            try {
1035
                values = (DynObjectValueItem[]) this.availableValuesMethod.invoke(context, new Object[]{this});
1036
            } catch (DynMethodException ex) {
1037
                return this.getAvailableValues();
1038
            }
1039
            if (values != null) {
1040
                return values;
1041
            }
1042
        }
1043
        Expression filter = this.availableValuesFilter;
1044
        if (!ExpressionUtils.isEmpty(filter)) {
1045
            if (this.isForeingKey() && this.foreingKey.isClosedList()) {
1046
                ContextForeingKey foreingkeyContext = null;
1047
                try {
1048
                    foreingkeyContext = this.foreingKey.createContext();
1049
                    foreingkeyContext.setContextValues(context);
1050
                    DynObjectValueItem[] values = this.foreingKey.getAvailableValues(foreingkeyContext);
1051
                    return values;
1052
                } finally {
1053
                    DisposeUtils.disposeQuietly(foreingkeyContext);
1054
                }
1055
            }
1056
            MutableSymbolTable contextSymbolTable = ExpressionUtils.createSymbolTable("feature", context);
1057
            MutableSymbolTable symbolTable = ExpressionUtils.createSymbolTable();
1058
            symbolTable.addSymbolTable(contextSymbolTable);
1059

    
1060
            DynObjectValueItem[] allValues = this.getAvailableValues();
1061
            List<DynObjectValueItem> filteredValues = new ArrayList<>();
1062
            for (DynObjectValueItem value : allValues) {
1063
                symbolTable.setVar("$value", value.getValue());
1064
                symbolTable.setVar("$label", value.getLabel());
1065
                Object include = filter.execute(symbolTable);
1066
                if (DataTypeUtils.isFalse(include, false)) {
1067
                    continue;
1068
                }
1069
                filteredValues.add(value);
1070
            }
1071
            DynObjectValueItem[] values = filteredValues.toArray(
1072
                    new DynObjectValueItem[filteredValues.size()]
1073
            );
1074
            return values;
1075
        }
1076
        return this.getAvailableValues();
1077
    }
1078

    
1079
    @Override
1080
    public boolean isAvailableValuesCalculated() {
1081
        if (this.availableValuesMethod != null) {
1082
            return true;
1083
        }
1084
        if (!ExpressionUtils.isEmpty(this.availableValuesFilter)) {
1085
            return true;
1086
        }
1087
        if( this.isForeingKey() && this.foreingKey.isClosedList() ) {
1088
            return true;
1089
        }
1090
        return false;
1091
    }
1092

    
1093
    @Override
1094
    public DynObjectValueItem[] getAvailableValues() {
1095
        DynObjectValueItem[] values = this.availableValues;
1096

    
1097
        if (values != null) {
1098
            return values;
1099
        }
1100
        if (this.isForeingKey() && this.foreingKey.isClosedList()) {
1101
            values = this.foreingKey.getAvailableValues(null);
1102
            return values;
1103
        }
1104
        values = this.availableValuesCache;
1105
        if (values != null) {
1106
            return values;
1107
        }
1108
//        if (this.isForeingKey() && this.foreingKey.isClosedList()) {
1109
//            values = this.foreingKey.getAvailableValues(null);
1110
//
1111
//        } else 
1112
        if (this.availableValuesExpression != null) {
1113
            values = this.getAvailableValuesFromExpression();
1114
        }
1115
        if (!this.avoidCachingAvailableValues) {
1116
            this.availableValuesCache = values;
1117
        }
1118
        return values;
1119
    }
1120

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

    
1255
    private DynObjectValueItem[] getAvailableValuesFromExpression() {
1256
        if (this.availableValuesExpression == null || this.availableValuesExpression.isEmpty()) {
1257
            return null;
1258
        }
1259
        try {
1260
            Object value = this.availableValuesExpression.execute(null);
1261
            if (value instanceof DynObjectValueItem[]) {
1262
                return (DynObjectValueItem[]) value;
1263
            }
1264
            if (value instanceof List) {
1265
                return this.getAvailableValuesFrom(new GetItemWithSize() {
1266
                    @Override
1267
                    public Object get(int i) {
1268
                        return ((List) value).get(i);
1269
                    }
1270

    
1271
                    @Override
1272
                    public int size() {
1273
                        return ((List) value).size();
1274
                    }
1275
                });
1276
            }
1277
        } catch (Throwable th) {
1278
            LOGGER.warn("Can't get available values from expression", th);
1279
        }
1280
        return null;
1281
    }
1282

    
1283
    @Override
1284
    public Expression getAvailableValuesExpression() {
1285
        return this.availableValuesExpression;
1286
    }
1287

    
1288
    @Override
1289
    public FeatureAttributeDescriptor setAvailableValuesExpression(String expression) {
1290
        if (StringUtils.isBlank(expression)) {
1291
            this.availableValuesExpression = null;
1292
            return this;
1293
        }
1294
        this.availableValuesExpression = ExpressionUtils.createExpression(expression);
1295
        this.setFixed(false);
1296
        return this;
1297
    }
1298

    
1299
    @Override
1300
    public FeatureAttributeDescriptor setAvailableValuesExpression(Expression expression) {
1301
        this.availableValuesExpression = expression;
1302
        this.setFixed(false);
1303
        return this;
1304
    }
1305

    
1306
    @Override
1307
    public String getLabelOfValue(Object value) {
1308
        String theLabel = null;
1309
        if( this.isForeingKey() && !this.getForeingKey().isClosedList() ) {
1310
            theLabel = this.getForeingKey().getLabel(null, value);
1311
            if( StringUtils.isNotBlank(theLabel) ) {
1312
                return theLabel;
1313
            }
1314
        }
1315
        if (this.labelOfValueMap != null) {
1316
            theLabel = this.labelOfValueMap.get(value);
1317
            if (theLabel == null) {
1318
                theLabel = Objects.toString(value, "");
1319
            }
1320
            return theLabel;
1321
        }
1322
        DynObjectValueItem[] values = this.getAvailableValues();
1323
        if (values == null) {
1324
            return this.format(value);
1325
        }
1326
        Map<Object, String> map = new LinkedHashMap<>();
1327
        for (DynObjectValueItem theValue : values) {
1328
            map.put(theValue.getValue(), theValue.getLabel());
1329
        }
1330
        this.labelOfValueMap = map;
1331
        theLabel = this.labelOfValueMap.get(value);
1332
        if (theLabel == null) {
1333
            return this.format(value);
1334
        }
1335
        return theLabel;
1336
    }
1337

    
1338
    @Override
1339
    public String getDescription() {
1340
        if (this.description == null) {
1341
            return getName();
1342
        }
1343
        return this.description;
1344
    }
1345

    
1346
    @Override
1347
    public Object getMaxValue() {
1348
        return this.maxValue;
1349
    }
1350

    
1351
    @Override
1352
    public Object getMinValue() {
1353
        return this.minValue;
1354
    }
1355

    
1356
    @Override
1357
    public int getTheTypeOfAvailableValues() {
1358
        return 1;
1359
    }
1360

    
1361
    @Override
1362
    public int getType() {
1363
        if (featureAttributeGetter != null) {
1364
            return featureAttributeGetter.getDataType().getType();
1365
        }
1366
        return getDataType().getType();
1367
    }
1368

    
1369
    @Override
1370
    public boolean isMandatory() {
1371
        if( this.isAutomatic() ) {
1372
            return false;
1373
        }
1374
        return !allowNull() || isPrimaryKey();
1375
    }
1376

    
1377
    @Override
1378
    public boolean isPersistent() {
1379
        return false;
1380
    }
1381

    
1382
    @Override
1383
    public DynField setAvailableValues(DynObjectValueItem[] values) {
1384
        if (ArrayUtils.isEmpty(values)) {
1385
            this.availableValues = null;
1386
        } else {
1387
            Coercion coercion = this.getCoercion();
1388
            this.availableValues = new DynObjectValueItem[values.length];
1389
            for (int i = 0; i < values.length; i++) {
1390
                DynObjectValueItem element = values[i];
1391
                Object value;
1392
                try {
1393
                    value = coercion.coerce(element.getValue());
1394
                } catch (CoercionException ex) {
1395
                    value = element.getValue();
1396
                }
1397
                this.availableValues[i] = new DynObjectValueItem(value, element.getLabel());
1398
            }
1399
        }
1400
        this.setFixed(false);
1401
        return this;
1402
    }
1403

    
1404
    @Override
1405
    public DynField setDescription(String description) {
1406
        this.description = description;
1407
        this.setFixed(false);
1408
        return this;
1409
    }
1410

    
1411
    @Override
1412
    public DynField setMandatory(boolean mandatory) {
1413
        throw new UnsupportedOperationException();
1414
    }
1415

    
1416
    @Override
1417
    public DynField setMaxValue(Object maxValue) {
1418
        try {
1419
            this.maxValue = this.coerce(maxValue);
1420
        } catch (CoercionException e) {
1421
            throw new IllegalArgumentException(e);
1422
        }
1423
        return this;
1424
    }
1425

    
1426
    @Override
1427
    public DynField setMinValue(Object minValue) {
1428
        try {
1429
            this.maxValue = this.coerce(minValue);
1430
        } catch (CoercionException e) {
1431
            throw new IllegalArgumentException(e);
1432
        }
1433
        return this;
1434
    }
1435

    
1436
    @Override
1437
    public DynField setPersistent(boolean persistent) {
1438
        throw new UnsupportedOperationException();
1439
    }
1440

    
1441
    @Override
1442
    public DynField setTheTypeOfAvailableValues(int type) {
1443
        throw new UnsupportedOperationException();
1444
    }
1445

    
1446
    @Override
1447
    public DynField setType(int type) {
1448
        throw new UnsupportedOperationException();
1449
    }
1450

    
1451
    @Override
1452
    @Deprecated
1453
    public DynField setDefaultDynValue(Object defaultValue) {
1454
        return this.setDefaultFieldValue(defaultValue);
1455
    }
1456

    
1457
    @Override
1458
    public DynField setDefaultFieldValue(Object defaultValue) {
1459
        this.defaultValue = defaultValue;
1460
        this.setFixed(false);
1461
        return this;
1462
    }
1463

    
1464
    @Override
1465
    public Class getClassOfValue() {
1466
        return null;
1467
    }
1468

    
1469
    @Override
1470
    public DynField getElementsType() {
1471
        return null;
1472
    }
1473

    
1474
    @Override
1475
    public DynField setClassOfValue(Class theClass)
1476
            throws DynFieldIsNotAContainerException {
1477
        throw new UnsupportedOperationException();
1478
    }
1479

    
1480
    @Override
1481
    public DynField setElementsType(DynStruct type)
1482
            throws DynFieldIsNotAContainerException {
1483
        throw new UnsupportedOperationException();
1484
    }
1485

    
1486
    @Override
1487
    public DynField setElementsType(int type)
1488
            throws DynFieldIsNotAContainerException {
1489
        throw new UnsupportedOperationException();
1490
    }
1491

    
1492
    public FeatureAttributeDescriptor setDataProfileName(String dataProfile) {
1493
        this.dataProfile = dataProfile;
1494
        this.setFixed(false);
1495
        return this;
1496
    }
1497

    
1498
    @Override
1499
    public String getDataProfileName() {
1500
        return dataProfile;
1501
    }
1502

    
1503
    @Override
1504
    public boolean hasDataProfile() {
1505
        return StringUtils.isNotBlank(dataProfile);
1506
    }
1507
    
1508
    @Override
1509
    public DataProfile getDataProfile() {
1510
        if (StringUtils.isBlank(dataProfile)) {
1511
            return null;
1512
        }
1513
        DataProfile profile = DALLocator.getDataManager().getDataProfile(dataProfile);
1514
        return profile;
1515
    }
1516

    
1517
    @Override
1518
    public void validate(Object value) throws DynFieldValidateException {
1519

    
1520
        if (value == null && !this.allowNull()) {
1521
            throw new DynFieldValidateException(value, this, null);
1522
        }
1523

    
1524
        try {
1525
            this.dataType.coerce(value);
1526
        } catch (CoercionException e) {
1527
            throw new DynFieldValidateException(value, this, e);
1528
        }
1529

    
1530
        /*
1531
         * Other checks will be needed
1532
         */
1533
    }
1534

    
1535
    @Override
1536
    public String getSubtype() {
1537
        if (featureAttributeGetter != null) {
1538
            return featureAttributeGetter.getDataType().getSubtype();
1539
        }
1540
        return this.dataType.getSubtype();
1541
    }
1542

    
1543
    @Override
1544
    public Object coerce(Object value) throws CoercionException {
1545
        if (value == null) {
1546
            return value; // O debe devolver this.defaultValue
1547
        }
1548
        try {
1549
            return this.getDataType().coerce(value, this.getCoercionContext());
1550
        } catch (Exception ex) {
1551
            throw new RuntimeException(ex);
1552
        }
1553
    }
1554

    
1555
    @Override
1556
    public DynField setAvailableValues(List values) {
1557
        if (values == null || values.isEmpty()) {
1558
            this.availableValues = null;
1559
        } else {
1560
            this.availableValues = (DynObjectValueItem[]) values.toArray(
1561
                    new DynObjectValueItem[values.size()]
1562
            );
1563
        }
1564
        this.setFixed(false);
1565
        return this;
1566
    }
1567

    
1568
    @Override
1569
    public String getGroup() {
1570
        return this.groupName;
1571
    }
1572

    
1573
    @Override
1574
    public int getOder() {
1575
        return this.order;
1576
    }
1577

    
1578
    @Override
1579
    public String getLabel() {
1580
        if (this.label == null) {
1581
            return this.getName();
1582
        }
1583
        return this.label;
1584
    }
1585

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

    
1595
    @Override
1596
    public DynField setLabel(String label) {
1597
        this.label = label;
1598
        this.setFixed(false);
1599
        return this;
1600
    }
1601

    
1602
    @Override
1603
    public DynField setShortLabel(String shortLabel) {
1604
        this.shortLabel = shortLabel;
1605
        this.setFixed(false);
1606
        return this;
1607
    }
1608

    
1609
    @Override
1610
    public String getShortLabel() {
1611
        return StringUtils.isBlank(shortLabel) ? getLabel() : shortLabel;
1612
    }
1613

    
1614
    @Override
1615
    public String getLocalizedShortLabel() {
1616
        if (StringUtils.isBlank(shortLabel)) {
1617
            return this.getLocalizedLabel();
1618
        }
1619
        I18nManager i18n = ToolsLocator.getI18nManager();
1620
        return i18n.getTranslation(shortLabel);
1621
    }
1622

    
1623
    @Override
1624
    public DynField setGroup(String groupName) {
1625
        this.groupName = groupName;
1626
        this.setFixed(false);
1627
        return this;
1628
    }
1629

    
1630
    @Override
1631
    public DynField setOrder(int order) {
1632
        this.order = order;
1633
        this.setFixed(false);
1634
        return this;
1635
    }
1636

    
1637
    @Override
1638
    public DynField setHidden(boolean hidden) {
1639
        this.hidden = hidden;
1640
        this.setFixed(false);
1641
        return this;
1642
    }
1643

    
1644
    @Override
1645
    public boolean isHidden() {
1646
        return this.hidden;
1647
    }
1648

    
1649
    @Override
1650
    public DynField setReadOnly(boolean readOnly) {
1651
        this.readOnly = readOnly;
1652
        this.setFixed(false);
1653
        return this;
1654
    }
1655

    
1656
    @Override
1657
    public boolean isContainer() {
1658
        return false;
1659
    }
1660

    
1661
    @Override
1662
    public Class getClassOfItems() {
1663
        return null;
1664
    }
1665

    
1666
    @Override
1667
    public DynField setClassOfItems(Class theClass) {
1668
        throw new UnsupportedOperationException();
1669
    }
1670

    
1671
    @Override
1672
    public DynField setType(DataType type) {
1673
        throw new UnsupportedOperationException();
1674
    }
1675

    
1676
    @Override
1677
    public DynField setSubtype(String subtype) {
1678
        throw new UnsupportedOperationException();
1679
    }
1680

    
1681
    @Override
1682
    public boolean isTime() {
1683
        return isTime;
1684
    }
1685

    
1686
    @Override
1687
    public FeatureAttributeGetter getFeatureAttributeGetter() {
1688
        return featureAttributeGetter;
1689
    }
1690

    
1691
    @Override
1692
    public void setFeatureAttributeGetter(
1693
            FeatureAttributeGetter featureAttributeTransform) {
1694
        this.featureAttributeGetter = featureAttributeTransform;
1695
        this.setFixed(false);
1696
    }
1697

    
1698
    @Override
1699
    public FeatureAttributeEmulator getFeatureAttributeEmulator() {
1700
        return this.featureAttributeEmulator;
1701
    }
1702

    
1703
    public FeatureAttributeDescriptor setFeatureAttributeEmulator(FeatureAttributeEmulator featureAttributeEmulator) {
1704
        this.featureAttributeEmulator = featureAttributeEmulator;
1705
        this.setFixed(false);
1706
        return this;
1707
    }
1708

    
1709
    @Override
1710
    public boolean isIndexed() {
1711
        return this.indexed;
1712
    }
1713

    
1714
    @Override
1715
    public boolean isForeingKey() {
1716
        return this.foreingKey != null && this.foreingKey.isForeingKey();
1717
    }
1718

    
1719
    @Override
1720
    public ForeingKey getForeingKey() {
1721
        return this.foreingKey;
1722
    }
1723

    
1724
    @Override
1725
    public boolean allowIndexDuplicateds() {
1726
        return this.allowIndexDuplicateds;
1727
    }
1728

    
1729
    @Override
1730
    public boolean isIndexAscending() {
1731
        return this.isIndexAscending;
1732
    }
1733

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

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

    
1744
    @Override
1745
    public String getClassNameOfValue() {
1746
        return null;
1747
    }
1748

    
1749
    @Override
1750
    public DynStruct getDynClassOfValue() {
1751
        return null;
1752
    }
1753

    
1754
    @Override
1755
    public DynField setTypeOfItems(int type) {
1756
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1757
    }
1758

    
1759
    @Override
1760
    public int getTypeOfItems() {
1761
        return DataTypes.INVALID;
1762
    }
1763

    
1764
    @Override
1765
    public DynField setClassOfItems(DynStruct dynStrct) {
1766
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1767
    }
1768

    
1769
    @Override
1770
    public DynField setClassOfItems(String theClassNameOfValue) {
1771
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
1772
    }
1773

    
1774
    @Override
1775
    public String getClassNameOfItems() {
1776
        return null;
1777
    }
1778

    
1779
    @Override
1780
    public DynStruct getDynClassOfItems() {
1781
        return null;
1782
    }
1783

    
1784
    @Override
1785
    public DynField setRelationType(int relationType) {
1786
        this.relationType = relationType;
1787
        this.setFixed(false);
1788
        return this;
1789
    }
1790

    
1791
    @Override
1792
    public int getRelationType() {
1793
        return this.relationType;
1794
    }
1795

    
1796
    @Override
1797
    public DynField setAvailableValues(DynMethod availableValuesMethod) {
1798
        this.availableValuesMethod = availableValuesMethod;
1799
        this.setFixed(false);
1800
        return this;
1801
    }
1802

    
1803
    @Override
1804
    public DynMethod getAvailableValuesMethod() {
1805
        if (this.availableValuesMethod != null) {
1806
            return this.availableValuesMethod;
1807
        }
1808
        if (this.availableValuesFilter == null && !this.isForeingKey()) {
1809
            return null;
1810
        }
1811
        DynMethod method = new AbstractDynMethod("getAvailableValuesOf" + this.getName()) {
1812
            @Override
1813
            public Object invoke(DynObject context, Object[] args) throws DynMethodException {
1814
                return getAvailableValues(context);
1815
            }
1816
        };
1817
        return method;
1818
    }
1819

    
1820
    @Override
1821
    public DynMethod getCalculateMethod() {
1822
        return this.calculateMethod;
1823
    }
1824

    
1825
    @Override
1826
    public DynField setCalculateMethod(DynMethod method) {
1827
        this.calculateMethod = method;
1828
        this.setFixed(false);
1829
        return this;
1830
    }
1831

    
1832
    @Override
1833
    public boolean isCalculated() {
1834
        return this.calculateMethod != null;
1835
    }
1836

    
1837
    @Override
1838
    public Object getCalculatedValue(DynObject self) {
1839
        try {
1840
            return this.calculateMethod.invoke(self, new Object[]{this});
1841
        } catch (DynMethodException ex) {
1842
            throw new RuntimeException(ex);
1843
        }
1844
    }
1845

    
1846
    @Override
1847
    public DynField setValidateElements(boolean validate) {
1848
        return this;
1849
    }
1850

    
1851
    @Override
1852
    public boolean getValidateElements() {
1853
        return false;
1854
    }
1855

    
1856
    @Override
1857
    public boolean hasLabel() {
1858
        return StringUtils.isNotBlank(this.label);
1859
    }
1860

    
1861
    @Override
1862
    public boolean hasShortLabel() {
1863
        return StringUtils.isNotBlank(this.shortLabel);
1864
    }
1865

    
1866
    @Override
1867
    public boolean hasDescription() {
1868
        return StringUtils.isNotBlank(this.description);
1869
    }
1870

    
1871
    @Override
1872
    public FeatureAttributeDescriptor getValue() {
1873
        return this;
1874
    }
1875

    
1876
    @Override
1877
    public int getDisplaySize() {
1878
        return this.displaySize;
1879
    }
1880

    
1881
    @Override
1882
    public boolean isInAvailableValues(Object valueToCheck) {
1883
        if (this.getAvailableValues() != null) {
1884
            for (DynObjectValueItem availableValue : this.getAvailableValues()) {
1885
                if (Objects.equals(valueToCheck, availableValue.getValue())) {
1886
                    return true;
1887
                }
1888
            }
1889
        }
1890
        return false;
1891
    }
1892

    
1893
    private class ConstantValueEvaluator extends AbstractEvaluator {
1894

    
1895
        @Override
1896
        public Object evaluate(EvaluatorData data) throws EvaluatorException {
1897
            return defaultValue;
1898
        }
1899

    
1900
        @Override
1901
        public String getName() {
1902
            return "Constant attribute " + name;
1903
        }
1904
    }
1905

    
1906
    public void setConstantValue(boolean isConstantValue) {
1907
        if (isConstantValue) {
1908
            /* Cuando un attributo tiene asociado un evaluador, este se interpreta
1909
             * como que no debe cargarse de la fuente de datos subyacente, siendo
1910
             * el evaluador el que se encarga de proporcionar su valor.
1911
             * Nos limitamos a asignar un evaluador que retorna simpre el valor
1912
             * por defecto para ese attributo.
1913
             */
1914
            this.evaluator = new ConstantValueEvaluator();
1915
        } else {
1916
            this.evaluator = null;
1917
        }
1918
        this.setFixed(false);
1919
    }
1920

    
1921
    @Override
1922
    public boolean isComputed() {
1923
        return featureAttributeEmulator != null || evaluator != null || isCalculated();
1924
    }
1925

    
1926
    @Override
1927
    public FeatureStore getStore() {
1928
        FeatureType ftype = this.getFeatureType();
1929
        if (ftype == null) {
1930
            return null;
1931
        }
1932
        return ftype.getStore();
1933
    }
1934

    
1935
    @Override
1936
    public FeatureType getFeatureType() {
1937
        if (this.typeRef == null) {
1938
            return null;
1939
        }
1940
        FeatureType ftype = (FeatureType) this.typeRef.get();
1941
//        LOGGER.info(String.format("FeatureAttributeDescriptor[%08x] get FeatureType [%08x], ref [%08x].", this.hashCode(), ftype.hashCode(), typeRef.hashCode()));
1942
        return ftype;
1943
    }
1944

    
1945
    public FeatureAttributeDescriptor setInterval(Interval interval) {
1946
        this.interval = interval;
1947
        this.setFixed(false);
1948
        return this;
1949
    }
1950

    
1951
    public void fixAll() {
1952
        if(this.fixed) {
1953
            return;
1954
        }
1955
        if (!this.getDataType().supportSize()) {
1956
            this.size = 0;
1957
        }
1958
        NumberPrecisionAndScale ps = this.getDataType().fixPrecisionAndScale(this.precision, this.scale);
1959
        this.precision = ps.getPrecision();
1960
        this.scale = ps.getScale();
1961

    
1962
        switch (this.getType()) {
1963
            case DataTypes.INSTANT:
1964
            case DataTypes.INTERVAL:
1965
            case DataTypes.DATE:
1966
            case DataTypes.TIME:
1967
            case DataTypes.TIMESTAMP:
1968
                if (this.getInterval() != null) {
1969
                    this.isTime = true;
1970
                }
1971
                break;
1972
        }
1973
//        TODO: Habria que meter este cambio en proximos builds 2022/11/16.
1974
//        if( this.isPrimaryKey() ) {
1975
//            this.allowNull = false;
1976
//        }
1977
        this.fixed = true;
1978
        
1979
    }
1980

    
1981
    @Override
1982
    public String[] getRequiredFieldNames() {
1983
        FeatureAttributeEmulator emulator = this.getFeatureAttributeEmulator();
1984
        if (emulator == null) {
1985
            return null;
1986
        }
1987
        return emulator.getRequiredFieldNames();
1988
    }
1989

    
1990
    @Override
1991
    public void recentUsed() {
1992
        DefaultFeatureType.RECENTS_USEDS.add(this);
1993
    }
1994

    
1995
    @Override
1996
    public boolean hasOnlyMetadataChanges(FeatureAttributeDescriptor other) {
1997
        if (other == null) {
1998
            throw new NullPointerException();
1999
        }
2000
        DefaultFeatureAttributeDescriptor old = (DefaultFeatureAttributeDescriptor) other;
2001
        if (!StringUtils.equalsIgnoreCase(old.name, this.name)) {
2002
            return false;
2003
        }
2004
        if (old.isComputed() && old.isComputed() == this.isComputed()) {
2005
            return true;
2006
        }
2007
        if (this.dataType != old.dataType) {
2008
            return false;
2009
        }
2010
        if (this.size != old.size) {
2011
            return false;
2012
        }
2013
        if (this.precision != old.precision) {
2014
            return false;
2015
        }
2016
//        if( this.primaryKey != old.primaryKey ) {
2017
//            return false;
2018
//        }
2019
        if (this.geomType != old.geomType) {
2020
            return false;
2021
        }
2022
        if (this.SRS != old.SRS) {
2023
            return false;
2024
        }
2025
        if (this.isAutomatic != old.isAutomatic) {
2026
            return false;
2027
        }
2028
        return true;
2029
    }
2030

    
2031
    private class PropertiesBuilder {
2032

    
2033
        private String name;
2034
        private DataType type;
2035
        private final Map<String, String> sets;
2036
        private Map<String, String> tags;
2037
        private String sep;
2038

    
2039
        public PropertiesBuilder() {
2040
            this.sets = new LinkedHashMap<>();
2041
        }
2042

    
2043
        public void separator(String sep) {
2044
            this.sep = sep;
2045
        }
2046

    
2047
        public void name(String name) {
2048
            this.name = name;
2049
        }
2050

    
2051
        public void type(DataType type) {
2052
            this.type = type;
2053
        }
2054

    
2055
        public void set(String name, ForeingKey fk) {
2056
            if (fk == null) {
2057
                return;
2058
            }
2059
            if (!fk.isForeingKey()) {
2060
                return;
2061
            }
2062
            this.set(name, fk.isForeingKey());
2063
            this.set(name + "_code", fk.getCodeName());
2064
            this.set(name + "_label", fk.getLabelFormula());
2065
            this.set(name + "_closedlist", fk.isClosedList());
2066
            this.set(name + "_table", fk.getTableName());
2067
        }
2068

    
2069
        public void set(String name, FeatureAttributeEmulator value) {
2070
            if (value == null) {
2071
                return;
2072
            }
2073
            if (value instanceof FeatureAttributeEmulatorExpression) {
2074
                this.set(name, ((FeatureAttributeEmulatorExpression) value).getExpression().getPhrase());
2075
            }
2076
        }
2077

    
2078
        public void set(String name, IProjection value) {
2079
            if (value == null) {
2080
                return;
2081
            }
2082
            this.set(name, value.getAbrev());
2083
        }
2084

    
2085
        public void set(String name, GeometryType value) {
2086
            if (value == null) {
2087
                return;
2088
            }
2089
            this.set(name, value.getFullName().replace(":", "@"));
2090
        }
2091

    
2092
        public void set(String name, Object value) {
2093
            if (value == null) {
2094
                return;
2095
            }
2096
            String s = Objects.toString(value, "");
2097
            if (StringUtils.isBlank(s)) {
2098
                return;
2099
            }
2100
            this.sets.put(name, s);
2101
        }
2102

    
2103
        public void set(String name, Object value, Object skipValue) {
2104
            if (value == null || value == skipValue) {
2105
                return;
2106
            }
2107
            String s = Objects.toString(value, "");
2108
            if (StringUtils.isBlank(s)) {
2109
                return;
2110
            }
2111
            this.sets.put(name, s);
2112
        }
2113

    
2114
        public void tag(String name, String value) {
2115
            if (value == null) {
2116
                return;
2117
            }
2118
            if (StringUtils.isBlank(value)) {
2119
                return;
2120
            }
2121
            if( this.tags == null ) {
2122
                this.tags = new HashMap<>();
2123
            }
2124
            this.tags.put(name, value);
2125
        }
2126

    
2127
        @Override
2128
        public String toString() {
2129
            StringBuilder builder = new StringBuilder();
2130
            builder.append(this.name);
2131
            builder.append(sep);
2132
            builder.append(this.type.getName());
2133
            for (String key : this.sets.keySet()) {
2134
                builder.append(sep);
2135
                String s = this.sets.get(key);
2136
                if(s.contains(sep)){
2137
                    builder.append("setesc");
2138
                    builder.append(sep);
2139
                    builder.append("html");
2140
                    builder.append(sep);
2141
                    builder.append(key);
2142
                    builder.append("=");
2143
                    s = StringEscapeUtils.escapeHtml3(s);
2144
                    s = StringUtils.replace(s, sep, "&#"+((int)(sep.charAt(0)))+";");
2145
                    builder.append(s);
2146
                } else {
2147
                    builder.append("set");
2148
                    builder.append(sep);
2149
                    builder.append(key);
2150
                    builder.append("=");
2151
                    builder.append(s);
2152
                }
2153
                
2154
            }
2155
            if( this.tags!=null ) {
2156
                for (String key : this.tags.keySet()) {
2157
                    builder.append(sep);
2158
                    String s = this.tags.get(key);
2159
                    if(s.contains(sep)){
2160
                        builder.append("tagesc");
2161
                        builder.append(sep);
2162
                        builder.append("html");
2163
                        builder.append(sep);
2164
                        builder.append(key);
2165
                        builder.append("=");
2166
                        s = StringEscapeUtils.escapeHtml3(s);
2167
                        s = StringUtils.replace(s, sep, "&#"+((int)(sep.charAt(0)))+";");
2168
                        builder.append(s);
2169
                    } else {
2170
                        builder.append("tag");
2171
                        builder.append(sep);
2172
                        builder.append(key);
2173
                        builder.append("=");
2174
                        builder.append(s);
2175
                    }
2176
                }
2177
            }
2178
            return builder.toString();
2179
        }
2180
    }
2181

    
2182
    private String getAll() {
2183
        PropertiesBuilder builder = new PropertiesBuilder();
2184
        builder.separator("/");
2185
        builder.name(this.name);
2186
        builder.type(this.dataType);
2187
        builder.set("size", this.size, 0);
2188
        switch (this.getType()) {
2189
            case DataTypes.BYTE:
2190
            case DataTypes.INTEGER:
2191
            case DataTypes.LONG:
2192
                break;
2193
            case DataTypes.FLOAT:
2194
            case DataTypes.DOUBLE:
2195
                if(this.locale != null){
2196
                    builder.set("locale", this.getLocale());
2197
                }
2198
                break;
2199
            case DataTypes.DECIMAL:
2200
                builder.set("precision", this.precision);
2201
                builder.set("scale", this.scale);
2202
                builder.set("roundMode", this.getRoundMode());
2203
                if(this.locale != null){
2204
                    builder.set("locale", this.getLocale());
2205
                }
2206
                break;
2207
            case DataTypes.DATE:
2208
            case DataTypes.TIME:
2209
            case DataTypes.TIMESTAMP:
2210
                if(this.locale != null){
2211
                    builder.set("locale", this.getLocale());
2212
                }
2213
                break;
2214
            case DataTypes.GEOMETRY:
2215
                IProjection proj = this.getSRS();
2216
                if (proj != null) {
2217
                    builder.set("srs", proj.getAbrev().replace(":", "@"));
2218
                }
2219
                GeometryType theGeomType = this.getGeomType();
2220
                if (theGeomType != null) {
2221
                    String geomTypeName = GeometryUtils.getGeometryTypeName(this.getGeomType().getType());
2222
                    String geomSubtypeName = GeometryUtils.getGeometrySubtypeName(this.getGeomType().getSubType());
2223
                    builder.set("geomtype", geomTypeName + "@" + geomSubtypeName);
2224
                }
2225
                break;
2226
        }
2227
        builder.set("hidden", this.isHidden(), false);
2228
        builder.set("readOnly", this.isReadOnly(), false);
2229
        builder.set("allowNull", this.allowNull(), true);
2230
        builder.set("pk", this.isPrimaryKey(), false);
2231
        builder.set("automatic", this.isAutomatic(), false);
2232
        builder.set("isindexed", this.isIndexed(), false);
2233
        builder.set("isindexascending", this.isIndexAscending(), false);
2234
        builder.set("allowindexduplicateds", this.allowIndexDuplicateds(), false);
2235
        builder.set("isTime", this.isTime(), false);
2236
        builder.set("profile", this.getDataProfileName());
2237
        builder.set("group", this.getGroup());
2238
        builder.set("description", this.description);
2239
        builder.set("label", this.label);
2240
        builder.set("shortLabel", this.shortLabel);
2241
        builder.set("defaultvalue", this.getDefaultValue());
2242
        builder.set("order", this.getOder());
2243
        if (this.getFeatureAttributeEmulator() instanceof FeatureAttributeEmulatorExpression) {
2244
            Expression exp = ((FeatureAttributeEmulatorExpression) this.getFeatureAttributeEmulator()).getExpression();
2245
            if (exp != null) {
2246
                builder.set("expression", exp.getPhrase());
2247
            }
2248
        }
2249
        builder.set("isAvoidCachingAvailableValues", this.isAvoidCachingAvailableValues(), false);
2250
        if (this.isForeingKey()) {
2251
            builder.set("fk", this.isForeingKey());
2252
            builder.set("fk_table", this.getForeingKey().getTableName());
2253
            builder.set("fk_code", this.getForeingKey().getCodeName());
2254
            builder.set("fk_label", this.getForeingKey().getLabelFormula());
2255
            builder.set("fk.closedlist", this.getForeingKey().isClosedList());
2256
        }
2257
        Tags theTags = this.getTags();
2258
        for (String tagname : theTags) {
2259
            String value = theTags.getString(tagname,null);
2260
            if( value!=null ) {
2261
                builder.tag(tagname, value);
2262
            }
2263
        }
2264
        return builder.toString();
2265
    }
2266

    
2267
    @Override
2268
    public Object get(String name) {
2269
        if (StringUtils.isBlank(name)) {
2270
            throw new IllegalArgumentException("Name can't be empty");
2271
        }
2272
        switch (name.trim().toLowerCase()) {
2273
            case "all":
2274
                return this.getAll();
2275
            case "isreadonly":
2276
            case "readonly":
2277
                return this.isReadOnly();
2278
            case "hidden":
2279
                return this.isHidden();
2280
            case "allownull":
2281
                return this.allowNull();
2282
            case "pk":
2283
            case "ispk":
2284
            case "primarykey":
2285
            case "isprimarykey":
2286
                return this.isPrimaryKey();
2287
            case "isindexed":
2288
                return this.isIndexed();
2289
            case "isindexascending":
2290
                return this.isIndexAscending();
2291
            case "allowindexduplicateds":
2292
                return this.allowIndexDuplicateds();
2293
            case "isautomatic":
2294
            case "automatic":
2295
                return this.isAutomatic();
2296
            case "time":
2297
            case "istime":
2298
                return this.isTime();
2299
            case "profile":
2300
                return this.getDataProfile();
2301
            case "group":
2302
                return this.getGroup();
2303
            case "description":
2304
                return this.getDescription();
2305
            case "label":
2306
                return this.getLabel();
2307
            case "shortlabel":
2308
                return this.getShortLabel();
2309
            case "isavoidcachingavailablevalues":
2310
            case "avoidcachingavailablevalues":
2311
            case "nocachingavailablevalues":
2312
                return this.isAvoidCachingAvailableValues();
2313
            case "expression":
2314
                return this.getFeatureAttributeEmulator();
2315
            case "size":
2316
                return this.getSize();
2317
            case "precision":
2318
                return this.getPrecision();
2319
            case "scale":
2320
                return this.getScale();
2321
            case "roundmode":
2322
                return this.getRoundMode();
2323
            case "locale":
2324
                return this.getLocale();
2325
            case "order":
2326
                return this.getOder();
2327
            case "isfk":
2328
            case "isforeingkey":
2329
                return this.isForeingKey();
2330
            case "fk":
2331
            case "foreingkey":
2332
                return this.getForeingKey();
2333
            case "fk_code":
2334
            case "foreingkey_code":
2335
            case "foreingkey.code":
2336
                return this.getForeingKey().getCodeName();
2337
            case "fk_label":
2338
            case "foreingkey_label":
2339
            case "foreingkey.label":
2340
                return this.getForeingKey().getLabelFormula();
2341
            case "fk.closedlist":
2342
            case "foreingkey_closedlist":
2343
            case "foreingkey.closedlist":
2344
                return this.getForeingKey().isClosedList();
2345
            case "fk_table":
2346
            case "foreingkey_table":
2347
            case "foreingkey.table":
2348
                return this.getForeingKey().getTableName();
2349
            case "interval":
2350
                return this.getInterval();
2351
            case "geomtype":
2352
            case "geometrytype":
2353
                return this.getGeomType();
2354
            case "srs":
2355
                return this.getSRS();
2356
            case "defaultvalue":
2357
                return this.getDefaultValue();
2358
            default:
2359
                throw new IllegalArgumentException("Name attribute '" + name + "' not valid.");
2360
        }
2361
    }
2362

    
2363
    public void setSRSForced(IProjection SRS) {
2364
        this.SRS = SRS;
2365
        this.setFixed(false);
2366
    }
2367

    
2368
    @Override
2369
    public JsonObject toJson() {
2370
        return this.toJsonBuilder().build();
2371
    }
2372

    
2373
    @Override
2374
    public JsonObjectBuilder toJsonBuilder() {
2375
        JsonObjectBuilder builder = Json.createObjectBuilder();
2376
        builder.add_class(this);
2377
        builder.add("name", this.name);
2378
        builder.add("description", this.description);
2379
        builder.add("label", this.label);
2380
        builder.add("shortLabel", this.shortLabel);
2381
        builder.add("order", this.order);
2382
        builder.add("groupName", this.groupName);
2383
        builder.add("dataType", this.dataType);
2384
        builder.add("size", this.size);
2385
        builder.add("precision", this.precision);
2386
        builder.add("scale", this.scale);
2387
        builder.add("roundMode", this.roundMode);
2388

    
2389
        builder.add("allowNull", this.allowNull);
2390
        builder.add("primaryKey", this.primaryKey);
2391
        builder.add("readOnly", this.readOnly);
2392
        builder.add("isAutomatic", this.isAutomatic);
2393
        builder.add("isTime", this.isTime);
2394
        builder.add("indexed", this.indexed);
2395
        builder.add("isIndexAscending", this.isIndexAscending);
2396
        builder.add("allowIndexDuplicateds", this.allowIndexDuplicateds);
2397
        builder.add("hidden", this.hidden);
2398
        builder.add("avoidCachingAvailableValues", this.avoidCachingAvailableValues);
2399

    
2400
        builder.add("geometryType", this.getGeomType());
2401
        builder.add("srs", this.getSRS());
2402

    
2403
        builder.add("relationType", this.relationType);
2404
        builder.add("displaySize", this.displaySize);
2405
        if(this.locale == null){ //!this.hasLocale()
2406
            builder.addNull("locale");
2407
        } else {
2408
            builder.add("locale", this.getLocale());
2409
        }
2410
        builder.add("expression", this.getFeatureAttributeEmulator());
2411
        if (this.isForeingKey()) {
2412
            builder.add("fk", this.isForeingKey());
2413
            builder.add("fk_table", this.getForeingKey().getTableName());
2414
            builder.add("fk_code", this.getForeingKey().getCodeName());
2415
            builder.add("fk_label", this.getForeingKey().getLabelFormula());
2416
            builder.add("fk_closedlist", this.getForeingKey().isClosedList());
2417
        }
2418
        builder.add("availableValuesExpression", this.availableValuesExpression);
2419
        builder.add("defaultValue", Objects.toString(this.defaultValue, null));
2420
        builder.add("dataProfile", this.getDataProfileName());
2421
        builder.add("tags", tags);
2422
        builder.add("availableValues", availableValues);
2423
        builder.add("additionalInfo", this.additionalInfo);
2424
        builder.add("defaultFormat", this.defaultFormat);
2425
        return builder;
2426
    }
2427

    
2428
    public void fromJson(JsonObject json) {
2429
        this.name = json.getString("name");
2430
        this.description = json.getString("description");
2431
        this.label = json.getString("label");
2432
        this.shortLabel = json.getString("shortLabel");
2433
        this.order = json.getInt("order");
2434
        this.groupName = json.getString("groupName");
2435
        this.precision = json.getInt("precision");
2436
        this.size = json.getInt("size");
2437
        this.scale = json.getInt("scale");
2438
        this.roundMode = json.getInt("roundMode");
2439

    
2440
        this.allowNull = json.getBoolean("allowNull");
2441
        this.primaryKey = json.getBoolean("primaryKey");
2442
        this.readOnly = json.getBoolean("readOnly");
2443
        this.isAutomatic = json.getBoolean("isAutomatic");
2444
        this.isTime = json.getBoolean("isTime");
2445
        this.indexed = json.getBoolean("indexed");
2446
        this.isIndexAscending = json.getBoolean("isIndexAscending");
2447
        this.allowIndexDuplicateds = json.getBoolean("allowIndexDuplicateds");
2448
        this.hidden = json.getBoolean("hidden");
2449
        this.avoidCachingAvailableValues = json.getBoolean("avoidCachingAvailableValues");
2450

    
2451
        this.relationType = json.getInt("relationType");
2452
        this.displaySize = json.getInt("displaySize");
2453

    
2454
        this.dataType = (DataType) Json.toObject(json, "dataType");
2455
        this.geomType = (GeometryType) Json.toObject(json, "geometryType");
2456
        this.SRS = (IProjection) Json.toObject(json, "srs");
2457
        this.locale = (Locale) Json.toObject(json, "locale");
2458
        this.featureAttributeEmulator = (FeatureAttributeEmulator) Json.toObject(json, "expression");
2459

    
2460
        this.tags = (Tags) Json.toObject(json, "tags");
2461
        this.additionalInfo = Json.toMap(json, "additionalInfo");
2462
        this.availableValues = (DynObjectValueItem[]) Json.toArray(json, "availableValues", new DynObjectValueItem[0]);
2463
        this.dataProfile = json.getString("dataProfile", null);
2464
        try {
2465
            this.defaultValue = json.getString("defaultValue", null);
2466
            if(!(this.defaultValue instanceof String && ExpressionUtils.isDynamicText((String) this.defaultValue))){
2467
                this.defaultValue = this.coerce(this.defaultValue);
2468
            }
2469
        } catch (Exception ex) {
2470
            LOGGER.warn("Can't retrive default value for attribute '" + this.name + "'.", ex);
2471
        }
2472
        this.availableValuesExpression = (Expression) Json.toObject(json, "availableValuesExpression");
2473
        if (json.getBoolean("fk", false)) {
2474
            this.foreingKey = new DefaultForeingKey();
2475
            this.foreingKey.setForeingKey(true);
2476
            this.foreingKey.setTableName(json.getString("fk_table", null));
2477
            this.foreingKey.setCodeName(json.getString("fk_code", null));
2478
            this.foreingKey.setLabelFormula(json.getString("fk_label", null));
2479
            this.foreingKey.setClosedList(json.getBoolean("fk_closedlist", false));
2480
        } else {
2481
            this.foreingKey = null;
2482
        }
2483
        this.defaultFormat = json.getString("defaultFormat",null);
2484
        this.setFixed(false);
2485
    }
2486

    
2487
    private static class TheJsonSerializer implements JsonManager.JsonSerializer {
2488

    
2489
        public TheJsonSerializer() {
2490
        }
2491

    
2492
        @Override
2493
        public Class getObjectClass() {
2494
            return DefaultFeatureAttributeDescriptor.class;
2495
        }
2496

    
2497
        @Override
2498
        public Object toObject(JsonObject json) {
2499
            DefaultFeatureAttributeDescriptor o = new DefaultFeatureAttributeDescriptor();
2500
            o.fromJson(json);
2501
            return o;
2502
        }
2503

    
2504
        @Override
2505
        public JsonObjectBuilder toJsonBuilder(Object value) {
2506
            return ((SupportToJson) value).toJsonBuilder();
2507
        }
2508

    
2509
    }
2510

    
2511
    public static void selfRegister() {
2512
        Json.registerSerializer(new TheJsonSerializer());
2513
    }
2514

    
2515
    @Override
2516
    public String getDefaultFormat() {
2517
        return this.defaultFormat;
2518
    }
2519

    
2520
    @Override
2521
    public String format(Object value) {
2522
        if(value == null){
2523
            return "";
2524
        }
2525
        try {
2526
            if( StringUtils.isBlank(this.defaultFormat)) {
2527
                if( this.locale==null ) { // !this.hasLocale()
2528
                    return DataTypeUtils.toString(Locale.getDefault(), value, Objects.toString(value, ""));
2529
                } else {
2530
                    return DataTypeUtils.toString(this.locale, value, Objects.toString(value, ""));
2531
                }
2532
            }
2533
            return String.format(this.defaultFormat, value);
2534
        } catch(Exception ex) {
2535
            return Objects.toString(value, "");
2536
        }
2537
    }
2538

    
2539
    @Override
2540
    public String toString() {
2541
        try {
2542
            String s = this.getAll();
2543
            return s;
2544
        } catch(Exception ex) {
2545
            return super.toString();
2546
        }
2547
    }
2548

    
2549
    public void setFixed(boolean fixed){
2550
        this.fixed = fixed;
2551
        this.observableHelper.notifyObservers(this, new BaseNotification(NOTIFICATION_FIXED_CHANGED, 0));
2552
    }
2553
    
2554
    public boolean isFixed() {
2555
        return this.fixed;
2556
    }
2557
    
2558
    @Override
2559
    public void addObserver(Observer o) {
2560
        this.observableHelper.addObserver(o);
2561
    }
2562

    
2563
    @Override
2564
    public void deleteObserver(Observer o) {
2565
        this.observableHelper.deleteObserver(o);
2566
    }
2567

    
2568
    @Override
2569
    public void deleteObservers() {
2570
        this.observableHelper.deleteObservers();
2571
    }
2572

    
2573
}