diff --git a/src/main/java/org/caosdb/datetime/FragmentDateTime.java b/src/main/java/org/caosdb/datetime/FragmentDateTime.java index 52faf5f2040de56c05bb7ce4b5aee130fadf67c3..a14192fcf30697808c19df13700ab34b53b87a17 100644 --- a/src/main/java/org/caosdb/datetime/FragmentDateTime.java +++ b/src/main/java/org/caosdb/datetime/FragmentDateTime.java @@ -22,7 +22,9 @@ */ package org.caosdb.datetime; +import java.util.Objects; import java.util.TimeZone; +import org.caosdb.server.datatype.Value; public abstract class FragmentDateTime implements DateTimeInterface { @@ -53,4 +55,13 @@ public abstract class FragmentDateTime implements DateTimeInterface { this.nanosecond = nanosecond; this.timeZone = tz; } + + @Override + public boolean equals(Value val) { + if (val instanceof FragmentDateTime) { + FragmentDateTime that = (FragmentDateTime) val; + return Objects.equals(that.toDatabaseString(), this.toDatabaseString()); + } + return false; + } } diff --git a/src/main/java/org/caosdb/datetime/UTCDateTime.java b/src/main/java/org/caosdb/datetime/UTCDateTime.java index 0ecb3ac60dfe796692c344e590f3935c36fceac3..765eb3f924b9d7d0bb26fd92b71c4513e6561f63 100644 --- a/src/main/java/org/caosdb/datetime/UTCDateTime.java +++ b/src/main/java/org/caosdb/datetime/UTCDateTime.java @@ -25,8 +25,10 @@ package org.caosdb.datetime; import java.util.ArrayList; import java.util.Calendar; import java.util.GregorianCalendar; +import java.util.Objects; import java.util.TimeZone; import org.caosdb.server.datatype.AbstractDatatype.Table; +import org.caosdb.server.datatype.Value; import org.jdom2.Element; public class UTCDateTime implements Interval { @@ -360,4 +362,13 @@ public class UTCDateTime implements Interval { public boolean hasNanoseconds() { return this.nanoseconds != null; } + + @Override + public boolean equals(Value val) { + if (val instanceof UTCDateTime) { + UTCDateTime that = (UTCDateTime) val; + return Objects.equals(that.toDatabaseString(), this.toDatabaseString()); + } + return false; + } } diff --git a/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java b/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java index 06216c822c705fa6fbaef949386966af15ff6b13..ac93629c5e8f3189975a8c1b93f9090fdc80edbe 100644 --- a/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java +++ b/src/main/java/org/caosdb/server/datatype/AbstractEnumValue.java @@ -52,7 +52,15 @@ public abstract class AbstractEnumValue implements SingleValue { @Override public boolean equals(final Object obj) { if (obj instanceof AbstractEnumValue) { - final AbstractEnumValue that = (AbstractEnumValue) obj; + return equals((AbstractEnumValue) obj); + } + return false; + } + + @Override + public boolean equals(Value val) { + if (val instanceof AbstractEnumValue) { + final AbstractEnumValue that = (AbstractEnumValue) val; return Objects.equal(that.value, this.value); } return false; diff --git a/src/main/java/org/caosdb/server/datatype/CollectionValue.java b/src/main/java/org/caosdb/server/datatype/CollectionValue.java index 55cbf0489c3f965e19784c80439d291930d89841..2a55b47feab814ab5633c5e65ebb8b2c2afcfe1a 100644 --- a/src/main/java/org/caosdb/server/datatype/CollectionValue.java +++ b/src/main/java/org/caosdb/server/datatype/CollectionValue.java @@ -80,4 +80,23 @@ public class CollectionValue implements Value, Iterable<IndexedSingleValue> { public int size() { return list.size(); } + + @Override + public boolean equals(Value val) { + if (val instanceof CollectionValue) { + CollectionValue that = (CollectionValue) val; + sort(); + that.sort(); + return this.list.equals(that.list); + } + return false; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Value) { + return this.equals((Value) obj); + } + return false; + } } diff --git a/src/main/java/org/caosdb/server/datatype/GenericValue.java b/src/main/java/org/caosdb/server/datatype/GenericValue.java index 02d735abcc36297e61b08f99455f077d832a3a4b..a4b1b0b1508b9debde1f34f1dd58786bdae840c8 100644 --- a/src/main/java/org/caosdb/server/datatype/GenericValue.java +++ b/src/main/java/org/caosdb/server/datatype/GenericValue.java @@ -89,8 +89,16 @@ public class GenericValue implements SingleValue { @Override public boolean equals(final Object obj) { - if (obj instanceof GenericValue) { - final GenericValue that = (GenericValue) obj; + if (obj instanceof Value) { + return equals((Value) obj); + } + return false; + } + + @Override + public boolean equals(Value val) { + if (val instanceof GenericValue) { + final GenericValue that = (GenericValue) val; return Objects.equal(that.value, this.value); } return false; diff --git a/src/main/java/org/caosdb/server/datatype/IndexedSingleValue.java b/src/main/java/org/caosdb/server/datatype/IndexedSingleValue.java index a9ed584b3ee858e6de2bbb84ad4a43da820cedb0..ec9280c3ab0cbdf6e577feec4d40a01f6402dbfe 100644 --- a/src/main/java/org/caosdb/server/datatype/IndexedSingleValue.java +++ b/src/main/java/org/caosdb/server/datatype/IndexedSingleValue.java @@ -22,6 +22,7 @@ */ package org.caosdb.server.datatype; +import java.util.Objects; import org.caosdb.server.datatype.AbstractDatatype.Table; import org.jdom2.Element; @@ -78,4 +79,21 @@ public class IndexedSingleValue implements SingleValue, Comparable<IndexedSingle public SingleValue getWrapped() { return this.wrapped; } + + @Override + public boolean equals(Value val) { + if (val instanceof IndexedSingleValue) { + IndexedSingleValue that = (IndexedSingleValue) val; + return Objects.equals(that.wrapped, this.wrapped); + } + return false; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof Value) { + return this.equals((Value) obj); + } + return false; + } } diff --git a/src/main/java/org/caosdb/server/datatype/ReferenceValue.java b/src/main/java/org/caosdb/server/datatype/ReferenceValue.java index 89601b50a46cbf838a995a222b0d4c43150f8a19..40b05e48d10db963268541ccc3efeec6e98e490c 100644 --- a/src/main/java/org/caosdb/server/datatype/ReferenceValue.java +++ b/src/main/java/org/caosdb/server/datatype/ReferenceValue.java @@ -199,13 +199,21 @@ public class ReferenceValue implements SingleValue { @Override public boolean equals(final Object obj) { - if (obj instanceof ReferenceValue) { - final ReferenceValue that = (ReferenceValue) obj; + if (obj instanceof Value) { + return equals((Value) obj); + } + return false; + } + + @Override + public boolean equals(Value val) { + if (val instanceof ReferenceValue) { + final ReferenceValue that = (ReferenceValue) val; if (that.getId() != null && getId() != null) { return that.getId().equals(getId()) && Objects.deepEquals(that.getVersion(), this.getVersion()); } else if (that.getName() != null && getName() != null) { - return that.getName().equals(getName()); + return Objects.equals(that.getName(), this.getName()); } } return false; diff --git a/src/main/java/org/caosdb/server/datatype/Value.java b/src/main/java/org/caosdb/server/datatype/Value.java index 540e6bc52b72744c4d7c1c651c40924d1da57c6d..a2612fb37dae11953b6083f3be8132b2dd61f77f 100644 --- a/src/main/java/org/caosdb/server/datatype/Value.java +++ b/src/main/java/org/caosdb/server/datatype/Value.java @@ -26,4 +26,6 @@ import org.jdom2.Element; public interface Value { public void addToElement(Element e); + + public abstract boolean equals(Value val); } diff --git a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java index ee9aa81e49c99b15ac30a8164a4b9afc18b666f2..334e408a82ddc343ed634979dc76e0b11f33a94b 100644 --- a/src/main/java/org/caosdb/server/transaction/WriteTransaction.java +++ b/src/main/java/org/caosdb/server/transaction/WriteTransaction.java @@ -41,6 +41,7 @@ import org.caosdb.server.entity.DeleteEntity; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.FileProperties; import org.caosdb.server.entity.InsertEntity; +import org.caosdb.server.entity.Message; import org.caosdb.server.entity.RetrieveEntity; import org.caosdb.server.entity.UpdateEntity; import org.caosdb.server.entity.container.TransactionContainer; @@ -400,7 +401,8 @@ public class WriteTransaction extends Transaction<WritableContainer> } // entity role - if (newEntity.hasRole() + if (!(newEntity instanceof Property && oldEntity instanceof Property) + && newEntity.hasRole() && oldEntity.hasRole() && !newEntity.getRole().equals(oldEntity.getRole()) || newEntity.hasRole() ^ oldEntity.hasRole()) { @@ -409,10 +411,18 @@ public class WriteTransaction extends Transaction<WritableContainer> } // entity value - if (newEntity.hasValue() - && oldEntity.hasValue() - && !newEntity.getValue().equals(oldEntity.getValue()) - || newEntity.hasValue() ^ oldEntity.hasValue()) { + if (newEntity.hasValue() && oldEntity.hasValue()) { + try { + newEntity.parseValue(); + oldEntity.parseValue(); + } catch (Message m) { + // ignore, parsing is handled elsewhere + } + if (!newEntity.getValue().equals(oldEntity.getValue())) { + needPermissions.add(EntityPermission.UPDATE_VALUE); + updatetable = true; + } + } else if (newEntity.hasValue() ^ oldEntity.hasValue()) { needPermissions.add(EntityPermission.UPDATE_VALUE); updatetable = true; } diff --git a/src/test/java/org/caosdb/server/transaction/UpdateTest.java b/src/test/java/org/caosdb/server/transaction/UpdateTest.java index fb121c260a7706e806ddc73e9005ea4a440f1510..36cce88a8b4514f5ec95bac9263cb58da5857b65 100644 --- a/src/test/java/org/caosdb/server/transaction/UpdateTest.java +++ b/src/test/java/org/caosdb/server/transaction/UpdateTest.java @@ -25,16 +25,22 @@ package org.caosdb.server.transaction; import static org.caosdb.server.utils.EntityStatus.QUALIFIED; import static org.caosdb.server.utils.EntityStatus.VALID; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import java.io.IOException; import java.security.NoSuchAlgorithmException; +import java.util.HashSet; import org.caosdb.server.CaosDBException; import org.caosdb.server.CaosDBServer; +import org.caosdb.server.datatype.CollectionValue; import org.caosdb.server.datatype.GenericValue; +import org.caosdb.server.datatype.ReferenceValue; import org.caosdb.server.entity.Entity; import org.caosdb.server.entity.EntityInterface; import org.caosdb.server.entity.StatementStatus; import org.caosdb.server.entity.wrapper.Property; +import org.caosdb.server.permissions.EntityPermission; +import org.caosdb.server.permissions.Permission; import org.caosdb.server.utils.EntityStatus; import org.junit.BeforeClass; import org.junit.Test; @@ -176,4 +182,78 @@ public class UpdateTest { assertEquals(newEntity.getEntityStatus(), QUALIFIED); assertEquals(newProperty.getEntityStatus(), QUALIFIED); } + + @Test + public void testDeriveUpdate_Collections() + throws NoSuchAlgorithmException, CaosDBException, IOException { + + final Entity newEntity = new Entity(); + final Property newProperty = new Property(1); + newProperty.setDatatype("List<Person>"); + CollectionValue newValue = new CollectionValue(); + newValue.add(new ReferenceValue(1234)); + newValue.add(null); + newValue.add(new GenericValue(2345)); + newValue.add(new GenericValue(3465)); + newProperty.setValue(newValue); + newEntity.addProperty(newProperty); + newEntity.setEntityStatus(QUALIFIED); + newProperty.setEntityStatus(QUALIFIED); + + // old entity represents the stored entity. + final Entity oldEntity = new Entity(); + final Property oldProperty = new Property(1); + oldProperty.setDatatype("List<Person>"); + CollectionValue oldValue = new CollectionValue(); + // Values are shuffled but have the correct index + oldValue.add(1, null); + oldValue.add(3, new GenericValue(3465)); + oldValue.add(2, new ReferenceValue(2345)); + oldValue.add(0, new ReferenceValue(1234)); + oldProperty.setValue(oldValue); + oldEntity.addProperty(oldProperty); + + HashSet<Permission> permissions = new WriteTransaction(null).deriveUpdate(newEntity, oldEntity); + // Both have been identified as equals + assertTrue(permissions.isEmpty()); + assertEquals(newEntity.getEntityStatus(), VALID); + assertEquals(newProperty.getEntityStatus(), VALID); + + // NEW TEST CASE + newValue.add(null); // newValue has another null + newProperty.setValue(newValue); + oldEntity.addProperty(oldProperty); // Add again, because deriveUpdate throws it away + newEntity.setEntityStatus(QUALIFIED); + newProperty.setEntityStatus(QUALIFIED); + + HashSet<Permission> permissions2 = + new WriteTransaction(null).deriveUpdate(newEntity, oldEntity); + HashSet<Permission> expected = new HashSet<Permission>(); + expected.add(EntityPermission.UPDATE_ADD_PROPERTY); + expected.add(EntityPermission.UPDATE_REMOVE_PROPERTY); + assertEquals(expected, permissions2); + assertEquals(newEntity.getEntityStatus(), QUALIFIED); + assertEquals(newProperty.getEntityStatus(), QUALIFIED); + + // NEW TEST CASE + // now change the order of oldValue + CollectionValue oldValue2 = new CollectionValue(); + // Values are shuffled but have the correct index + oldValue2.add(0, null); + oldValue2.add(1, new GenericValue(3465)); + oldValue2.add(2, new ReferenceValue(2345)); + oldValue2.add(3, new ReferenceValue(1234)); + oldValue2.add(4, null); + oldProperty.setValue(oldValue2); + + oldEntity.addProperty(oldProperty); // Add again, because deriveUpdate throws it away + newEntity.setEntityStatus(QUALIFIED); + newProperty.setEntityStatus(QUALIFIED); + + HashSet<Permission> permissions3 = + new WriteTransaction(null).deriveUpdate(newEntity, oldEntity); + assertEquals(expected, permissions3); + assertEquals(newEntity.getEntityStatus(), QUALIFIED); + assertEquals(newProperty.getEntityStatus(), QUALIFIED); + } }