From a82c835083b2a681b1a70124b2a0a44805438d48 Mon Sep 17 00:00:00 2001
From: felmer <felmer>
Date: Tue, 3 May 2016 06:41:03 +0000
Subject: [PATCH] SSDM-3529: Java part done.

SVN: 36459
---
 .../asapi/v3/helper/sort/SortAndPage.java     |  5 +-
 .../DataSetPropertyAssignmentTranslator.java  |  3 +-
 ...xperimentPropertyAssignmentTranslator.java |  3 +-
 .../MaterialPropertyAssignmentTranslator.java |  3 +-
 .../PropertyAssignmentTranslator.java         | 62 +++++++++++-
 .../entity/property/PropertyTypeRecord.java   |  1 +
 .../SamplePropertyAssignmentTranslator.java   |  3 +-
 .../asapi/v3/SearchDataSetTypeTest.java       | 95 ++++++++++++++++++-
 .../v3/dto/property/PropertyAssignment.java   | 19 ++++
 .../asapi/v3/dto/property/PropertyType.java   | 35 +++++++
 .../PropertyAssignmentFetchOptions.java       | 23 +++++
 .../generic/sharedapi/v3/dictionary.txt       |  5 +-
 12 files changed, 246 insertions(+), 11 deletions(-)

diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
index cc6a8d546a7..63d9eba47c8 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
@@ -185,7 +185,10 @@ public class SortAndPage
                             } else
                             {
                                 Collection newValue = sortAndPage(Collections.singleton(value), subFo);
-                                setMethod.invoke(object, newValue.iterator().next());
+                                if (setMethod != null)
+                                {
+                                    setMethod.invoke(object, newValue.iterator().next());
+                                }
                             }
                         }
                     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/dataset/DataSetPropertyAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/dataset/DataSetPropertyAssignmentTranslator.java
