OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Rev 174 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 174 Rev 182
Line 1... Line 1...
1
/*
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
3
 * 
4
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
4
 * Copyright 2011-2019 OpenConcerto, by ILM Informatique. All rights reserved.
5
 * 
5
 * 
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
6
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
7
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
8
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
9
 * language governing permissions and limitations under the License.
9
 * language governing permissions and limitations under the License.
Line 34... Line 34...
34
import org.openconcerto.utils.ExceptionUtils;
34
import org.openconcerto.utils.ExceptionUtils;
35
import org.openconcerto.utils.ListMap;
35
import org.openconcerto.utils.ListMap;
36
import org.openconcerto.utils.RecursionType;
36
import org.openconcerto.utils.RecursionType;
37
import org.openconcerto.utils.SetMap;
37
import org.openconcerto.utils.SetMap;
38
import org.openconcerto.utils.Tuple2;
38
import org.openconcerto.utils.Tuple2;
-
 
39
import org.openconcerto.utils.Value;
39
import org.openconcerto.utils.cc.IClosure;
40
import org.openconcerto.utils.cc.IClosure;
40
import org.openconcerto.utils.cc.ITransformer;
41
import org.openconcerto.utils.cc.ITransformer;
41
import org.openconcerto.utils.cc.IdentitySet;
42
import org.openconcerto.utils.cc.IdentitySet;
42
import org.openconcerto.utils.cc.LinkedIdentitySet;
43
import org.openconcerto.utils.cc.LinkedIdentitySet;
43
import org.openconcerto.utils.cc.TransformedMap;
44
import org.openconcerto.utils.cc.TransformedMap;
Line 107... Line 108...
107
         * Copy every SQLRowValues.
108
         * Copy every SQLRowValues.
108
         */
109
         */
109
        COPY_ROW
110
        COPY_ROW
110
    }
111
    }
111
 
112
 
-
 
113
    public static enum SQLDefaultCopyMode {
-
 
114
        COPY, REMOVE, PARSE_ELSE_REMOVE, PARSE_ELSE_ERROR;
-
 
115
    }
-
 
116
 
112
    static public enum CreateMode {
117
    static public enum CreateMode {
113
        /**
118
        /**
114
         * Never create rows.
119
         * Never create rows.
115
         */
120
         */
116
        CREATE_NONE,
121
        CREATE_NONE,
Line 148... Line 153...
148
        public String toString() {
153
        public String toString() {
149
            return SQLRowValues.class.getSimpleName() + ".SQL_EMPTY_LINK";
154
            return SQLRowValues.class.getSimpleName() + ".SQL_EMPTY_LINK";
150
        }
155
        }
151
    };
156
    };
152
 
157
 
-
 
158
    public static final class SQLExpression {
-
 
159
        private final String sql;
-
 
160
 
-
 
161
        public SQLExpression(String sql) {
-
 
162
            super();
-
 
163
            this.sql = sql;
-
 
164
        }
-
 
165
 
-
 
166
        public final String getSQL() {
-
 
167
            return this.sql;
-
 
168
        }
-
 
169
 
-
 
170
        @Override
-
 
171
        public String toString() {
-
 
172
            return this.getClass().getSimpleName() + " " + this.sql;
-
 
173
        }
-
 
174
    }
-
 
175
 
