From c8dd12bece63531b30dbc7a65c3ac2d4ad9a6bcd Mon Sep 17 00:00:00 2001
From: alaskowski <alaskowski@ethz.ch>
Date: Wed, 20 Sep 2023 16:47:44 +0200
Subject: [PATCH] SSDM-55: Extracted IPropertiesHolder methods to an abstract
 AbstractEntity class, Created AbstractEntityFetchOptions

---
 .../v3/dto/common/entity/AbstractEntity.java  | 320 +++++++++++++++
 .../AbstractEntityFetchOptions.java           |  52 +++
 .../generic/asapi/v3/dto/dataset/DataSet.java | 271 +------------
 .../fetchoptions/DataSetFetchOptions.java     |  28 +-
 .../asapi/v3/dto/experiment/Experiment.java   | 322 ++-------------
 .../fetchoptions/ExperimentFetchOptions.java  |  29 +-
 .../asapi/v3/dto/material/Material.java       | 271 +------------
 .../fetchoptions/MaterialFetchOptions.java    |  27 +-
 .../generic/asapi/v3/dto/sample/Sample.java   | 367 ++----------------
 .../fetchoptions/SampleFetchOptions.java      |  27 +-
 10 files changed, 450 insertions(+), 1264 deletions(-)
 create mode 100644 api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntity.java
 create mode 100644 api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/AbstractEntityFetchOptions.java

diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntity.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntity.java
new file mode 100644
index 00000000000..3278bdda7f6
--- /dev/null
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/entity/AbstractEntity.java
@@ -0,0 +1,320 @@
+/*
+ *  Copyright ETH 2023 Zürich, Scientific IT Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.entity;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.AbstractEntityFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.ObjectPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.PropertiesDeserializer;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.id.SamplePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
+
+import java.io.Serializable;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+@JsonObject("as.dto.common.entity.AbstractEntity")
+public abstract class AbstractEntity<OBJECT> implements Serializable, IPropertiesHolder
+{
+
+    @JsonProperty
+    private AbstractEntityFetchOptions<OBJECT> fetchOptions;
+
+    @JsonProperty
+    @JsonDeserialize(contentUsing = PropertiesDeserializer.class)
+    protected Map<String, Serializable> properties;
+
+
+    @JsonIgnore
+    protected AbstractEntityFetchOptions<OBJECT> getFetchOptions()
+    {
+        return fetchOptions;
+    }
+
+    @JsonIgnore
+    protected void setFetchOptions(AbstractEntityFetchOptions<OBJECT> fetchOptions)
+    {
+        this.fetchOptions = fetchOptions;
+    }
+
+    @JsonIgnore
+    @Override
+    public Map<String, Serializable> getProperties()
+    {
+        if (fetchOptions != null && fetchOptions.hasProperties())
+        {
+            return properties;
+        }
+        else
+        {
+            throw new NotFetchedException("Properties have not been fetched.");
+        }
+    }
+
+    // Method automatically generated with DtoGenerator
+    @Override
+    public void setProperties(Map<String, Serializable> properties)
+    {
+        this.properties = properties;
+    }
+
+    @Override
+    public Serializable getProperty(String propertyName)
+    {
+        return getProperties() != null ? PropertiesDeserializer.getPropertyAsString(getProperties().get(propertyName)) : null;
+    }
+
+    @Override
+    public void setProperty(String propertyName, Serializable propertyValue)
+    {
+        if (properties == null)
+        {
+            properties = new HashMap<>();
+        }
+        properties.put(propertyName, propertyValue);
+    }
+
+    @Override
+    public Long getIntegerProperty(String propertyName)
+    {
+        String propertyValue = (String)getProperty(propertyName);
+        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Long.parseLong(propertyValue);
+    }
+
+    @Override
+    public void setIntegerProperty(String propertyName, Long propertyValue)
+    {
+        setProperty(propertyName, Objects.toString(propertyValue, null));
+    }
+
+    @Override
+    public String getVarcharProperty(String propertyName)
+    {
+        return (String) getProperty(propertyName);
+    }
+
+    @Override
+    public void setVarcharProperty(String propertyName, String propertyValue)
+    {
+        setProperty(propertyName, propertyValue);
+    }
+
+    @Override
+    public String getMultilineVarcharProperty(String propertyName)
+    {
+        return (String) getProperty(propertyName);
+    }
+
+    @Override
+    public void setMultilineVarcharProperty(String propertyName, String propertyValue)
+    {
+        setProperty(propertyName, propertyValue);
+    }
+
+    @Override
+    public Double getRealProperty(String propertyName)
+    {
+        String propertyValue = (String) getProperty(propertyName);
+        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Double.parseDouble(propertyValue);
+
+    }
+
+    @Override
+    public void setRealProperty(String propertyName, Double propertyValue)
+    {
+        setProperty(propertyName, Objects.toString(propertyValue, null));
+    }
+
+    @Override
+    public ZonedDateTime getTimestampProperty(String propertyName)
+    {
+        String propertyValue = (String) getProperty(propertyName);
+        return propertyValue == null ? null : ZonedDateTime.parse(propertyValue);
+    }
+
+    @Override
+    public void setTimestampProperty(String propertyName, ZonedDateTime propertyValue)
+    {
+        String value = (propertyValue == null) ? null : propertyValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"));
+                setProperty(propertyName, value);
+    }
+
+    @Override
+    public Boolean getBooleanProperty(String propertyName)
+    {
+        String propertyValue = (String) getProperty(propertyName);
+        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Boolean.parseBoolean(propertyValue);
+
+    }
+
+    @Override
+    public void setBooleanProperty(String propertyName, Boolean propertyValue)
+    {
+        setProperty(propertyName, Objects.toString(propertyValue, null));
+    }
+
+    @Override
+    public String[] getControlledVocabularyProperty(String propertyName)
+    {
+        if(getProperties() == null || getProperties().get(propertyName) == null) {
+            return null;
+        }
+        Serializable value = getProperties().get(propertyName);
+        if(value.getClass().isArray()) {
+            Serializable[] values = (Serializable[]) value;
+            return Arrays.stream(values).map(x -> (String)x).toArray(String[]::new);
+        } else {
+            String propertyValue = (String) value;
+            return new String[]{ propertyValue };
+        }
+    }
+
+    @Override
+    public void setControlledVocabularyProperty(String propertyName, String[] propertyValue)
+    {
+        setProperty(propertyName, propertyValue);
+    }
+
+    @Override
+    public SamplePermId[] getSampleProperty(String propertyName)
+    {
+        if(getProperties() == null || getProperties().get(propertyName) == null) {
+            return null;
+        }
+        Serializable value = getProperties().get(propertyName);
+        if(value.getClass().isArray()) {
+            Serializable[] values = (Serializable[]) value;
+            return Arrays.stream(values).map(x -> new SamplePermId((String)x)).toArray(SamplePermId[]::new);
+        } else {
+            String propertyValue = (String) value;
+            return new SamplePermId[]{new SamplePermId(propertyValue)};
+        }
+    }
+
+    @Override
+    public void setSampleProperty(String propertyName, SamplePermId[] propertyValue)
+    {
+        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue)
+                .map(ObjectPermId::getPermId)
+                .toArray(String[]::new));
+    }
+
+    @Override
+    public String getHyperlinkProperty(String propertyName)
+    {
+        return (String) getProperty(propertyName);
+    }
+
+    @Override
+    public void setHyperlinkProperty(String propertyName, String propertyValue)
+    {
+        setProperty(propertyName, propertyValue);
+    }
+
+    @Override
+    public String getXmlProperty(String propertyName)
+    {
+        return (String) getProperty(propertyName);
+    }
+
+    @Override
+    public void setXmlProperty(String propertyName, String propertyValue)
+    {
+        setProperty(propertyName, propertyValue);
+    }
+
+    @Override
+    public Long[] getIntegerArrayProperty(String propertyName)
+    {
+        String propertyValue = (String) getProperty(propertyName);
+        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Long::parseLong).toArray(Long[]::new);
+
+    }
+
+    @Override
+    public void setIntegerArrayProperty(String propertyName, Long[] propertyValue)
+    {
+        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
+    }
+
+    @Override
+    public Double[] getRealArrayProperty(String propertyName)
+    {
+        String propertyValue = (String) getProperty(propertyName);
+        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Double::parseDouble).toArray(Double[]::new);
+    }
+
+    @Override
+    public void setRealArrayProperty(String propertyName, Double[] propertyValue)
+    {
+        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
+    }
+
+    @Override
+    public String[] getStringArrayProperty(String propertyName)
+    {
+        String propertyValue = (String) getProperty(propertyName);
+        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).toArray(String[]::new);
+    }
+
+    @Override
+    public void setStringArrayProperty(String propertyName, String[] propertyValue)
+    {
+        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).reduce((a,b) -> a + ", " + b).get());
+    }
+
+    @Override
+    public ZonedDateTime[] getTimestampArrayProperty(String propertyName)
+    {
+        String propertyValue = (String) getProperty(propertyName);
+        return propertyValue == null ? null : Arrays.stream(propertyValue.split(","))
+                .map(String::trim)
+                .map(dateTime -> ZonedDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X")))
+                .toArray(ZonedDateTime[]::new);
+    }
+
+    @Override
+    public void setTimestampArrayProperty(String propertyName, ZonedDateTime[] propertyValue)
+    {
+        String value = (propertyValue == null) ? null : Arrays.stream(propertyValue)
+                .map(dateTime -> dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")))
+                .reduce((a,b) -> a + ", " + b)
+                .get();
+        setProperty(propertyName, value);
+    }
+
+    @Override
+    public String getJsonProperty(String propertyName)
+    {
+        return (String) getProperty(propertyName);
+    }
+
+    @Override
+    public void setJsonProperty(String propertyName, String propertyValue)
+    {
+        setProperty(propertyName, propertyValue);
+    }
+}
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/AbstractEntityFetchOptions.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/AbstractEntityFetchOptions.java
new file mode 100644
index 00000000000..4597891a493
--- /dev/null
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/AbstractEntityFetchOptions.java
@@ -0,0 +1,52 @@
+/*
+ *  Copyright ETH 2023 Zürich, Scientific IT Services
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ */
+
+package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyFetchOptions;
+import ch.systemsx.cisd.base.annotation.JsonObject;
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+@JsonObject("as.dto.common.fetchoptions.AbstractEntityFetchOptions")
+public abstract class AbstractEntityFetchOptions<OBJECT> extends FetchOptions<OBJECT>
+{
+    @JsonProperty
+    protected PropertyFetchOptions properties;
+
+    // Method automatically generated with DtoGenerator
+    public PropertyFetchOptions withProperties()
+    {
+        if (properties == null)
+        {
+            properties = new PropertyFetchOptions();
+        }
+        return properties;
+    }
+
+    // Method automatically generated with DtoGenerator
+    public PropertyFetchOptions withPropertiesUsing(PropertyFetchOptions fetchOptions)
+    {
+        return properties = fetchOptions;
+    }
+
+    // Method automatically generated with DtoGenerator
+    public boolean hasProperties()
+    {
+        return properties != null;
+    }
+
+}
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java
index 426d7c7fad5..f489d583369 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/DataSet.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.entity.AbstractEntity;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.ObjectPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IEntityTypeHolder;
@@ -62,13 +63,11 @@ import java.util.stream.Stream;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.dataset.DataSet")
-public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IExperimentHolder, IMaterialPropertiesHolder, IModificationDateHolder, IModifierHolder, IParentChildrenHolder<DataSet>, IPermIdHolder, IPropertiesHolder, IRegistrationDateHolder, IRegistratorHolder, ISampleHolder, ITagsHolder
+public class DataSet extends AbstractEntity<DataSet>
+        implements Serializable, ICodeHolder, IEntityTypeHolder, IExperimentHolder, IMaterialPropertiesHolder, IModificationDateHolder, IModifierHolder, IParentChildrenHolder<DataSet>, IPermIdHolder, IPropertiesHolder, IRegistrationDateHolder, IRegistratorHolder, ISampleHolder, ITagsHolder
 {
     private static final long serialVersionUID = 1L;
 
-    @JsonProperty
-    private DataSetFetchOptions fetchOptions;
-
     @JsonProperty
     private DataSetPermId permId;
 
@@ -117,10 +116,6 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
     @JsonProperty
     private Sample sample;
 
-    @JsonProperty
-    @JsonDeserialize(contentUsing = PropertiesDeserializer.class)
-    private Map<String, Serializable> properties;
-
     @JsonProperty
     private Map<String, Material> materialProperties;
 
@@ -200,13 +195,13 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
     @JsonIgnore
     public DataSetFetchOptions getFetchOptions()
     {
-        return fetchOptions;
+        return (DataSetFetchOptions) super.getFetchOptions();
     }
 
     // Method automatically generated with DtoGenerator
     public void setFetchOptions(DataSetFetchOptions fetchOptions)
     {
-        this.fetchOptions = fetchOptions;
+        super.setFetchOptions(fetchOptions);
     }
 
     // Method automatically generated with DtoGenerator
@@ -464,28 +459,6 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
         this.sample = sample;
     }
 