index 09783cfc702..523b846df30 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/dataset/DataSetPropertyAssignmentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/dataset/DataSetPropertyAssignmentTranslator.java
@@ -50,7 +50,8 @@ public class DataSetPropertyAssignmentTranslator extends PropertyAssignmentTrans
             Collection<Long> dataSetTypePropertyTypeIds, PropertyAssignmentFetchOptions relatedFetchOptions)
     {
         DataSetQuery query = QueryTool.getManagedQuery(DataSetQuery.class);
-        return getAssignments(query.getPropertyAssignments(new LongOpenHashSet(dataSetTypePropertyTypeIds)));
+        return getAssignments(context, query.getPropertyAssignments(new LongOpenHashSet(dataSetTypePropertyTypeIds)), 
+                relatedFetchOptions);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/experiment/ExperimentPropertyAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/experiment/ExperimentPropertyAssignmentTranslator.java
index 961b8d5d222..73557619d6c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/experiment/ExperimentPropertyAssignmentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/experiment/ExperimentPropertyAssignmentTranslator.java
@@ -50,7 +50,8 @@ public class ExperimentPropertyAssignmentTranslator extends PropertyAssignmentTr
             Collection<Long> experimentTypePropertyTypeIds, PropertyAssignmentFetchOptions relatedFetchOptions)
     {
         ExperimentQuery query = QueryTool.getManagedQuery(ExperimentQuery.class);
-        return getAssignments(query.getPropertyAssignments(new LongOpenHashSet(experimentTypePropertyTypeIds)));
+        return getAssignments(context, query.getPropertyAssignments(new LongOpenHashSet(experimentTypePropertyTypeIds)), 
+                relatedFetchOptions);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/material/MaterialPropertyAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/material/MaterialPropertyAssignmentTranslator.java
index 3535d14111f..0a35198b699 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/material/MaterialPropertyAssignmentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/material/MaterialPropertyAssignmentTranslator.java
@@ -50,7 +50,8 @@ public class MaterialPropertyAssignmentTranslator extends PropertyAssignmentTran
             Collection<Long> materialTypePropertyTaypeIds, PropertyAssignmentFetchOptions relatedFetchOptions)
     {
         MaterialQuery query = QueryTool.getManagedQuery(MaterialQuery.class);
-        return getAssignments(query.getPropertyAssignments(new LongOpenHashSet(materialTypePropertyTaypeIds)));
+        return getAssignments(context, query.getPropertyAssignments(new LongOpenHashSet(materialTypePropertyTaypeIds)), 
+                relatedFetchOptions);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyAssignmentTranslator.java
index 29d4643a3e4..36bb259df94 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyAssignmentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyAssignmentTranslator.java
@@ -21,12 +21,20 @@ import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.springframework.beans.factory.annotation.Autowired;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.DataTypeCode;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyAssignmentFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularyFetchOptions;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.TranslationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.entity.common.ObjectToManyRelationTranslator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.translator.entity.vocabulary.IVocabularyTranslator;
 
 import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
 import net.lemnik.eodsql.QueryTool;
@@ -36,7 +44,12 @@ import net.lemnik.eodsql.QueryTool;
  */
 public abstract class PropertyAssignmentTranslator extends ObjectToManyRelationTranslator<PropertyAssignment, PropertyAssignmentFetchOptions>
 {
-    protected Map<Long, PropertyAssignment> getAssignments(Collection<PropertyAssignmentRecord> assignmentRecords)
+    @Autowired
+    private IVocabularyTranslator vocabularyTranslator;
+
+    protected Map<Long, PropertyAssignment> getAssignments(TranslationContext context, 
+            Collection<PropertyAssignmentRecord> assignmentRecords, 
+            PropertyAssignmentFetchOptions assignmentFetchOptions)
     {
         Map<Long, PropertyAssignment> assignments = new HashMap<>();
         Map<Long, List<PropertyAssignment>> assignmentsByPropertyTypeId = new HashMap<>();
@@ -53,8 +66,39 @@ public abstract class PropertyAssignmentTranslator extends ObjectToManyRelationT
             }
             list.add(assignment);
         }
+        VocabularyFetchOptions vocabularyFetchOptions = getVocabularyFetchOptions(assignmentFetchOptions);
+        Map<Long, PropertyType> propertyTypesByVocabularyId = injectPropertyTypes(assignmentsByPropertyTypeId, vocabularyFetchOptions);
+        injectVocabularies(context, vocabularyFetchOptions, propertyTypesByVocabularyId);
+        return assignments;
+    }
+
+    private void injectVocabularies(TranslationContext context, VocabularyFetchOptions vocabularyFetchOptions,
+            Map<Long, PropertyType> propertyTypesByVocabularyId)
+    {
+        if (vocabularyFetchOptions != null)
+        {
+            Set<Long> ids = propertyTypesByVocabularyId.keySet();
+            Map<Long, Vocabulary> map = vocabularyTranslator.translate(context, ids, vocabularyFetchOptions);
+            Set<Entry<Long, Vocabulary>> entrySet = map.entrySet();
+            for (Entry<Long, Vocabulary> entry : entrySet)
+            {
+                Long vocabularyId = entry.getKey();
+                Vocabulary vocabulary = entry.getValue();
+                PropertyType propertyType = propertyTypesByVocabularyId.get(vocabularyId);
+                if (propertyType != null)
+                {
+                    propertyType.setVocabulary(vocabulary);
+                }
+            }
+        }
+    }
+
+    private Map<Long, PropertyType> injectPropertyTypes(Map<Long, List<PropertyAssignment>> assignmentsByPropertyTypeId,
+            VocabularyFetchOptions vocabularyFetchOptions)
+    {
         PropertyTypeQuery query = QueryTool.getManagedQuery(PropertyTypeQuery.class);
         List<PropertyTypeRecord> propertyTypeRecords = query.getPropertyTypes(new LongOpenHashSet(assignmentsByPropertyTypeId.keySet()));
+        Map<Long, PropertyType> propertyTypesByVocabularyId = new HashMap<>();
         for (PropertyTypeRecord propertyTypeRecord : propertyTypeRecords)
         {
             Long propertyTypeId = propertyTypeRecord.id;
@@ -64,12 +108,26 @@ public abstract class PropertyAssignmentTranslator extends ObjectToManyRelationT
             propertyType.setDescription(propertyTypeRecord.description);
             propertyType.setDataTypeCode(DataTypeCode.valueOf(propertyTypeRecord.dataSetTypeCode));
             propertyType.setInternalNameSpace(propertyTypeRecord.is_internal_namespace);
+            propertyType.setVocabularyFetchOptions(vocabularyFetchOptions);
+            if (propertyTypeRecord.covo_id != null)
+            {
+                propertyTypesByVocabularyId.put(propertyTypeRecord.covo_id, propertyType);
+            }
             for (PropertyAssignment assignment : assignmentsByPropertyTypeId.get(propertyTypeId))
             {
                 assignment.setPropertyType(propertyType);
             }
         }
-        return assignments;
+        return propertyTypesByVocabularyId;
+    }
+
+    private VocabularyFetchOptions getVocabularyFetchOptions(PropertyAssignmentFetchOptions assignmentFetchOptions)
+    {
+        if (assignmentFetchOptions.hasVocabulary())
+        {
+            return assignmentFetchOptions.withVocabulary();
+        }
+        return null;
     }
 
     @Override
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyTypeRecord.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyTypeRecord.java
index 79e5c03835b..f6f8a365158 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyTypeRecord.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/property/PropertyTypeRecord.java
@@ -32,4 +32,5 @@ public class PropertyTypeRecord extends ObjectBaseRecord
     public Boolean is_internal_namespace;
 
     public String dataSetTypeCode;