153
    static public enum ValidityCheck {
176
    static public enum ValidityCheck {
154
        /**
177
        /**
155
         * The check is never performed.
178
         * The check is never performed.
156
         */
179
         */
157
        FORBIDDEN {
180
        FORBIDDEN {
Line 339... Line 362...
339
     * and all others connected to it and {@link SQLRowValuesCluster#freeze()} the copy. I.e. if the
362
     * and all others connected to it and {@link SQLRowValuesCluster#freeze()} the copy. I.e. if the
340
     * result is to be shared among threads, it still needs to be safely published.
363
     * result is to be shared among threads, it still needs to be safely published.
341
     * 
364
     * 
342
     * @return this if already frozen, otherwise a frozen copy of this.
365
     * @return this if already frozen, otherwise a frozen copy of this.
343
     */
366
     */
-
 
367
    @Override
344
    public final SQLRowValues toImmutable() {
368
    public final SQLRowValues toImmutable() {
345
        if (this.isFrozen())
369
        if (this.isFrozen())
346
            return this;
370
            return this;
347
        return this.getGraph().deepCopy(this, true);
371
        return this.getGraph().deepCopy(this, true);
348
    }
372
    }
Line 459... Line 483...
459
     * 
483
     * 
460
     * @param fk a foreign key, eg "ID_FAMILLE_2".
484
     * @param fk a foreign key, eg "ID_FAMILLE_2".
461
     * @return the foreign row, eg FAMILLE[1].
485
     * @return the foreign row, eg FAMILLE[1].
462
     */
486
     */
463
    public final SQLRowValues grow(String fk) {
487
    public final SQLRowValues grow(String fk) {
-
 
488
        // growUndefined to make sure we can cast (otherwise if fk is undefined
-
 
489
        // getForeign() returns a row)
-
 
490
        return (SQLRowValues) this.grow(fk, true);
-
 
491
    }
-
 
492
 
-
 
493
    public final SQLRowAccessor grow(String fk, final boolean growUndefined) {
464
        final Object val = this.getContainedObject(fk);
494
        final Object val = this.getContainedObject(fk);
465
        // if fk is in our map with a null value, nothing to grow
495
        // if fk is in our map with a null value, nothing to grow
466
        if (val != null && !(val instanceof SQLRowValues)) {
496
        if (val != null && !(val instanceof SQLRowValues)) {
467
            final SQLRowValues vals = new SQLRowValues(this.getTable());
497
            final SQLRowValues vals = new SQLRowValues(this.getTable());
468
            vals.putRowValues(fk).setAllToNull();
498
            vals.putRowValues(fk).setAllToNull();
469
            this.grow(vals, true);
499
            this.grow(vals, true, growUndefined);
470
        }
500
        }
471
        return (SQLRowValues) this.getForeign(fk);
501
        return this.getForeign(fk);
472
    }
502
    }
473
 
503
 
474
    public final SQLRowValues grow(SQLRowValues graph) {
504
    public final SQLRowValues grow(SQLRowValues graph) {
475
        return this.grow(graph, true);
505
        return this.grow(graph, true);
476
    }
506
    }
Line 677... Line 707...
677
 
707
 
678
    @Override
708
    @Override
679
    public final Object getObject(String fieldName) {
709
    public final Object getObject(String fieldName) {
680
        return this.values.get(fieldName);
710
        return this.values.get(fieldName);
681
    }
711
    }
-
 
712
    
-
 
713
    @Override
-
 
714
    public final Object getObjectNoCheck(String fieldName) {
-
 
715
        return this.values.get(fieldName);
-
 
716
    }
-
 
717
    
-
 
718
    public final Value<Object> getNonDefaultObject(String fieldName) {
-
 
719
        return getNonDefaultObject(fieldName, Object.class);
-
 
720
    }
-
 
721
 
-
 
722
    /**
-
 
723
     * Never return {@link #SQL_DEFAULT}.
-
 
724
     * 
-
 
725
     * @param fieldName the name of the field.
-
 
726
     * @param clazz the type of value expected.
-
 
727
     * @return {@link Value#getNone()} if the field is DEFAULT and the
-
 
728
     *         {@link SQLField#getParsedDefaultValue() default value} cannot be parsed, otherwise
-
 
729
     *         either the non DEFAULT field value or the parsed DEFAULT.
-
 
730
     */
-
 
731
    public final <T> Value<T> getNonDefaultObject(String fieldName, Class<T> clazz) {
-
 
732
        final Object res = this.getObject(fieldName);
-
 
733
        if (res == SQL_DEFAULT) {
-
 
734
            return this.getTable().getField(fieldName).getParsedDefaultValue().cast(clazz);
-
 
735
        } else {
-
 
736
            return Value.getSome(clazz.cast(res));
-
 
737
        }
-
 
738
    }
682
 
739
 
683
    @Override
740
    @Override
684
    public Map<String, Object> getAbsolutelyAll() {
741
    public Map<String, Object> getAbsolutelyAll() {
685
        return getAllValues(ForeignCopyMode.COPY_ROW);
742
        return getAllValues(ForeignCopyMode.COPY_ROW);
686
    }
743
    }
687
 
744
 
688
    protected final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns) {
745
    protected final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns) {
-
 
746
        return this.getAllValues(copyForeigns, SQLDefaultCopyMode.COPY);
-
 
747
    }
-
 
748
 
-
 
749
    public final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns, SQLDefaultCopyMode copyDefaults) {
689
        return this.getAllValues(copyForeigns, false);
750
        return this.getAllValues(copyForeigns, copyDefaults, false);
690
    }
751
    }
691
 
752
 
692
    private final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns, final boolean copy) {
753
    private final Map<String, Object> getAllValues(ForeignCopyMode copyForeigns, SQLDefaultCopyMode copyDefaults, final boolean copy) {
693
        final Map<String, Object> toAdd;
754
        final Map<String, Object> toAdd;
694
        if (copyForeigns == ForeignCopyMode.COPY_ROW || this.foreigns.size() == 0) {
755
        if ((copyForeigns == ForeignCopyMode.COPY_ROW || this.foreigns.size() == 0) && (copyDefaults == SQLDefaultCopyMode.COPY || !this.values.values().contains(SQL_DEFAULT))) {
695
            if (copy) {
756
            if (copy) {
696
                toAdd = createLinkedHashMap(this.size());
757
                toAdd = createLinkedHashMap(this.size());
697
                toAdd.putAll(this.values);
758
                toAdd.putAll(this.values);
698
            } else {
759
            } else {
699
                toAdd = this.values;
760
                toAdd = this.values;
700
            }
761
            }
701
        } else {
762
        } else {
702
            final Set<Entry<String, Object>> entrySet = this.values.entrySet();
763
            final Set<Entry<String, Object>> entrySet = this.values.entrySet();
703
            toAdd = createLinkedHashMap(entrySet.size());
764
            toAdd = createLinkedHashMap(entrySet.size());
704
            for (final Map.Entry<String, Object> e : entrySet) {
765
            for (final Map.Entry<String, Object> e : entrySet) {
-
 
766
                final String fieldName = e.getKey();
-
 
767
                boolean add = true;
-
 
768
                Object val = e.getValue();
-
 
769
                if (e.getValue() == SQL_DEFAULT) {
-
 
770
                    if (copyDefaults == SQLDefaultCopyMode.REMOVE) {
-
 
771
                        add = false;
-
 
772
                    } else if (copyDefaults == SQLDefaultCopyMode.COPY) {
-
 
773
                        add = true;
-
 
774
                    } else {
-
 
775
                        final Value<Object> parsedDefaultValue = this.getTable().getField(fieldName).getParsedDefaultValue();
-
 
776
                        if (parsedDefaultValue.hasValue()) {
-
 
777
                            val = parsedDefaultValue.getValue();
-
 
778
                        } else if (copyDefaults == SQLDefaultCopyMode.PARSE_ELSE_ERROR) {
-
 
779
                            throw new IllegalStateException("Couldn't parse default for " + fieldName);
-
 
780
                        } else {
-
 
781
                            assert copyDefaults == SQLDefaultCopyMode.PARSE_ELSE_REMOVE;
-
 
782
                            add = false;
-
 
783
                        }
-
 
784
                    }
705
                if (!(e.getValue() instanceof SQLRowValues)) {
785
                } else if (e.getValue() instanceof SQLRowValues) {
706
                    toAdd.put(e.getKey(), e.getValue());
786
                    if (copyForeigns == ForeignCopyMode.NO_COPY) {
-
 
787
                        add = false;
707
                } else if (copyForeigns == ForeignCopyMode.COPY_NULL) {
788
                    } else if (copyForeigns == ForeignCopyMode.COPY_ROW) {
708
                    toAdd.put(e.getKey(), null);
789
                        add = true;
709
                } else if (copyForeigns != ForeignCopyMode.NO_COPY) {
790
                    } else if (copyForeigns == ForeignCopyMode.COPY_NULL) {
-
 
791
                        val = null;
-
 
792
                    } else {
710
                    final SQLRowValues foreign = (SQLRowValues) e.getValue();
793
                        final SQLRowValues foreign = (SQLRowValues) e.getValue();
711
                    if (foreign.hasID())
794
                        if (foreign.hasID())
712
                        toAdd.put(e.getKey(), foreign.getIDNumber());
795
                            val = foreign.getIDNumber();
713
                    else if (copyForeigns == ForeignCopyMode.COPY_ID_OR_ROW)
796
                        else if (copyForeigns == ForeignCopyMode.COPY_ID_OR_RM)
714
                        toAdd.put(e.getKey(), foreign);
797
                            add = false;
-
 
798
                        else
-
 
799
                            assert copyForeigns == ForeignCopyMode.COPY_ID_OR_ROW;
-
 
800
                    }
715
                }
801
                }
-
 
802
                if (add)
-
 
803
                    toAdd.put(fieldName, val);
716
            }
804
            }
717
        }
805
        }
718
        return copy ? toAdd : Collections.unmodifiableMap(toAdd);
806
        return copy ? toAdd : Collections.unmodifiableMap(toAdd);
719
    }
807
    }
720
 
808
 
Line 795... Line 883...
795
    public boolean contains(String fieldName) {
883
    public boolean contains(String fieldName) {
796
        return this.values.containsKey(fieldName);
884
        return this.values.containsKey(fieldName);
797
    }
885
    }
798
 
886
 
799
    @Override
887
    @Override
800
    public final SQLRow asRow() {
888
    public final SQLRow asRow(final Boolean immutable) {
801
        if (!this.hasID())
889
        if (!this.hasID())
802
            throw new IllegalStateException(this + " has no ID");
890
            throw new IllegalStateException(this + " has no ID");
-
 
891
        // keep the most information without throwing an exception
803
        return new SQLRow(this.getTable(), this.getAllValues(ForeignCopyMode.COPY_ID_OR_RM));
892
        final SQLRow res = new SQLRow(this.getTable(), this.getAllValues(ForeignCopyMode.COPY_ID_OR_RM, SQLDefaultCopyMode.PARSE_ELSE_REMOVE, false));
-
 
893
        // if the caller doesn't care about immutable, keep the same
-
 
894
        if (Boolean.TRUE.equals(immutable) || (immutable == null && this.isFrozen()))
-
 
895
            res.freeze();
-
 
896
        return res;
804
    }
897
    }
805
 
898
 
806
    @Override
899
    @Override
807
    public final SQLRowValues asRowValues() {
900
    public final SQLRowValues asRowValues(final Boolean immutable) {
-
 
901
        if (immutable == null || this.isFrozen() == immutable)
808
        return this;
902
            return this;
-
 
903
        else
-
 
904
            return this.getGraph().deepCopy(this, immutable);
809
    }
905
    }
810
 
906
 
811
    // *** set
907
    // *** set
812
 
908
 
813
    /**
909
    /**
814
     * Whether this can be modified.
910
     * Whether this can be modified.
815
     * 
911
     * 
816
     * @return <code>true</code> if this (and its graph) is not modifiable.
912
     * @return <code>true</code> if this (and its graph) is not modifiable.
817
     */
913
     */
-
 
914
    @Override
818
    public final boolean isFrozen() {
915
    public final boolean isFrozen() {
819
        final SQLRowValuesCluster g = this.getGraph(false);
916
        final SQLRowValuesCluster g = this.getGraph(false);
820
        return g != null && g.isFrozen();
917
        return g != null && g.isFrozen();
821
    }
918
    }
822
 
919
 
Line 1008... Line 1105...
1008
    // TODO support java.time.LocalDateTime in Java 8
1105
    // TODO support java.time.LocalDateTime in Java 8
1009
    public static <T, U> U convert(final Class<T> source, final Object value, final Class<U> dest) {
1106
    public static <T, U> U convert(final Class<T> source, final Object value, final Class<U> dest) {
1010
        final ValueConvertor<T, U> conv = ValueConvertorFactory.find(source, dest);
1107
        final ValueConvertor<T, U> conv = ValueConvertorFactory.find(source, dest);
1011
        if (conv == null)
1108
        if (conv == null)
1012
            throw new IllegalArgumentException("No convertor to " + dest + " from " + source);
1109
            throw new IllegalArgumentException("No convertor to " + dest + " from " + source);
1013
        assert source == value.getClass();
-
 
1014
        @SuppressWarnings("unchecked")
-
 
1015
        final T tVal = (T) value;
-
 
1016
        return conv.convert(tVal);
1110
        return conv.convert(source.cast(value));
1017
    }
1111
    }
1018
 
1112
 
1019
    private void _put(String fieldName, Object value, final boolean checkName, final ValueOperation checkValue) {
1113
    private void _put(String fieldName, Object value, final boolean checkName, final ValueOperation checkValue) {
1020
        // table.contains() can take up to 35% of this method
1114
        // table.contains() can take up to 35% of this method
1021
        if (checkName && !this.getTable().contains(fieldName))
1115
        if (checkName && !this.getTable().contains(fieldName))
1022
            throw new IllegalArgumentException(fieldName + " is not in table " + this.getTable());
1116
            throw new IllegalArgumentException(fieldName + " is not in table " + this.getTable());
1023
        if (value == SQL_EMPTY_LINK) {
1117
        if (value == SQL_EMPTY_LINK) {
1024
            // keep getForeignTable since it does the check
1118
            // keep getForeignTable since it does the check
1025
            value = this.getForeignTable(fieldName).getUndefinedIDNumber();
1119
            value = this.getForeignTable(fieldName).getUndefinedIDNumber();
1026
        } else if (value != null && value != SQL_DEFAULT && checkValue != ValueOperation.PASS) {
1120
        } else if (value != null && value != SQL_DEFAULT && !(value instanceof SQLExpression) && checkValue != ValueOperation.PASS) {
1027
            final SQLField field = this.getTable().getField(fieldName);
1121
            final SQLField field = this.getTable().getField(fieldName);
1028
            if (value instanceof SQLRowValues) {
1122
            if (value instanceof SQLRowValues) {
1029
                if (!field.isForeignKey())
1123
                if (!field.isForeignKey())
1030
                    throw new IllegalArgumentException("Since value is a SQLRowValues, expected a foreign key but got " + field);
1124
                    throw new IllegalArgumentException("Since value is a SQLRowValues, expected a foreign key but got " + field);
1031
            } else {
1125
            } else {
1032
                final Class<?> javaType = field.getType().getJavaType();
1126
                final Class<?> javaType = field.getType().getJavaType();
1033
                if (!javaType.isInstance(value)) {
1127
                if (!javaType.isInstance(value)) {
1034
                    if (checkValue == ValueOperation.CONVERT) {
1128
                    if (checkValue == ValueOperation.CONVERT) {
-
 
1129
                        try {
1035
                        value = convert(value.getClass(), value, javaType);
1130
                            value = convert(value.getClass(), value, javaType);
-
 
1131
                        } catch (Exception e) {
-
 
1132
                            throw new IllegalArgumentException("Couldn't convert " + SQLBase.quoteIdentifier(fieldName) + " " + value + getClassName(value.getClass()) + " to " + javaType, e);
-
 
1133
                        }
1036
                    } else {
1134
                    } else {
1037
                        throw new IllegalArgumentException("Wrong type for " + fieldName + ", expected " + javaType + " but got " + value.getClass());
1135
                        throw new IllegalArgumentException("Wrong type for " + fieldName + ", expected " + javaType + " but got " + value.getClass());
1038
                    }
1136
                    }
1039
                }
1137
                }
1040
            }
1138
            }
Line 1060... Line 1158...
1060
    public SQLRowValues putEmptyLink(String fieldName) {
1158
    public SQLRowValues putEmptyLink(String fieldName) {
1061
        return this.put(fieldName, SQL_EMPTY_LINK);
1159
        return this.put(fieldName, SQL_EMPTY_LINK);
1062
    }
1160
    }
1063
 
1161
 
1064
    /**
1162
    /**
-
 
1163
     * Put an SQL expression into this.
-
 
1164
     * 
-
 
1165
     * @param fieldName the field name.
-
 
1166
     * @param sqlExpression an SQL expression, e.g. "now()" or "'a'".
-
 
1167
     * @return this.
-
 
1168
     */
-
 
1169
    public SQLRowValues putSQL(final String fieldName, final String sqlExpression) {
-
 
1170
        return this.put(fieldName, new SQLExpression(sqlExpression));
-
 
1171
    }
-
 
1172
 
-
 
1173
    /**
1065
     * Set a new {@link SQLRowValues} as the value of <code>fieldName</code>. ATTN contrary to many
1174
     * Set a new {@link SQLRowValues} as the value of <code>fieldName</code>. ATTN contrary to many
1066
     * methods this one do not return <code>this</code>.
1175
     * methods this one do not return <code>this</code>.
1067
     * 
1176
     * 
1068
     * @param fieldName the name of a foreign field.
1177
     * @param fieldName the name of a foreign field.
1069
     * @return the newly created values.
1178
     * @return the newly created values.
Line 1081... Line 1190...
1081
    }
1190
    }
1082
 
1191
 
1083
    /**
1192
    /**
1084
     * Create or follow the passed path and put the passed row at the end.
1193
     * Create or follow the passed path and put the passed row at the end.
1085
     * 
1194
     * 
1086
     * @param p the {@link Path#isSingleLink() single link} path.
1195
     * @param p the {@link Path#isSingleLink() single link} path, can only be {@link Path#length()
-
 
1196
     *        empty} if <code>vals</code> is <code>null</code>.
1087
     * @param createPath <code>true</code> if new rows must {@link #createPathToOne(Path) always be
1197
     * @param createPath <code>true</code> if new rows must {@link #createPathToOne(Path) always be
1088
     *        created}, <code>false</code> if existing rows can be {@link #assurePath(Path) used}.
1198
     *        created}, <code>false</code> if existing rows can be {@link #assurePath(Path) used}.
1089
     * @param vals the row to {@link #put(Step, SQLRowValues) put}, <code>null</code> to create a
1199
     * @param vals the row to {@link #put(Step, SQLRowValues) put}, <code>null</code> to create a
1090
     *        new one.
1200
     *        new one.
1091
     * @return the row that was added.
1201
     * @return the row at the end of the path.
1092
     * @throws IllegalArgumentException if the path is invalid.
1202
     * @throws IllegalArgumentException if the path is invalid.
1093
     */
1203
     */
1094
    public final SQLRowValues put(final Path p, final boolean createPath, final SQLRowValues vals) throws IllegalArgumentException {
1204
    public final SQLRowValues put(final Path p, final boolean createPath, final SQLRowValues vals) throws IllegalArgumentException {
1095
        if (p.length() == 0)
1205
        if (p.length() == 0) {
-
 
1206
            if (vals == null) {
-
 
1207
                return this;
-
 
1208
            } else {
1096
            throw new IllegalArgumentException("Empty path");
1209
                throw new IllegalArgumentException("Empty path, won't merge " + vals + " into " + this);
-
 
1210
            }
-
 
1211
        }
1097
        if (!p.isSingleLink())
1212
        if (!p.isSingleLink())
1098
            throw new IllegalArgumentException("Multi-link path " + p);
1213
            throw new IllegalArgumentException("Multi-link path " + p);
1099
        // checks first table
1214
        // checks first table
1100
        final SQLRowValues beforeLast = createPath ? this.createPathToOne(p.minusLast()) : this.assurePath(p.minusLast());
1215
        final SQLRowValues beforeLast = createPath ? this.createPathToOne(p.minusLast()) : this.assurePath(p.minusLast());
1101
        // checks last table
1216
        // checks last table
Line 1275... Line 1390...
1275
 
1390
 
1276
    public final SQLRowValues putAll(Map<String, ?> m) {
1391
    public final SQLRowValues putAll(Map<String, ?> m) {
1277
        return this.putAll(m, null);
1392
        return this.putAll(m, null);
1278
    }
1393
    }
1279
 
1394
 
-
 
1395
    public final SQLRowValues putAllAbsent(Map<String, ?> m) {
-
 
1396
        return this.loadAll(m, FillMode.DONT_OVERWRITE);
-
 
1397
    }
-
 
1398
 
1280
    public final SQLRowValues putAll(Map<String, ?> m, final Collection<String> keys) {
1399
    public final SQLRowValues putAll(Map<String, ?> m, final Collection<String> keys) {
1281
        return this.putAll(m, keys, FillMode.OVERWRITE);
1400
        return this.putAll(m, keys, FillMode.OVERWRITE);
1282
    }
1401
    }
1283
 
1402
 
1284
    final SQLRowValues putAll(Map<String, ?> m, final Collection<String> keys, final FillMode fillMode) {
1403
    final SQLRowValues putAll(Map<String, ?> m, final Collection<String> keys, final FillMode fillMode) {
Line 1837... Line 1956...
1837
                    // otherwise would have to check more than field
1956
                    // otherwise would have to check more than field
1838
                    assert foreignLink.getCols().size() == 1;
1957
                    assert foreignLink.getCols().size() == 1;
1839
                    // verifie l'intégrité (a rowValues is obviously correct, as is EMPTY,
1958
                    // verifie l'intégrité (a rowValues is obviously correct, as is EMPTY,
1840
                    // DEFAULT is the responsability of the DB)
1959
                    // DEFAULT is the responsability of the DB)
1841
                    final Object fieldVal = this.getObject(fieldName);
1960
                    final Object fieldVal = this.getObject(fieldName);
-
 
1961
                    if (fieldVal instanceof SQLExpression)
-
 
1962
                        return new Object[] { fieldName, null };
1842
                    if (fieldVal != null && fieldVal != SQL_DEFAULT && !(fieldVal instanceof SQLRowValues)) {
1963
                    if (fieldVal != null && fieldVal != SQL_DEFAULT && !(fieldVal instanceof SQLRowValues)) {
1843
                        final SQLRow pb = foreignTable.checkValidity(((Number) fieldVal).intValue());
1964
                        final SQLRow pb = foreignTable.checkValidity(((Number) fieldVal).intValue());
1844
                        if (pb != null)
1965
                        if (pb != null)
1845
                            return new Object[] { fieldName, pb };
1966
                            return new Object[] { fieldName, pb };
1846
                    }
1967
                    }
Line 2028... Line 2149...
2028
    @Override
2149
    @Override
2029
    public String mapToString() {
2150
    public String mapToString() {
2030
        String result = this.getClass().getSimpleName() + " on " + this.getTable() + " : {";
2151
        String result = this.getClass().getSimpleName() + " on " + this.getTable() + " : {";
2031
        result += CollectionUtils.join(this.values.entrySet(), ", ", new ITransformer<Entry<String, ?>, String>() {
2152
        result += CollectionUtils.join(this.values.entrySet(), ", ", new ITransformer<Entry<String, ?>, String>() {
2032
            public String transformChecked(final Entry<String, ?> e) {
2153
            public String transformChecked(final Entry<String, ?> e) {
-
 
2154
                final Object fieldVal = e.getValue();
2033
                final String className = e.getValue() == null ? "" : "(" + e.getValue().getClass() + ")";
2155
                final String className = getClassName(fieldVal);
2034
                final String value;
2156
                final String value;
2035
                // avoid infinite loop (and overly verbose string)
2157
                // avoid infinite loop (and overly verbose string)
2036
                if (e.getValue() instanceof SQLRowValues) {
2158
                if (fieldVal instanceof SQLRowValues) {
2037
                    final SQLRowValues foreignVals = (SQLRowValues) e.getValue();
2159
                    final SQLRowValues foreignVals = (SQLRowValues) fieldVal;
2038
                    if (foreignVals == SQLRowValues.this) {
2160
                    if (foreignVals == SQLRowValues.this) {
2039
                        value = "this";
2161
                        value = "this";
2040
                    } else if (foreignVals.hasID()) {
2162
                    } else if (foreignVals.hasID()) {
2041
                        value = foreignVals.getIDNumber().toString();
2163
                        value = foreignVals.getIDNumber().toString();
2042
                    } else {
2164
                    } else {
2043
                        // so that if the same vals is referenced multiple times, we can see it
2165
                        // so that if the same vals is referenced multiple times, we can see it
2044
                        value = "@" + System.identityHashCode(foreignVals);
2166
                        value = "@" + System.identityHashCode(foreignVals);
2045
                    }
2167
                    }
2046
                } else
2168
                } else {
2047
                    value = String.valueOf(e.getValue());
2169
                    value = String.valueOf(fieldVal);
-
 
2170
                }
2048
                return e.getKey() + "=" + value + className;
2171
                return e.getKey() + "=" + value + className;
2049
            }
2172
            }
2050
        });
2173
        });
2051
        result += "}";
2174
        result += "}";
2052
        return result;
2175
        return result;
2053
    }
2176
    }
2054
 
2177
 
-
 
2178
    protected String getClassName(final Object val) {
-
 
2179
        // SQL_DEFAULT already has our class name in toString()
-
 
2180
        return val == null || val == SQL_DEFAULT || val instanceof SQLExpression ? "" : "(" + val.getClass() + ")";
-
 
2181
    }
-
 
2182
 
2055
    /**
2183
    /**
2056
     * Return a graphical representation (akin to the result of a query) of the tree rooted at
2184
     * Return a graphical representation (akin to the result of a query) of the tree rooted at
2057
     * <code>this</code>.
2185
     * <code>this</code>.
2058
     * 
2186
     * 
2059
     * @return a string representing the rows pointing to this.
2187
     * @return a string representing the rows pointing to this.
Line 2170... Line 2298...
2170
            if (!this.values.keySet().equals(o.values.keySet()))
2298
            if (!this.values.keySet().equals(o.values.keySet()))
2171
                return false;
2299
                return false;
2172
        }
2300
        }
2173
        // fields are already checked so if IDs are not wanted, just omit foreign rows
2301
        // fields are already checked so if IDs are not wanted, just omit foreign rows
2174
        final ForeignCopyMode copyMode = useForeignID ? ForeignCopyMode.COPY_ID_OR_RM : ForeignCopyMode.NO_COPY;
2302
        final ForeignCopyMode copyMode = useForeignID ? ForeignCopyMode.COPY_ID_OR_RM : ForeignCopyMode.NO_COPY;
2175
        final Map<String, Object> thisVals = this.getAllValues(copyMode, !usePK);
2303
        final Map<String, Object> thisVals = this.getAllValues(copyMode, SQLDefaultCopyMode.COPY, !usePK);
2176
        final Map<String, Object> oVals = o.getAllValues(copyMode, !usePK);
2304
        final Map<String, Object> oVals = o.getAllValues(copyMode, SQLDefaultCopyMode.COPY, !usePK);
2177
        if (!usePK) {
2305
        if (!usePK) {
2178
            final List<String> pk = this.getTable().getPKsNames();
2306
            final List<String> pk = this.getTable().getPKsNames();
2179
            thisVals.keySet().removeAll(pk);
2307
            thisVals.keySet().removeAll(pk);
2180
            oVals.keySet().removeAll(pk);
2308
            oVals.keySet().removeAll(pk);
2181
        }
2309
        }
Line 2241... Line 2369...
2241
        final PreparedStatement pStmt;
2369
        final PreparedStatement pStmt;
2242
        final String tableQuoted = table.getSQLName().quote();
2370
        final String tableQuoted = table.getSQLName().quote();
2243
        String req = (insert ? "INSERT INTO " : "UPDATE ") + tableQuoted + " ";
2371
        String req = (insert ? "INSERT INTO " : "UPDATE ") + tableQuoted + " ";
2244
        if (insert) {
2372
        if (insert) {
2245
            assert fieldsNames.size() == values.size();
2373
            assert fieldsNames.size() == values.size();
-
 
2374
            final List<String> insertValues = new ArrayList<>(values.size());
2246
            // remove DEFAULT since they are useless and prevent us from using
2375
            // remove DEFAULT since they are useless and prevent us from using
2247
            // INSERT INTO "TABLEAU_ELECTRIQUE" ("ID_OBSERVATION", ...) select DEFAULT, ?,
2376
            // INSERT INTO "TABLEAU_ELECTRIQUE" ("ID_OBSERVATION", ...) select DEFAULT, ?,
2248
            // MAX("ORDRE") + 1 FROM "TABLEAU_ELECTRIQUE"
2377
            // MAX("ORDRE") + 1 FROM "TABLEAU_ELECTRIQUE"
2249
            for (int i = values.size() - 1; i >= 0; i--) {
2378
            for (int i = values.size() - 1; i >= 0; i--) {
2250
                if (values.get(i) == SQL_DEFAULT) {
2379
                if (values.get(i) == SQL_DEFAULT) {
2251
                    fieldsNames.remove(i);
2380
                    fieldsNames.remove(i);
2252
                    values.remove(i);
2381
                    values.remove(i);
-
 
2382
                } else {
-
 
2383
                    insertValues.add(getFieldValue(values.get(i)));
2253
                }
2384
                }
2254
            }
2385
            }
2255
            assert fieldsNames.size() == values.size();
2386
            assert fieldsNames.size() == values.size();
2256
 
2387
 
2257
            // ajout de l'ordre
2388
            // ajout de l'ordre
2258
            final SQLField order = table.getOrderField();
2389
            final SQLField orderF = table.getOrderField();
2259
            final boolean selectOrder;
2390
            final boolean selectOrder;
2260
            if (order != null && !fieldsNames.contains(order.getName())) {
2391
            if (orderF != null && !fieldsNames.contains(orderF.getName())) {
2261
                // si l'ordre n'est pas spécifié, ajout à la fin
2392
                // si l'ordre n'est pas spécifié, ajout à la fin
2262
                fieldsNames.add(order.getName());
2393
                fieldsNames.add(orderF.getName());
2263
                selectOrder = true;
2394
                selectOrder = true;
2264
            } else {
2395
            } else {
2265
                selectOrder = false;
2396
                selectOrder = false;
2266
            }
2397
            }
2267
 
2398
 
Line 2272... Line 2403...
2272
                req += "(" + CollectionUtils.join(fieldsNames, ", ", new ITransformer<String, String>() {
2403
                req += "(" + CollectionUtils.join(fieldsNames, ", ", new ITransformer<String, String>() {
2273
                    public String transformChecked(String input) {
2404
                    public String transformChecked(String input) {
2274
                        return SQLBase.quoteIdentifier(input);
2405
                        return SQLBase.quoteIdentifier(input);
2275
                    }
2406
                    }
2276
                }) + ")";
2407
                }) + ")";
2277
                // no DEFAULT thus only ?
-
 
2278
                final String questionMarks = CollectionUtils.join(Collections.nCopies(values.size(), "?"), ", ");
2408
                final String questionMarks = CollectionUtils.join(insertValues, ", ");
2279
                if (selectOrder) {
2409
                if (selectOrder) {
2280
                    // needed since VALUES ( (select MAX("ORDRE") from "LOCAL") ) on MySQL yield
2410
                    // needed since VALUES ( (select MAX("ORDRE") from "LOCAL") ) on MySQL yield
2281
                    // "You can't specify target table 'LOCAL' for update in FROM clause"
2411
                    // "You can't specify target table 'LOCAL' for update in FROM clause"
2282
                    req += " select ";
2412
                    req += " select ";
2283
                    req += questionMarks;
2413
                    req += questionMarks;
2284
                    if (values.size() > 0)
2414
                    if (values.size() > 0)
2285
                        req += ", ";
2415
                        req += ", ";
2286
                    // COALESCE for empty tables, MIN_ORDER + 1 since MIN_ORDER cannot be moved
2416
                    // COALESCE for empty tables, MIN_ORDER + 1 since MIN_ORDER cannot be moved
2287
                    req += "COALESCE(MAX(" + SQLBase.quoteIdentifier(order.getName()) + "), " + ReOrder.MIN_ORDER + ") + 1 FROM " + tableQuoted;
2417
                    req += "COALESCE(MAX(" + SQLBase.quoteIdentifier(orderF.getName()) + "), " + ReOrder.MIN_ORDER + ") + 1 FROM " + tableQuoted;
2288
                } else {
2418
                } else {
2289
                    req += " VALUES (";
2419
                    req += " VALUES (";
2290
                    req += questionMarks;
2420
                    req += questionMarks;
2291
                    req += ")";
2421
                    req += ")";
2292
                }
2422
                }
Line 2312... Line 2442...
2312
        }
2442
        }
2313
        // set fields values
2443
        // set fields values
2314
        int i = 0;
2444
        int i = 0;
2315
        for (final Object value : values) {
2445
        for (final Object value : values) {
2316
            // nothing to set if there's no corresponding '?'
2446
            // nothing to set if there's no corresponding '?'
2317
            if (value != SQL_DEFAULT) {
2447
            if (value != SQL_DEFAULT && !(value instanceof SQLExpression)) {
2318
                final Object toIns;
2448
                final Object toIns;
2319
                if (value instanceof SQLRowValues) {
2449
                if (value instanceof SQLRowValues) {
2320
                    // TODO if we already point to some row, archive it
2450
                    // TODO if we already point to some row, archive it
2321
                    toIns = ((SQLRowValues) value).insert().getIDNumber();
2451
                    toIns = ((SQLRowValues) value).insert().getIDNumber();
2322
                } else
2452
                } else
Line 2328... Line 2458...
2328
        }
2458
        }
2329
        return Tuple2.create(pStmt, fieldsNames);
2459
        return Tuple2.create(pStmt, fieldsNames);
2330
    }
2460
    }
2331
 
2461
 
2332
    private static String getFieldValue(final Object value) {
2462
    private static String getFieldValue(final Object value) {
2333
        return value == SQL_DEFAULT ? "DEFAULT" : "?";
2463
        if (value == SQL_DEFAULT)
-
 
2464
            return "DEFAULT";
-
 
2465
        else if (value instanceof SQLExpression)
-
 
2466
            return ((SQLExpression) value).getSQL();
-
 
2467
        else
-
 
2468
            return "?";
2334
    }
2469
    }
2335
 
2470
 
2336
    @Override
2471
    @Override
2337
    public SQLTableModifiedListener createTableListener(SQLDataListener l) {
2472
    public SQLTableModifiedListener createTableListener(SQLDataListener l) {
2338
        return new SQLTableListenerData<SQLRowValues>(this, l);
2473
        return new SQLTableListenerData<SQLRowValues>(this, l);
Line 2519... Line 2654...
2519
    }
2654
    }
2520
 
2655
 
2521
    public static final SQLRowValues trim(final SQLRowValues r) {
2656
    public static final SQLRowValues trim(final SQLRowValues r) {
2522
        return new SQLRowValues(r, ForeignCopyMode.COPY_ID_OR_RM);
2657
        return new SQLRowValues(r, ForeignCopyMode.COPY_ID_OR_RM);
2523
    }
2658
    }
-
 
2659
 
-
 
2660
    public static final List<SQLRowValues> toImmutableList(final Collection<SQLRowValues> rows) {
-
 
2661
        return toImmutableList(rows, new ArrayList<>(rows.size()));
-
 
2662
    }
-
 
2663
 
-
 
2664
    public static final List<SQLRowValues> toImmutableList(final Collection<SQLRowValues> rows, final List<SQLRowValues> res) {
-
 
2665
        for (final SQLRowValues v : rows)
-
 
2666
            res.add(v.toImmutable());
-
 
2667
        return Collections.unmodifiableList(res);
-
 
2668
    }
2524
}
2669
}