in core/src/main/java/com/jetbrains/youtrackdb/internal/core/record/impl/EntityImpl.java [1579:1857]
private static void validateProperty(
DatabaseSessionInternal session, ImmutableSchema schema, EntityImpl iRecord,
ImmutableSchemaProperty p)
throws ValidationException {
iRecord.checkForBinding();
final Object propertyValue;
var entry = iRecord.properties.get(p.getName());
if (entry != null && entry.exists()) {
// AVOID CONVERSIONS: FASTER!
propertyValue = entry.value;
if (p.isNotNull() && propertyValue == null)
// NULLITY
{
throw new ValidationException(session.getDatabaseName(),
"The property '" + p.getFullName() + "' cannot be null, record: " + iRecord);
}
if (propertyValue != null && p.getRegexp() != null && p.getType() == PropertyType.STRING) {
// REGEXP
if (!((String) propertyValue).matches(p.getRegexp())) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' does not match the regular expression '"
+ p.getRegexp()
+ "'. Field value is: "
+ propertyValue
+ ", record: "
+ iRecord);
}
}
} else {
if (p.isMandatory()) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' is mandatory, but not found on record: "
+ iRecord);
}
propertyValue = null;
}
final var type = p.getType();
if (propertyValue != null && type != null) {
// CHECK TYPE
switch (type) {
case LINK:
validateLink(schema, session, p, propertyValue, false);
break;
case LINKLIST:
if (!(propertyValue instanceof EntityLinkListImpl)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' has been declared as LINKLIST but an incompatible type is used. Value: "
+ propertyValue);
}
validateLinkCollection(session, schema, p, (Collection<Object>) propertyValue, entry);
break;
case LINKSET:
if (!(propertyValue instanceof EntityLinkSetImpl)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' has been declared as LINKSET but an incompatible type is used. Value: "
+ propertyValue);
}
validateLinkCollection(session, schema, p, (Collection<Object>) propertyValue, entry);
break;
case LINKMAP:
if (!(propertyValue instanceof EntityLinkMapIml)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' has been declared as LINKMAP but an incompatible type is used. Value: "
+ propertyValue);
}
validateLinkCollection(session, schema, p, ((Map<?, Object>) propertyValue).values(),
entry);
break;
case LINKBAG:
if (!(propertyValue instanceof LinkBag)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' has been declared as LINKBAG but an incompatible type is used. Value: "
+ propertyValue);
}
validateLinkCollection(session, schema, p, (Iterable<Object>) propertyValue, entry);
break;
case EMBEDDED:
validateEmbedded(session, p, propertyValue);
break;
case EMBEDDEDLIST:
if (!(propertyValue instanceof EntityEmbeddedListImpl<?>)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' has been declared as EMBEDDEDLIST but an incompatible type is used. Value:"
+ " "
+ propertyValue);
}
if (p.getLinkedClass() != null) {
for (var item : ((List<?>) propertyValue)) {
validateEmbedded(session, p, item);
}
} else {
if (p.getLinkedType() != null) {
for (var item : ((List<?>) propertyValue)) {
validateType(session, p, item);
}
}
}
break;
case EMBEDDEDSET:
if (!(propertyValue instanceof EntityEmbeddedSetImpl<?>)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' has been declared as EMBEDDEDSET but an incompatible type is used. Value: "
+ propertyValue);
}
if (p.getLinkedClass() != null) {
for (var item : ((Set<?>) propertyValue)) {
validateEmbedded(session, p, item);
}
} else {
if (p.getLinkedType() != null) {
for (var item : ((Set<?>) propertyValue)) {
validateType(session, p, item);
}
}
}
break;
case EMBEDDEDMAP:
if (!(propertyValue instanceof EntityEmbeddedMapImpl<?>)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' has been declared as EMBEDDEDMAP but an incompatible type is used. Value: "
+ propertyValue);
}
if (p.getLinkedClass() != null) {
for (var colleEntry : ((Map<?, ?>) propertyValue).entrySet()) {
validateEmbedded(session, p, colleEntry.getValue());
}
} else {
if (p.getLinkedType() != null) {
for (var collEntry : ((Map<?, ?>) propertyValue).entrySet()) {
validateType(session, p, collEntry.getValue());
}
}
}
break;
}
}
if (p.getMin() != null && propertyValue != null) {
// MIN
final var min = p.getMin();
if (p.getMinComparable().compareTo(propertyValue) > 0) {
switch (p.getType()) {
case STRING:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains fewer characters than "
+ min
+ " requested");
case DATE:
case DATETIME:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains the date "
+ propertyValue
+ " which precedes the first acceptable date ("
+ min
+ ")");
case BINARY:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains fewer bytes than "
+ min
+ " requested");
case EMBEDDEDLIST:
case EMBEDDEDSET:
case LINKLIST:
case LINKSET:
case EMBEDDEDMAP:
case LINKMAP:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains fewer items than "
+ min
+ " requested");
default:
throw new ValidationException(session.getDatabaseName(),
"The property '" + p.getFullName() + "' is less than " + min);
}
}
}
if (p.getMaxComparable() != null && propertyValue != null) {
final var max = p.getMax();
if (p.getMaxComparable().compareTo(propertyValue) < 0) {
switch (p.getType()) {
case STRING:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains more characters than "
+ max
+ " requested");
case DATE:
case DATETIME:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains the date "
+ propertyValue
+ " which is after the last acceptable date ("
+ max
+ ")");
case BINARY:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains more bytes than "
+ max
+ " requested");
case EMBEDDEDLIST:
case EMBEDDEDSET:
case LINKLIST:
case LINKSET:
case EMBEDDEDMAP:
case LINKMAP:
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' contains more items than "
+ max
+ " requested");
default:
throw new ValidationException(session.getDatabaseName(),
"The property '" + p.getFullName() + "' is greater than " + max);
}
}
}
if (p.isReadonly()) {
if (entry != null
&& (entry.isTxChanged() || entry.isTxTrackedModified())
&& !entry.isTxCreated()) {
// check if the property is actually changed by equal.
// this is due to a limitation in the merge algorithm used server side marking all
// non-simple properties as dirty
var orgVal = entry.getOnLoadValue(session);
var simple =
propertyValue != null ? PropertyTypeInternal.isSimpleValueType(propertyValue)
: PropertyTypeInternal.isSimpleValueType(orgVal);
if (simple || propertyValue != null && orgVal == null || propertyValue == null
|| !Objects.deepEquals(propertyValue, orgVal)) {
throw new ValidationException(session.getDatabaseName(),
"The property '"
+ p.getFullName()
+ "' is immutable and cannot be altered. Field value is: "
+ entry.value);
}
}
}
}