+    public Long covo_id;
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/sample/SamplePropertyAssignmentTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/sample/SamplePropertyAssignmentTranslator.java
index db87489f50e..5609b13b274 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/sample/SamplePropertyAssignmentTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/entity/sample/SamplePropertyAssignmentTranslator.java
@@ -49,7 +49,8 @@ public class SamplePropertyAssignmentTranslator extends PropertyAssignmentTransl
             Collection<Long> sampleTypePropertyTypeIds, PropertyAssignmentFetchOptions relatedFetchOptions)
     {
         SampleQuery query = QueryTool.getManagedQuery(SampleQuery.class);
-        return getAssignments(query.getPropertyAssignments(new LongOpenHashSet(sampleTypePropertyTypeIds)));
+        return getAssignments(context, query.getPropertyAssignments(new LongOpenHashSet(sampleTypePropertyTypeIds)), 
+                relatedFetchOptions);
     }
 
 }
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDataSetTypeTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDataSetTypeTest.java
index cb526708b2d..ff94590cd11 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDataSetTypeTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/SearchDataSetTypeTest.java
@@ -18,6 +18,7 @@ package ch.ethz.sis.openbis.systemtest.asapi.v3;
 
 import static org.testng.Assert.assertEquals;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
@@ -27,7 +28,11 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSetType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetTypeFetchOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.search.EntityTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.DataTypeCode;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyAssignment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyAssignmentFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
 
 /**
@@ -47,13 +52,97 @@ public class SearchDataSetTypeTest extends AbstractTest
         List<DataSetType> types = searchResult.getObjects();
         List<String> codes = extractCodes(types);
         Collections.sort(codes);
-        assertEquals(
-                codes.toString(),
-                "[CONTAINER_TYPE, DELETION_TEST, DELETION_TEST_CONTAINER, HCS_IMAGE, HCS_IMAGE_ANALYSIS_DATA, LINK_TYPE, REQUIRES_EXPERIMENT, UNKNOWN, VALIDATED_CONTAINER_TYPE, VALIDATED_IMPOSSIBLE_TO_UPDATE_TYPE, VALIDATED_NORMAL_TYPE]");
+        assertEquals(codes.toString(), "[CONTAINER_TYPE, DELETION_TEST, DELETION_TEST_CONTAINER, HCS_IMAGE, "
+                + "HCS_IMAGE_ANALYSIS_DATA, LINK_TYPE, REQUIRES_EXPERIMENT, UNKNOWN, VALIDATED_CONTAINER_TYPE, "
+                + "VALIDATED_IMPOSSIBLE_TO_UPDATE_TYPE, VALIDATED_NORMAL_TYPE]");
         assertEquals(types.get(0).getFetchOptions().hasPropertyAssignments(), true);
         v3api.logout(sessionToken);
     }
 
+    @Test
+    public void testSearchAllWithoutVocabularies()
+    {
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        EntityTypeSearchCriteria searchCriteria = new EntityTypeSearchCriteria();
+        DataSetTypeFetchOptions fetchOptions = new DataSetTypeFetchOptions();
+        fetchOptions.withPropertyAssignments();
+        
+        SearchResult<DataSetType> searchResult = v3api.searchDataSetTypes(sessionToken, searchCriteria, fetchOptions);
+        
+        List<DataSetType> types = searchResult.getObjects();
+        List<String> codes = extractCodes(types);
+        Collections.sort(codes);
+        assertEquals(codes.toString(), "[CONTAINER_TYPE, DELETION_TEST, DELETION_TEST_CONTAINER, HCS_IMAGE, "
+                + "HCS_IMAGE_ANALYSIS_DATA, LINK_TYPE, REQUIRES_EXPERIMENT, UNKNOWN, VALIDATED_CONTAINER_TYPE, "
+                + "VALIDATED_IMPOSSIBLE_TO_UPDATE_TYPE, VALIDATED_NORMAL_TYPE]");
+        assertEquals(types.get(0).getFetchOptions().hasPropertyAssignments(), true);
+        assertEquals(getDataSetTypePropertyTypeInfo(types).toString(), "[CONTAINER_TYPE:, "
+                + "DELETION_TEST: BACTERIUM[MATERIAL] DESCRIPTION[VARCHAR] ORGANISM[CONTROLLEDVOCABULARY:?], "
+                + "DELETION_TEST_CONTAINER: BACTERIUM[MATERIAL] DESCRIPTION[VARCHAR] ORGANISM[CONTROLLEDVOCABULARY:?], "
+                + "HCS_IMAGE: ANY_MATERIAL[MATERIAL] BACTERIUM[MATERIAL] COMMENT[VARCHAR] GENDER[CONTROLLEDVOCABULARY:?], "
+                + "HCS_IMAGE_ANALYSIS_DATA:, LINK_TYPE:, REQUIRES_EXPERIMENT:, UNKNOWN:, "
+                + "VALIDATED_CONTAINER_TYPE:, VALIDATED_IMPOSSIBLE_TO_UPDATE_TYPE:, VALIDATED_NORMAL_TYPE:]");
+        v3api.logout(sessionToken);
+    }
+    
+    @Test
+    public void testSearchAllWithVocabularies()
+    {
+        String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        EntityTypeSearchCriteria searchCriteria = new EntityTypeSearchCriteria();
+        DataSetTypeFetchOptions fetchOptions = new DataSetTypeFetchOptions();
+        PropertyAssignmentFetchOptions assignmentFetchOptions = fetchOptions.withPropertyAssignments();
+        assignmentFetchOptions.sortBy().label().desc();
+        assignmentFetchOptions.withVocabulary();
+        
+        SearchResult<DataSetType> searchResult = v3api.searchDataSetTypes(sessionToken, searchCriteria, fetchOptions);
+        
+        List<DataSetType> types = searchResult.getObjects();
+        List<String> codes = extractCodes(types);
+        Collections.sort(codes);
+        assertEquals(codes.toString(), "[CONTAINER_TYPE, DELETION_TEST, DELETION_TEST_CONTAINER, HCS_IMAGE, "
+                + "HCS_IMAGE_ANALYSIS_DATA, LINK_TYPE, REQUIRES_EXPERIMENT, UNKNOWN, VALIDATED_CONTAINER_TYPE, "
+                + "VALIDATED_IMPOSSIBLE_TO_UPDATE_TYPE, VALIDATED_NORMAL_TYPE]");
+        assertEquals(types.get(0).getFetchOptions().hasPropertyAssignments(), true);
+        assertEquals(getDataSetTypePropertyTypeInfo(types).toString(), "[CONTAINER_TYPE:, "
+                + "DELETION_TEST: BACTERIUM[MATERIAL] ORGANISM[CONTROLLEDVOCABULARY:ORGANISM] DESCRIPTION[VARCHAR], "
+                + "DELETION_TEST_CONTAINER: BACTERIUM[MATERIAL] ORGANISM[CONTROLLEDVOCABULARY:ORGANISM] DESCRIPTION[VARCHAR], "
+                + "HCS_IMAGE: BACTERIUM[MATERIAL] ANY_MATERIAL[MATERIAL] GENDER[CONTROLLEDVOCABULARY:GENDER] COMMENT[VARCHAR], "
+                + "HCS_IMAGE_ANALYSIS_DATA:, LINK_TYPE:, REQUIRES_EXPERIMENT:, UNKNOWN:, "
+                + "VALIDATED_CONTAINER_TYPE:, VALIDATED_IMPOSSIBLE_TO_UPDATE_TYPE:, VALIDATED_NORMAL_TYPE:]");
+        v3api.logout(sessionToken);
+    }
+    
+    protected List<String> getDataSetTypePropertyTypeInfo(List<DataSetType> types)
+    {
+        List<String> infos = new ArrayList<>();
+        for (DataSetType type : types)
+        {
+            List<PropertyAssignment> assignments = type.getPropertyAssignments();
+            String info = type.getCode() + ":";
+            for (PropertyAssignment assignment : assignments)
+            {
+                PropertyType propertyType = assignment.getPropertyType();
+                info += " " + propertyType.getCode() + "[" + propertyType.getDataTypeCode();
+                if (propertyType.getVocabularyFetchOptions() != null)
+                {
+                    Vocabulary vocabulary = propertyType.getVocabulary();
+                    if (vocabulary != null)
+                    {
+                        info += ":" + vocabulary.getCode();
+                    }
+                } else if (propertyType.getDataTypeCode() == DataTypeCode.CONTROLLEDVOCABULARY)
+                {
+                    info += ":?";
+                }
+                info += "]";
+            }
+            infos.add(info);
+        }
+        Collections.sort(infos);
+        return infos;
+    }
+    
     @Test
     public void testSearchExactCode()
     {
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java
index eee2692dfd4..8efbe86521e 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyAssignment.java
@@ -20,6 +20,9 @@ import java.io.Serializable;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularyFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
 /**
@@ -56,4 +59,20 @@ public class PropertyAssignment implements Serializable
         this.propertyType = propertyType;
     }
 
+    public VocabularyFetchOptions getVocabularyFetchOptions()
+    {
+        return propertyType.getVocabularyFetchOptions();
+    }
+    
+    public Vocabulary getVocabulary()
+    {
+        if (getVocabularyFetchOptions() != null)
+        {
+            return propertyType.getVocabulary();
+        }
+        else
+        {
+            throw new NotFetchedException("Vocabulary has not been fetched.");
+        }
+    }
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java
index 34c8a9160b7..24d7c9b3fc0 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/PropertyType.java
@@ -21,6 +21,9 @@ import java.io.Serializable;
 import com.fasterxml.jackson.annotation.JsonProperty;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularyFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.exceptions.NotFetchedException;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
 /**
@@ -45,7 +48,13 @@ public class PropertyType implements ICodeHolder, Serializable
 
     @JsonProperty
     private boolean internalNameSpace;
+    
+    @JsonProperty
+    private VocabularyFetchOptions vocabularyFetchOptions;
 
+    @JsonProperty
+    private Vocabulary vocabulary;
+    
     @Override
     public String getCode()
     {
@@ -97,4 +106,30 @@ public class PropertyType implements ICodeHolder, Serializable
         this.internalNameSpace = internalNameSpace;
     }
 
+    public VocabularyFetchOptions getVocabularyFetchOptions()
+    {
+        return vocabularyFetchOptions;
+    }
+
+    public void setVocabularyFetchOptions(VocabularyFetchOptions fetchOptions)
+    {
+        this.vocabularyFetchOptions = fetchOptions;
+    }
+    
+    public Vocabulary getVocabulary()
+    {
+        if (getVocabularyFetchOptions() != null)
+        {
+            return vocabulary;
+        }
+        else
+        {
+            throw new NotFetchedException("Vocabulary has not been fetched.");
+        }
+    }
+    
+    public void setVocabulary(Vocabulary vocabulary)
+    {
+        this.vocabulary = vocabulary;
+    }
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java
index 425ea6d05ee..5dffe1ee00d 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/property/fetchoptions/PropertyAssignmentFetchOptions.java
@@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
 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.property.PropertyAssignment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularyFetchOptions;
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
 /**
@@ -32,9 +33,31 @@ public class PropertyAssignmentFetchOptions extends FetchOptions<PropertyAssignm
 
     private static final long serialVersionUID = 1L;
 
+    @JsonProperty
+    private VocabularyFetchOptions vocabulary;
+    
     @JsonProperty
     private PropertyAssignmentSortOptions sort;
 
+    public VocabularyFetchOptions withVocabulary()
+    {
+        if (vocabulary == null)
+        {
+            vocabulary = new VocabularyFetchOptions();
+        }
+        return vocabulary;
+    }
+
+    public VocabularyFetchOptions withVocabularyUsing(VocabularyFetchOptions fetchOptions)
+    {
+        return vocabulary = fetchOptions;
+    }
+
+    public boolean hasVocabulary()
+    {
+        return vocabulary != null;
+    }
+
     @Override
     public PropertyAssignmentSortOptions sortBy()
     {
diff --git a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
index 0b8f012fc5f..40a1ac1b63e 100644
--- a/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
+++ b/openbis_api/sourceTest/java/ch/ethz/sis/openbis/generic/sharedapi/v3/dictionary.txt
@@ -1138,4 +1138,7 @@ Listable Sample Type Search Criteria
 is Listable
 Sample Type Search Criteria
 with Listable
-set Listable
\ No newline at end of file
+set Listable
+
+get Vocabulary Fetch Options
+set Vocabulary Fetch Options
-- 
GitLab