-    // Method automatically generated with DtoGenerator
-    @JsonIgnore
-    @Override
-    public Map<String, Serializable> getProperties()
-    {
-        if (getFetchOptions() != null && getFetchOptions().hasProperties())
-        {
-            return properties;
-        }
-        else
-        {
-            throw new NotFetchedException("Properties have not been fetched.");
-        }
-    }
-
-    // Method automatically generated with DtoGenerator
-    @Override
-    public void setProperties(Map<String, Serializable> properties)
-    {
-        this.properties = properties;
-    }
-
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
@@ -940,22 +913,6 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
         this.accessDate = accessDate;
     }
 
-    @Override
-    public String getProperty(String propertyName)
-    {
-        return getProperties() != null ? PropertiesDeserializer.getPropertyAsString(getProperties().get(propertyName)) : null;
-    }
-
-    @Override
-    public void setProperty(String propertyName, Serializable propertyValue)
-    {
-        if (properties == null)
-        {
-            properties = new HashMap<String, Serializable>();
-        }
-        properties.put(propertyName, propertyValue);
-    }
-
     @Override
     public Material getMaterialProperty(String propertyName)
     {
@@ -972,224 +929,6 @@ public class DataSet implements Serializable, ICodeHolder, IEntityTypeHolder, IE
         materialProperties.put(propertyName, propertyValue);
     }
 
-    @Override
-    public Long getIntegerProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Long.parseLong(propertyValue);
-    }
-
-    @Override
-    public void setIntegerProperty(String propertyName, Long propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getMultilineVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setMultilineVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public Double getRealProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Double.parseDouble(propertyValue);
-    }
-
-    @Override
-    public void setRealProperty(String propertyName, Double propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public ZonedDateTime getTimestampProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : ZonedDateTime.parse(getProperty(propertyName));
-    }
-
-    @Override
-    public void setTimestampProperty(String propertyName, ZonedDateTime propertyValue)
-    {
-        String value = (propertyValue == null) ? null : propertyValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"));
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public Boolean getBooleanProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Boolean.parseBoolean(propertyValue);
-    }
-
-    @Override
-    public void setBooleanProperty(String propertyName, Boolean propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getHyperlinkProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setHyperlinkProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getXmlProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setXmlProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String[] getControlledVocabularyProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x -> (String)x).toArray(String[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new String[]{ propertyValue };
-        }
-    }
-
-    @Override
-    public void setControlledVocabularyProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public SamplePermId[] getSampleProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x -> new SamplePermId((String)x)).toArray(SamplePermId[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new SamplePermId[]{new SamplePermId(propertyValue)};
-        }
-    }
-
-    @Override
-    public void setSampleProperty(String propertyName, SamplePermId[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue)
-                .map(ObjectPermId::getPermId)
-                .toArray(String[]::new));
-    }
-
-    @Override
-    public Long[] getIntegerArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Long::parseLong).toArray(Long[]::new);
-    }
-
-    @Override
-    public void setIntegerArrayProperty(String propertyName, Long[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public Double[] getRealArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Double::parseDouble).toArray(Double[]::new);
-    }
-
-    @Override
-    public void setRealArrayProperty(String propertyName, Double[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public String[] getStringArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).toArray(String[]::new);
-    }
-
-    @Override
-    public void setStringArrayProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public ZonedDateTime[] getTimestampArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : Arrays.stream(propertyValue.split(","))
-             .map(String::trim)
-             .map(dateTime -> ZonedDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X")))
-             .toArray(ZonedDateTime[]::new);
-    }
-
-    @Override
-    public void setTimestampArrayProperty(String propertyName, ZonedDateTime[] propertyValue)
-    {
-        String value = (propertyValue == null) ? null : Arrays.stream(propertyValue)
-                 .map(dateTime -> dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")))
-                 .reduce((a,b) -> a + ", " + b)
-                 .get();
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public String getJsonProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setJsonProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
     @JsonIgnore
     public Map<String, String> getMetaData()
     {
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/fetchoptions/DataSetFetchOptions.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/fetchoptions/DataSetFetchOptions.java
index 52e6b171046..af7b2390456 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/fetchoptions/DataSetFetchOptions.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/dataset/fetchoptions/DataSetFetchOptions.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.AbstractEntityFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptionsToStringBuilder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
@@ -38,7 +39,7 @@ import java.io.Serializable;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.dataset.fetchoptions.DataSetFetchOptions")
-public class DataSetFetchOptions extends FetchOptions<DataSet> implements Serializable
+public class DataSetFetchOptions extends AbstractEntityFetchOptions<DataSet> implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
@@ -60,9 +61,6 @@ public class DataSetFetchOptions extends FetchOptions<DataSet> implements Serial
     @JsonProperty
     private SampleFetchOptions sample;
 
-    @JsonProperty
-    private PropertyFetchOptions properties;
-
     @JsonProperty
     private MaterialFetchOptions materialProperties;
 
@@ -255,28 +253,6 @@ public class DataSetFetchOptions extends FetchOptions<DataSet> implements Serial
         return sample != null;
     }
 
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withProperties()
-    {
-        if (properties == null)
-        {
-            properties = new PropertyFetchOptions();
-        }
-        return properties;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withPropertiesUsing(PropertyFetchOptions fetchOptions)
-    {
-        return properties = fetchOptions;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public boolean hasProperties()
-    {
-        return properties != null;
-    }
-
     // Method automatically generated with DtoGenerator
     public MaterialFetchOptions withMaterialProperties()
     {
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
index b69adb9a37e..784e589df34 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/Experiment.java
@@ -16,6 +16,7 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.Attachment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.entity.AbstractEntity;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.ObjectPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IAttachmentsHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
@@ -65,16 +66,14 @@ import java.util.Set;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.experiment.Experiment")
-public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder, IDataSetsHolder,
+public class Experiment extends AbstractEntity<Experiment>
+        implements Serializable, IAttachmentsHolder, ICodeHolder, IDataSetsHolder,
         IEntityTypeHolder, IIdentifierHolder, IMaterialPropertiesHolder, IModificationDateHolder,
         IModifierHolder, IPermIdHolder, IProjectHolder, IPropertiesHolder, IRegistrationDateHolder,
         IRegistratorHolder, ISamplesHolder, ITagsHolder
 {
     private static final long serialVersionUID = 1L;
 
-    @JsonProperty
-    private ExperimentFetchOptions fetchOptions;
-
     @JsonProperty
     private ExperimentPermId permId;
 
@@ -129,10 +128,6 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
     @JsonProperty
     private List<HistoryEntry> unknownHistory;
 
-    @JsonProperty
-    @JsonDeserialize(contentUsing = PropertiesDeserializer.class)
-    private Map<String, Serializable> properties;
-
     @JsonProperty
     private Map<String, Material> materialProperties;
 
@@ -154,18 +149,18 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
     @JsonProperty
     private Map<String, String> metaData;
 
-
     // Method automatically generated with DtoGenerator
     @JsonIgnore
+    @Override
     public ExperimentFetchOptions getFetchOptions()
     {
-        return fetchOptions;
+        return (ExperimentFetchOptions) super.getFetchOptions();
     }
 
     // Method automatically generated with DtoGenerator
     public void setFetchOptions(ExperimentFetchOptions fetchOptions)
     {
-        this.fetchOptions = fetchOptions;
+        super.setFetchOptions(fetchOptions);
     }
 
     // Method automatically generated with DtoGenerator
@@ -285,8 +280,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasType())
         {
             return type;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Experiment type has not been fetched.");
         }
@@ -306,8 +300,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasProject())
         {
             return project;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Project has not been fetched.");
         }
@@ -327,8 +320,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasDataSets())
         {
             return dataSets;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Data sets have not been fetched.");
         }
@@ -348,8 +340,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasSamples())
         {
             return samples;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Samples have not been fetched.");
         }
@@ -368,8 +359,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasHistory())
         {
             return history;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("History have not been fetched.");
         }
@@ -388,8 +378,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasPropertiesHistory())
         {
             return propertiesHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Properties history have not been fetched.");
         }
