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 |
}
|