@@ -408,8 +397,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasProjectHistory())
         {
             return projectHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Project history have not been fetched.");
         }
@@ -428,8 +416,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasSamplesHistory())
         {
             return samplesHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Samples history have not been fetched.");
         }
@@ -448,8 +435,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasDataSetsHistory())
         {
             return dataSetsHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Data sets history have not been fetched.");
         }
@@ -468,8 +454,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasUnknownHistory())
         {
             return unknownHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Unknown history have not been fetched.");
         }
@@ -481,28 +466,6 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         this.unknownHistory = unknownHistory;
     }
 
-    // Method automatically generated with DtoGenerator
-    @JsonIgnore
-    @Override
-    public Map<String, Serializable> getProperties()
-    {
-        if (getFetchOptions() != null && getFetchOptions().hasProperties())
-        {
-            return properties;
-        }
-        else
-        {
-            throw new NotFetchedException("Properties have not been fetched.");
-        }
-    }
-
-    // Method automatically generated with DtoGenerator
-    @Override
-    public void setProperties(Map<String, Serializable> properties)
-    {
-        this.properties = properties;
-    }
-
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
@@ -511,8 +474,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasMaterialProperties())
         {
             return materialProperties;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Material Properties have not been fetched.");
         }
@@ -532,8 +494,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasSampleProperties())
         {
             return sampleProperties;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Sample Properties have not been fetched.");
         }
@@ -553,8 +514,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasTags())
         {
             return tags;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Tags have not been fetched.");
         }
@@ -574,8 +534,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasRegistrator())
         {
             return registrator;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Registrator has not been fetched.");
         }
@@ -595,8 +554,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasModifier())
         {
             return modifier;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Modifier has not been fetched.");
         }
@@ -616,8 +574,7 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         if (getFetchOptions() != null && getFetchOptions().hasAttachments())
         {
             return attachments;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Attachments have not been fetched.");
         }
@@ -629,22 +586,6 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         this.attachments = attachments;
     }
 
-    @Override
-    public String getProperty(String propertyName)
-    {
-        return getProperties() != null ? PropertiesDeserializer.getPropertyAsString(getProperties().get(propertyName)) : null;
-    }
-
-    @Override
-    public void setProperty(String propertyName, Serializable propertyValue)
-    {
-        if (properties == null)
-        {
-            properties = new HashMap<String, Serializable>();
-        }
-        properties.put(propertyName, propertyValue);
-    }
-
     @Override
     public Material getMaterialProperty(String propertyName)
     {
@@ -661,224 +602,6 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         materialProperties.put(propertyName, propertyValue);
     }
 
-    @Override
-    public Long getIntegerProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Long.parseLong(propertyValue);
-    }
-
-    @Override
-    public void setIntegerProperty(String propertyName, Long propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getMultilineVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setMultilineVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public Double getRealProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Double.parseDouble(propertyValue);
-    }
-
-    @Override
-    public void setRealProperty(String propertyName, Double propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public ZonedDateTime getTimestampProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : ZonedDateTime.parse(getProperty(propertyName));
-    }
-
-    @Override
-    public void setTimestampProperty(String propertyName, ZonedDateTime propertyValue)
-    {
-        String value = (propertyValue == null) ? null : propertyValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"));
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public Boolean getBooleanProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Boolean.parseBoolean(propertyValue);
-    }
-
-    @Override
-    public void setBooleanProperty(String propertyName, Boolean propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getHyperlinkProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setHyperlinkProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getXmlProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setXmlProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String[] getControlledVocabularyProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x -> (String)x).toArray(String[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new String[]{ propertyValue };
-        }
-    }
-
-    @Override
-    public void setControlledVocabularyProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public SamplePermId[] getSampleProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x -> new SamplePermId((String)x)).toArray(SamplePermId[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new SamplePermId[]{new SamplePermId(propertyValue)};
-        }
-    }
-
-    @Override
-    public void setSampleProperty(String propertyName, SamplePermId[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue)
-                .map(ObjectPermId::getPermId)
-                .toArray(String[]::new));
-    }
-
-    @Override
-    public Long[] getIntegerArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Long::parseLong).toArray(Long[]::new);
-    }
-
-    @Override
-    public void setIntegerArrayProperty(String propertyName, Long[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public Double[] getRealArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Double::parseDouble).toArray(Double[]::new);
-    }
-
-    @Override
-    public void setRealArrayProperty(String propertyName, Double[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public String[] getStringArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).toArray(String[]::new);
-    }
-
-    @Override
-    public void setStringArrayProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public ZonedDateTime[] getTimestampArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : Arrays.stream(propertyValue.split(","))
-             .map(String::trim)
-             .map(dateTime -> ZonedDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X")))
-             .toArray(ZonedDateTime[]::new);
-    }
-
-    @Override
-    public void setTimestampArrayProperty(String propertyName, ZonedDateTime[] propertyValue)
-    {
-        String value = (propertyValue == null) ? null : Arrays.stream(propertyValue)
-                 .map(dateTime -> dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")))
-                 .reduce((a,b) -> a + ", " + b)
-                 .get();
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public String getJsonProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setJsonProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
     @JsonIgnore
     public Map<String, String> getMetaData()
     {
@@ -890,7 +613,6 @@ public class Experiment implements Serializable, IAttachmentsHolder, ICodeHolder
         this.metaData = metaData;
     }
 
-
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/fetchoptions/ExperimentFetchOptions.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/fetchoptions/ExperimentFetchOptions.java
index 822dd587dc7..5beea32df00 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/fetchoptions/ExperimentFetchOptions.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/experiment/fetchoptions/ExperimentFetchOptions.java
@@ -16,7 +16,7 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.fetchoptions.AttachmentFetchOptions;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.AbstractEntityFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptionsToStringBuilder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
@@ -25,7 +25,6 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.history.fetchoptions.HistoryEntr
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.fetchoptions.MaterialFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.fetchoptions.PersonFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions.ProjectFetchOptions;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.fetchoptions.TagFetchOptions;
 import ch.systemsx.cisd.base.annotation.JsonObject;
@@ -36,7 +35,7 @@ import java.io.Serializable;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.experiment.fetchoptions.ExperimentFetchOptions")
-public class ExperimentFetchOptions extends FetchOptions<Experiment> implements Serializable
+public class ExperimentFetchOptions extends AbstractEntityFetchOptions<Experiment> implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
@@ -70,9 +69,6 @@ public class ExperimentFetchOptions extends FetchOptions<Experiment> implements
     @JsonProperty
     private HistoryEntryFetchOptions unknownHistory;
 
-    @JsonProperty
-    private PropertyFetchOptions properties;
-
     @JsonProperty
     private MaterialFetchOptions materialProperties;
 
@@ -314,27 +310,6 @@ public class ExperimentFetchOptions extends FetchOptions<Experiment> implements
         return unknownHistory != null;
     }
 
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withProperties()
-    {
-        if (properties == null)
-        {
-            properties = new PropertyFetchOptions();
-        }
-        return properties;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withPropertiesUsing(PropertyFetchOptions fetchOptions)
-    {
-        return properties = fetchOptions;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public boolean hasProperties()
-    {
-        return properties != null;
-    }
 
     // Method automatically generated with DtoGenerator
     public MaterialFetchOptions withMaterialProperties()
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java
index 08d79b540a7..a2eb6be6e02 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/Material.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.material;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.entity.AbstractEntity;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.ObjectPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IEntityTypeHolder;
@@ -53,13 +54,11 @@ import java.util.Set;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.material.Material")
-public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, IMaterialPropertiesHolder, IModificationDateHolder, IPermIdHolder, IPropertiesHolder, IRegistrationDateHolder, IRegistratorHolder, ITagsHolder
+public class Material extends AbstractEntity<Material>
+        implements Serializable, ICodeHolder, IEntityTypeHolder, IMaterialPropertiesHolder, IModificationDateHolder, IPermIdHolder, IPropertiesHolder, IRegistrationDateHolder, IRegistratorHolder, ITagsHolder
 {
     private static final long serialVersionUID = 1L;
 
-    @JsonProperty
-    private MaterialFetchOptions fetchOptions;
-
     @JsonProperty
     private MaterialPermId permId;
 
@@ -81,10 +80,6 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
     @JsonProperty
     private Date modificationDate;
 
-    @JsonProperty
-    @JsonDeserialize(contentUsing = PropertiesDeserializer.class)
-    private Map<String, Serializable> properties;
-
     @JsonProperty
     private Map<String, Material> materialProperties;
 
@@ -95,13 +90,13 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
     @JsonIgnore
     public MaterialFetchOptions getFetchOptions()
     {
-        return fetchOptions;
+        return (MaterialFetchOptions) super.getFetchOptions();
     }
 
     // Method automatically generated with DtoGenerator
     public void setFetchOptions(MaterialFetchOptions fetchOptions)
     {
-        this.fetchOptions = fetchOptions;
+        super.setFetchOptions(fetchOptions);
     }
 
     // Method automatically generated with DtoGenerator
@@ -222,28 +217,6 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
         this.modificationDate = modificationDate;
     }
 
-    // Method automatically generated with DtoGenerator
-    @JsonIgnore
-    @Override
-    public Map<String, Serializable> getProperties()
-    {
-        if (getFetchOptions() != null && getFetchOptions().hasProperties())
-        {
-            return properties;
-        }
-        else
-        {
-            throw new NotFetchedException("Properties have not been fetched.");
-        }
-    }
-
-    // Method automatically generated with DtoGenerator
-    @Override
-    public void setProperties(Map<String, Serializable> properties)
-    {
-        this.properties = properties;
-    }
-
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
@@ -287,22 +260,6 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
         this.tags = tags;
     }
 
-    @Override
-    public String getProperty(String propertyName)
-    {
-        return getProperties() != null ? (String) getProperties().get(propertyName) : null;
-    }
-
-    @Override
-    public void setProperty(String propertyName, Serializable propertyValue)
-    {
-        if (properties == null)
-        {
-            properties = new HashMap<>();
-        }
-        properties.put(propertyName, propertyValue);
-    }
-
     @Override
     public Material getMaterialProperty(String propertyName)
     {
@@ -319,224 +276,6 @@ public class Material implements Serializable, ICodeHolder, IEntityTypeHolder, I
         materialProperties.put(propertyName, propertyValue);
     }
 
-    @Override
-    public Long getIntegerProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Long.parseLong(propertyValue);
-    }
-
-    @Override
-    public void setIntegerProperty(String propertyName, Long propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getMultilineVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setMultilineVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public Double getRealProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Double.parseDouble(propertyValue);
-    }
-
-    @Override
-    public void setRealProperty(String propertyName, Double propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public ZonedDateTime getTimestampProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : ZonedDateTime.parse(getProperty(propertyName));
-    }
-
-    @Override
-    public void setTimestampProperty(String propertyName, ZonedDateTime propertyValue)
-    {
-        String value = (propertyValue == null) ? null : propertyValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"));
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public Boolean getBooleanProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Boolean.parseBoolean(propertyValue);
-    }
-
-    @Override
-    public void setBooleanProperty(String propertyName, Boolean propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getHyperlinkProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setHyperlinkProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getXmlProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setXmlProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String[] getControlledVocabularyProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x->(String)x).toArray(String[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new String[]{ propertyValue };
-        }
-    }
-
-    @Override
-    public void setControlledVocabularyProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public SamplePermId[] getSampleProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x -> new SamplePermId((String)x)).toArray(SamplePermId[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new SamplePermId[]{new SamplePermId(propertyValue)};
-        }
-    }
-
-    @Override
-    public void setSampleProperty(String propertyName, SamplePermId[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue)
-                .map(ObjectPermId::getPermId)
-                .toArray(String[]::new));
-    }
-
-    @Override
-    public Long[] getIntegerArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Long::parseLong).toArray(Long[]::new);
-    }
-
-    @Override
-    public void setIntegerArrayProperty(String propertyName, Long[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public Double[] getRealArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Double::parseDouble).toArray(Double[]::new);
-    }
-
-    @Override
-    public void setRealArrayProperty(String propertyName, Double[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public String[] getStringArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).toArray(String[]::new);
-    }
-
-    @Override
-    public void setStringArrayProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public ZonedDateTime[] getTimestampArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : Arrays.stream(propertyValue.split(","))
-             .map(String::trim)
-             .map(dateTime -> ZonedDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X")))
-             .toArray(ZonedDateTime[]::new);
-    }
-
-    @Override
-    public void setTimestampArrayProperty(String propertyName, ZonedDateTime[] propertyValue)
-    {
-        String value = (propertyValue == null) ? null : Arrays.stream(propertyValue)
-                 .map(dateTime -> dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")))
-                 .reduce((a,b) -> a + ", " + b)
-                 .get();
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public String getJsonProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setJsonProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
     // Method automatically generated with DtoGenerator
     @Override
     public String toString()
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/fetchoptions/MaterialFetchOptions.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/fetchoptions/MaterialFetchOptions.java
index 0bfabbafcd8..39d62d66b84 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/fetchoptions/MaterialFetchOptions.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/material/fetchoptions/MaterialFetchOptions.java
@@ -15,6 +15,7 @@
  */
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.material.fetchoptions;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.AbstractEntityFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptionsToStringBuilder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.history.fetchoptions.HistoryEntryFetchOptions;
@@ -32,7 +33,7 @@ import java.io.Serializable;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.material.fetchoptions.MaterialFetchOptions")
-public class MaterialFetchOptions extends FetchOptions<Material> implements Serializable
+public class MaterialFetchOptions extends AbstractEntityFetchOptions<Material> implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
@@ -45,9 +46,6 @@ public class MaterialFetchOptions extends FetchOptions<Material> implements Seri
     @JsonProperty
     private PersonFetchOptions registrator;
 
-    @JsonProperty
-    private PropertyFetchOptions properties;
-
     @JsonProperty
     private MaterialFetchOptions materialProperties;
 
@@ -123,27 +121,6 @@ public class MaterialFetchOptions extends FetchOptions<Material> implements Seri
         return registrator != null;
     }
 
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withProperties()
-    {
-        if (properties == null)
-        {
-            properties = new PropertyFetchOptions();
-        }
-        return properties;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withPropertiesUsing(PropertyFetchOptions fetchOptions)
-    {
-        return properties = fetchOptions;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public boolean hasProperties()
-    {
-        return properties != null;
-    }
 
     // Method automatically generated with DtoGenerator
     public MaterialFetchOptions withMaterialProperties()
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
index 3cb82e616df..f0f696d401c 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/Sample.java
@@ -17,7 +17,7 @@ package ch.ethz.sis.openbis.generic.asapi.v3.dto.sample;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.Attachment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.Relationship;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.ObjectPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.entity.AbstractEntity;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IAttachmentsHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IDataSetsHolder;
@@ -35,7 +35,6 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationD
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistratorHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ISpaceHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ITagsHolder;
-import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.property.PropertiesDeserializer;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.history.HistoryEntry;
@@ -52,30 +51,27 @@ import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 import com.fasterxml.jackson.annotation.JsonIgnore;
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
 
 import java.io.Serializable;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Set;
 
 /*
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.sample.Sample")
-public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, IDataSetsHolder, IEntityTypeHolder, IExperimentHolder, IIdentifierHolder, IMaterialPropertiesHolder, IModificationDateHolder, IModifierHolder, IParentChildrenHolder<Sample>, IPermIdHolder, IProjectHolder, IPropertiesHolder, IRegistrationDateHolder, IRegistratorHolder, ISpaceHolder, ITagsHolder
+public class Sample extends AbstractEntity<Sample>
+        implements Serializable, IAttachmentsHolder, ICodeHolder, IDataSetsHolder,
+        IEntityTypeHolder, IExperimentHolder, IIdentifierHolder, IMaterialPropertiesHolder,
+        IModificationDateHolder, IModifierHolder, IParentChildrenHolder<Sample>, IPermIdHolder,
+        IProjectHolder, IPropertiesHolder, IRegistrationDateHolder, IRegistratorHolder,
+        ISpaceHolder, ITagsHolder
 {
     private static final long serialVersionUID = 1L;
 
-    @JsonProperty
-    private SampleFetchOptions fetchOptions;
-
     @JsonProperty
     private SamplePermId permId;
 
@@ -118,10 +114,6 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
     @JsonProperty
     private Experiment experiment;
 
-    @JsonProperty
-    @JsonDeserialize(contentUsing = PropertiesDeserializer.class)
-    private Map<String, Serializable> properties;
-
     @JsonProperty
     private Map<String, Material> materialProperties;
 
@@ -199,15 +191,16 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
 
     // Method automatically generated with DtoGenerator
     @JsonIgnore
+    @Override
     public SampleFetchOptions getFetchOptions()
     {
-        return fetchOptions;
+        return (SampleFetchOptions) super.getFetchOptions();
     }
 
     // Method automatically generated with DtoGenerator
     public void setFetchOptions(SampleFetchOptions fetchOptions)
     {
-        this.fetchOptions = fetchOptions;
+        super.setFetchOptions(fetchOptions);
     }
 
     // Method automatically generated with DtoGenerator
@@ -353,8 +346,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasType())
         {
             return type;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Sample type has not been fetched.");
         }
@@ -374,8 +366,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasProject())
         {
             return project;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Project has not been fetched.");
         }
@@ -395,8 +386,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasSpace())
         {
             return space;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Space has not been fetched.");
         }
@@ -416,8 +406,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasExperiment())
         {
             return experiment;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Experiment has not been fetched.");
         }
@@ -429,28 +418,6 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         this.experiment = experiment;
     }
 
-    // Method automatically generated with DtoGenerator
-    @JsonIgnore
-    @Override
-    public Map<String, Serializable> getProperties()
-    {
-        if (getFetchOptions() != null && getFetchOptions().hasProperties())
-        {
-            return properties;
-        }
-        else
-        {
-            throw new NotFetchedException("Properties have not been fetched.");
-        }
-    }
-
-    // Method automatically generated with DtoGenerator
-    @Override
-    public void setProperties(Map<String, Serializable> properties)
-    {
-        this.properties = properties;
-    }
-
     // Method automatically generated with DtoGenerator
     @JsonIgnore
     @Override
@@ -459,8 +426,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasMaterialProperties())
         {
             return materialProperties;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Material Properties have not been fetched.");
         }
@@ -480,8 +446,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasSampleProperties())
         {
             return sampleProperties;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Sample Properties have not been fetched.");
         }
@@ -501,8 +466,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasParents())
         {
             return parents;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Parents have not been fetched.");
         }
@@ -521,8 +485,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasParents())
         {
             return parentsRelationships;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Parents have not been fetched.");
         }
@@ -542,8 +505,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasChildren())
         {
             return children;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Children have not been fetched.");
         }
@@ -562,8 +524,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasChildren())
         {
             return childrenRelationships;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Children have not been fetched.");
         }
@@ -582,8 +543,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasContainer())
         {
             return container;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Container sample has not been fetched.");
         }
@@ -602,8 +562,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasComponents())
         {
             return components;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Component samples have not been fetched.");
         }
@@ -623,8 +582,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasDataSets())
         {
             return dataSets;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Data sets have not been fetched.");
         }
@@ -643,8 +601,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasHistory())
         {
             return history;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("History have not been fetched.");
         }
@@ -663,8 +620,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasPropertiesHistory())
         {
             return propertiesHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Properties history have not been fetched.");
         }
@@ -683,8 +639,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasSpaceHistory())
         {
             return spaceHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Space history have not been fetched.");
         }
@@ -703,8 +658,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasProjectHistory())
         {
             return projectHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Project history have not been fetched.");
         }
@@ -723,8 +677,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasExperimentHistory())
         {
             return experimentHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Experiment history have not been fetched.");
         }
@@ -743,8 +696,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasParentsHistory())
         {
             return parentsHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Parents history have not been fetched.");
         }
@@ -763,8 +715,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasChildrenHistory())
         {
             return childrenHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Children history have not been fetched.");
         }
@@ -783,8 +734,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasContainerHistory())
         {
             return containerHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Container history have not been fetched.");
         }
@@ -803,8 +753,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasComponentsHistory())
         {
             return componentsHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Components history have not been fetched.");
         }
@@ -823,8 +772,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasDataSetsHistory())
         {
             return dataSetsHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Data sets history have not been fetched.");
         }
@@ -843,8 +791,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasUnknownHistory())
         {
             return unknownHistory;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Unknown history have not been fetched.");
         }
@@ -864,8 +811,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasTags())
         {
             return tags;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Tags have not been fetched.");
         }
@@ -885,8 +831,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasRegistrator())
         {
             return registrator;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Registrator has not been fetched.");
         }
@@ -906,8 +851,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasModifier())
         {
             return modifier;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Modifier has not been fetched.");
         }
@@ -927,8 +871,7 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         if (getFetchOptions() != null && getFetchOptions().hasAttachments())
         {
             return attachments;
-        }
-        else
+        } else
         {
             throw new NotFetchedException("Attachments have not been fetched.");
         }
@@ -940,22 +883,6 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         this.attachments = attachments;
     }
 
-    @Override
-    public String getProperty(String propertyName)
-    {
-        return getProperties() != null ? PropertiesDeserializer.getPropertyAsString(getProperties().get(propertyName)) : null;
-    }
-
-    @Override
-    public void setProperty(String propertyName, Serializable propertyValue)
-    {
-        if (properties == null)
-        {
-            properties = new HashMap<>();
-        }
-        properties.put(propertyName, propertyValue);
-    }
-
     @Override
     public Material getMaterialProperty(String propertyName)
     {
@@ -972,224 +899,6 @@ public class Sample implements Serializable, IAttachmentsHolder, ICodeHolder, ID
         materialProperties.put(propertyName, propertyValue);
     }
 
-    @Override
-    public Long getIntegerProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Long.parseLong(propertyValue);
-    }
-
-    @Override
-    public void setIntegerProperty(String propertyName, Long propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getMultilineVarcharProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setMultilineVarcharProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public Double getRealProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Double.parseDouble(propertyValue);
-    }
-
-    @Override
-    public void setRealProperty(String propertyName, Double propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public ZonedDateTime getTimestampProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : ZonedDateTime.parse(getProperty(propertyName));
-    }
-
-    @Override
-    public void setTimestampProperty(String propertyName, ZonedDateTime propertyValue)
-    {
-        String value = (propertyValue == null) ? null : propertyValue.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX"));
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public Boolean getBooleanProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Boolean.parseBoolean(propertyValue);
-    }
-
-    @Override
-    public void setBooleanProperty(String propertyName, Boolean propertyValue)
-    {
-        setProperty(propertyName, Objects.toString(propertyValue, null));
-    }
-
-    @Override
-    public String getHyperlinkProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setHyperlinkProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String getXmlProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setXmlProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public String[] getControlledVocabularyProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x->(String)x).toArray(String[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new String[]{ propertyValue };
-        }
-    }
-
-    @Override
-    public void setControlledVocabularyProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
-    @Override
-    public SamplePermId[] getSampleProperty(String propertyName)
-    {
-        if(getProperties() == null || getProperties().get(propertyName) == null) {
-            return null;
-        }
-        Serializable value = getProperties().get(propertyName);
-        if(value.getClass().isArray()) {
-            Serializable[] values = (Serializable[]) value;
-            return Arrays.stream(values).map(x -> new SamplePermId((String)x)).toArray(SamplePermId[]::new);
-        } else {
-            String propertyValue = (String) value;
-            return new SamplePermId[]{new SamplePermId(propertyValue)};
-        }
-    }
-
-    @Override
-    public void setSampleProperty(String propertyName, SamplePermId[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue)
-                .map(ObjectPermId::getPermId)
-                .toArray(String[]::new));
-    }
-
-    @Override
-    public Long[] getIntegerArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Long::parseLong).toArray(Long[]::new);
-    }
-
-    @Override
-    public void setIntegerArrayProperty(String propertyName, Long[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public Double[] getRealArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).map(Double::parseDouble).toArray(Double[]::new);
-    }
-
-    @Override
-    public void setRealArrayProperty(String propertyName, Double[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).map(Object::toString).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public String[] getStringArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return (propertyValue == null || propertyValue.trim().isEmpty()) ? null : Arrays.stream(propertyValue.split(",")).map(String::trim).toArray(String[]::new);
-    }
-
-    @Override
-    public void setStringArrayProperty(String propertyName, String[] propertyValue)
-    {
-        setProperty(propertyName, propertyValue == null ? null : Arrays.stream(propertyValue).reduce((a,b) -> a + ", " + b).get());
-    }
-
-    @Override
-    public ZonedDateTime[] getTimestampArrayProperty(String propertyName)
-    {
-        String propertyValue = getProperty(propertyName);
-        return propertyValue == null ? null : Arrays.stream(propertyValue.split(","))
-             .map(String::trim)
-             .map(dateTime -> ZonedDateTime.parse(dateTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss X")))
-             .toArray(ZonedDateTime[]::new);
-    }
-
-    @Override
-    public void setTimestampArrayProperty(String propertyName, ZonedDateTime[] propertyValue)
-    {
-        String value = (propertyValue == null) ? null : Arrays.stream(propertyValue)
-                 .map(dateTime -> dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ssX")))
-                 .reduce((a,b) -> a + ", " + b)
-                 .get();
-        setProperty(propertyName, value);
-    }
-
-    @Override
-    public String getJsonProperty(String propertyName)
-    {
-        return getProperty(propertyName);
-    }
-
-    @Override
-    public void setJsonProperty(String propertyName, String propertyValue)
-    {
-        setProperty(propertyName, propertyValue);
-    }
-
     @JsonIgnore
     public Relationship getParentRelationship(ISampleId parentId)
     {
diff --git a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/fetchoptions/SampleFetchOptions.java b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/fetchoptions/SampleFetchOptions.java
index 2076bcca10d..1e8d06de4fa 100644
--- a/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/fetchoptions/SampleFetchOptions.java
+++ b/api-openbis-java/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/sample/fetchoptions/SampleFetchOptions.java
@@ -16,6 +16,7 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.attachment.fetchoptions.AttachmentFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.AbstractEntityFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptionsToStringBuilder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetFetchOptions;
@@ -38,7 +39,7 @@ import java.io.Serializable;
  * Class automatically generated with DtoGenerator
  */
 @JsonObject("as.dto.sample.fetchoptions.SampleFetchOptions")
-public class SampleFetchOptions extends FetchOptions<Sample> implements Serializable
+public class SampleFetchOptions extends AbstractEntityFetchOptions<Sample> implements Serializable
 {
     private static final long serialVersionUID = 1L;
 
@@ -54,9 +55,6 @@ public class SampleFetchOptions extends FetchOptions<Sample> implements Serializ
     @JsonProperty
     private ExperimentFetchOptions experiment;
 
-    @JsonProperty
-    private PropertyFetchOptions properties;
-
     @JsonProperty
     private MaterialFetchOptions materialProperties;
 
@@ -214,27 +212,6 @@ public class SampleFetchOptions extends FetchOptions<Sample> implements Serializ
         return experiment != null;
     }
 
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withProperties()
-    {
-        if (properties == null)
-        {
-            properties = new PropertyFetchOptions();
-        }
-        return properties;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public PropertyFetchOptions withPropertiesUsing(PropertyFetchOptions fetchOptions)
-    {
-        return properties = fetchOptions;
-    }
-
-    // Method automatically generated with DtoGenerator
-    public boolean hasProperties()
-    {
-        return properties != null;
-    }
 
     // Method automatically generated with DtoGenerator
     public MaterialFetchOptions withMaterialProperties()
-- 
GitLab