Repository: Wikidata/Wikidata-Toolkit (java)

Issue:
DatamodelConverter is missing support for some entity types
This should also have `MediaInfoIdValue` and others (lexeme, form, sense):
https://github.com/Wikidata/Wikidata-Toolkit/blob/6754af679e90480cae596b733f28d2062cc04ebf/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverter.java#L418-L427

Pull request:
Add support for missing entity types in DatamodelConverter
Closes #627.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1be4a884..45085619 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -13,7 +13,7 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        java: [ 8, 11 ]
+        java: [ 8, 11, 17 ]
 
     steps:
       - uses: actions/checkout@v2
diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverter.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverter.java
index 8e1a8c73..caeca8e8 100644
--- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverter.java
+++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverter.java
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.stream.Collectors;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,16 +33,23 @@ import org.wikidata.wdtk.datamodel.interfaces.Claim;
 import org.wikidata.wdtk.datamodel.interfaces.DataObjectFactory;
 import org.wikidata.wdtk.datamodel.interfaces.DatatypeIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
+import org.wikidata.wdtk.datamodel.interfaces.FormDocument;
+import org.wikidata.wdtk.datamodel.interfaces.FormIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.GlobeCoordinatesValue;
 import org.wikidata.wdtk.datamodel.interfaces.ItemDocument;
 import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
+import org.wikidata.wdtk.datamodel.interfaces.LexemeDocument;
 import org.wikidata.wdtk.datamodel.interfaces.LexemeIdValue;
+import org.wikidata.wdtk.datamodel.interfaces.MediaInfoDocument;
+import org.wikidata.wdtk.datamodel.interfaces.MediaInfoIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue;
 import org.wikidata.wdtk.datamodel.interfaces.NoValueSnak;
 import org.wikidata.wdtk.datamodel.interfaces.PropertyDocument;
 import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.QuantityValue;
 import org.wikidata.wdtk.datamodel.interfaces.Reference;
+import org.wikidata.wdtk.datamodel.interfaces.SenseDocument;
+import org.wikidata.wdtk.datamodel.interfaces.SenseIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.SiteLink;
 import org.wikidata.wdtk.datamodel.interfaces.Snak;
 import org.wikidata.wdtk.datamodel.interfaces.SnakGroup;
@@ -120,6 +128,39 @@ public class DatamodelConverter implements SnakVisitor<Snak>,
 	public LexemeIdValue copy(LexemeIdValue object) {
 		return dataObjectFactory.getLexemeIdValue(object.getId(), object.getSiteIri());
 	}
+	
+	/**
+     * Copies a {@link FormIdValue}.
+     *
+     * @param object
+     *            object to copy
+     * @return copied object
+     */
+    public FormIdValue copy(FormIdValue object) {
+        return dataObjectFactory.getFormIdValue(object.getId(), object.getSiteIri());
+    }
+    
+    /**
+     * Copies a {@link SenseIdValue}.
+     *
+     * @param object
+     *            object to copy
+     * @return copied object
+     */
+    public SenseIdValue copy(SenseIdValue object) {
+        return dataObjectFactory.getSenseIdValue(object.getId(), object.getSiteIri());
+    }
+    
+    /**
+     * Copies a {@link MediaInfoIdValue}.
+     *
+     * @param object
+     *            object to copy
+     * @return copied object
+     */
+    public MediaInfoIdValue copy(MediaInfoIdValue object) {
+        return dataObjectFactory.getMediaInfoIdValue(object.getId(), object.getSiteIri());
+    }
 
 	/**
 	 * Copies a {@link DatatypeIdValue}.
@@ -376,6 +417,74 @@ public class DatamodelConverter implements SnakVisitor<Snak>,
 				copySiteLinks(object.getSiteLinks()),
 				object.getRevisionId());
 	}
+	
+	/**
+	 * Copies a {@link MediaInfoDocument}.
+	 * 
+	 * @param object
+	 *            object to copy
+	 * @return copied object
+	 */
+	public MediaInfoDocument copy(MediaInfoDocument object) {
+	    return dataObjectFactory.getMediaInfoDocument(
+	            copy(object.getEntityId()),
+	            copyMonoLingualTextValues(object.getLabels().values()),
+	            copyStatementGroups(object.getStatementGroups()),
+	            object.getRevisionId());
+	}
+	
+	/**
+     * Copies a {@link LexemeDocument}.
+     * 
+     * @param object
+     *            object to copy
+     * @return copied object
+     */
+    public LexemeDocument copy(LexemeDocument object) {
+        return dataObjectFactory.getLexemeDocument(
+                copy(object.getEntityId()),
+                copy(object.getLexicalCategory()),
+                copy(object.getLanguage()),
+                copyMonoLingualTextValues(object.getLemmas().values()),
+                copyStatementGroups(object.getStatementGroups()),
+                copyFormDocuments(object.getForms()),
+                copySenseDocuments(object.getSenses()),
+                object.getRevisionId());
+    }
+    
+    /**
+     * Copies a {@link FormDocument}.
+     * 
+     * @param object
+     *            object to copy
+     * @return copied object
+     */
+    public FormDocument copy(FormDocument object) {
+        return dataObjectFactory.getFormDocument(
+                copy(object.getEntityId()),
+                copyMonoLingualTextValues(object.getRepresentations().values()),
+                copyItemIds(object.getGrammaticalFeatures()),
+                copyStatementGroups(object.getStatementGroups()),
+                object.getRevisionId());
+    }
+    
+
+    /**
+     * Copies a {@link SenseDocument}.
+     * 
+     * @param object
+     *            object to copy
+     * @return copied object
+     */
+    public SenseDocument copy(SenseDocument object) {
+        return dataObjectFactory.getSenseDocument(
+                copy(object.getEntityId()),
+                copyMonoLingualTextValues(object.getGlosses().values()),
+                copyStatementGroups(object.getStatementGroups()),
+                object.getRevisionId());
+    }
+    
+    
 
 	/**
 	 * Copies a {@link Snak}.
@@ -420,6 +529,14 @@ public class DatamodelConverter implements SnakVisitor<Snak>,
 			return copy((ItemIdValue) value);
 		} else if (value instanceof PropertyIdValue) {
 			return copy((PropertyIdValue) value);
+        } else if (value instanceof LexemeIdValue) {
+            return copy((LexemeIdValue) value);
+        } else if (value instanceof FormIdValue) {
+            return copy((FormIdValue) value);
+        } else if (value instanceof SenseIdValue) {
+            return copy((SenseIdValue) value);
+        } else if (value instanceof MediaInfoIdValue) {
+            return copy((MediaInfoIdValue) value);
 		} else {
 			throw new UnsupportedOperationException(
 					"Cannot convert entity id value: " + value.getClass());
@@ -553,5 +670,42 @@ public class DatamodelConverter implements SnakVisitor<Snak>,
 		}
 		return result;
 	}
+	
+	/**
+	 * Copies a list of item ids.
+	 * 
+	 * @param ids
+	 * @return
+	 */
+    private List<ItemIdValue> copyItemIds(List<ItemIdValue> ids) {
+        return ids.stream()
+                .map(id -> copy(id))
+                .collect(Collectors.toList());
+    }
+    
+    /**
+     * Copies a list of sense documents.
+     * 
+     * @param senses
+     * @return
+     */
+    private List<SenseDocument> copySenseDocuments(List<SenseDocument> senses) {
+        return senses.stream()
+                .map(sense -> copy(sense))
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * Copies a list of form documents.
+     * 
+     * @param forms
+     * @return
+     */
+    private List<FormDocument> copyFormDocuments(List<FormDocument> forms) {
+        return forms.stream()
+                .map(form -> copy(form))
+                .collect(Collectors.toList());
+    }
+
 
 }


Test patch:
diff --git a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverterTest.java b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverterTest.java
index e812d9a0..3bdc2738 100644
--- a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverterTest.java
+++ b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/helpers/DatamodelConverterTest.java
@@ -41,12 +41,20 @@ import org.wikidata.wdtk.datamodel.implementation.StringValueImpl;
 import org.wikidata.wdtk.datamodel.implementation.TimeValueImpl;
 import org.wikidata.wdtk.datamodel.implementation.ValueSnakImpl;
 import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue;
+import org.wikidata.wdtk.datamodel.interfaces.FormDocument;
+import org.wikidata.wdtk.datamodel.interfaces.FormIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.GlobeCoordinatesValue;
 import org.wikidata.wdtk.datamodel.interfaces.ItemDocument;
 import org.wikidata.wdtk.datamodel.interfaces.ItemIdValue;
+import org.wikidata.wdtk.datamodel.interfaces.LexemeDocument;
+import org.wikidata.wdtk.datamodel.interfaces.LexemeIdValue;
+import org.wikidata.wdtk.datamodel.interfaces.MediaInfoDocument;
+import org.wikidata.wdtk.datamodel.interfaces.MediaInfoIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue;
 import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.QuantityValue;
+import org.wikidata.wdtk.datamodel.interfaces.SenseDocument;
+import org.wikidata.wdtk.datamodel.interfaces.SenseIdValue;
 import org.wikidata.wdtk.datamodel.interfaces.Snak;
 import org.wikidata.wdtk.datamodel.interfaces.SnakGroup;
 import org.wikidata.wdtk.datamodel.interfaces.Statement;
@@ -217,7 +225,78 @@ public class DatamodelConverterTest {
 		DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
 		assertEquals(item, converter.copy(item));
 	}
-
+	
+	
+    @Test
+    public void testCopyMediaInfoIdValue() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        MediaInfoIdValue mediaInfo = getTestMediaInfoIdValue(34);
+        assertEquals(mediaInfo, converter.copy(mediaInfo));
+    }
+    
+    @Test
+    public void testCopyLexemeIdValue() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        LexemeIdValue lexeme = getTestLexemeIdValue(45);
+        assertEquals(lexeme, converter.copy(lexeme));
+    }
+    
+    @Test
+    public void testCopyFormIdValue() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        FormIdValue form = getTestFormIdValue(56);
+        assertEquals(form, converter.copy(form));
+    }
+    
+    @Test
+    public void testCopySenseIdValue() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        SenseIdValue sense = getTestSenseIdValue(56);
+        assertEquals(sense, converter.copy(sense));
+    }
+    
+    @Test
+    public void testMediaInfoDocument() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        MediaInfoDocument document = Datamodel.makeMediaInfoDocument(
+                getTestMediaInfoIdValue(78),
+                Collections.singletonList(Datamodel.makeMonolingualTextValue("en", "label")),
+                Collections.emptyList());
+        assertEquals(document, converter.copy(document));
+    }
+    
+    @Test
+    public void testLexemeDocument() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        LexemeDocument document = Datamodel.makeLexemeDocument(
+                getTestLexemeIdValue(90),
+                getTestItemIdValue(38),
+                getTestItemIdValue(39),
+                Collections.singletonList(Datamodel.makeMonolingualTextValue("en", "lemma")));
+        assertEquals(document, converter.copy(document));
+    }
+    
+    @Test
+    public void testFormDocument() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        FormDocument document = Datamodel.makeFormDocument(
+                getTestFormIdValue(92),
+                Collections.emptyList(),
+                Collections.emptyList(),
+                Collections.emptyList());
+        assertEquals(document, converter.copy(document));
+    }
+    
+    @Test
+    public void testSenseDocument() {
+        DatamodelConverter converter = new DatamodelConverter(new DataObjectFactoryImpl());
+        SenseDocument document = Datamodel.makeSenseDocument(
+                getTestSenseIdValue(738),
+                Collections.singletonList(Datamodel.makeMonolingualTextValue("en", "gloss")),
+                Collections.emptyList());
+        assertEquals(document, converter.copy(document));
+    }
+	
 	public enum ValueType {
 		STRING, ITEM, GLOBE_COORDINATES, TIME, QUANTITY, MONOLINGUAL_TEXT;
 
@@ -247,6 +326,22 @@ public class DatamodelConverterTest {
 	private PropertyIdValue getTestPropertyIdValue(int seed) {
 		return new PropertyIdValueImpl("P4" + seed, "foo:");
 	}
+	
+	private MediaInfoIdValue getTestMediaInfoIdValue(int seed) {
+        return Datamodel.makeMediaInfoIdValue("M4" + seed, "foo:");
+    }
+
+    private LexemeIdValue getTestLexemeIdValue(int seed) {
+        return Datamodel.makeLexemeIdValue("L4" + seed, "foo:");
+    }
+    
+    private FormIdValue getTestFormIdValue(int seed) {
+        return Datamodel.makeFormIdValue("L4" + seed + "-F1", "foo:");
+    }
+    
+    private SenseIdValue getTestSenseIdValue(int seed) {
+        return Datamodel.makeSenseIdValue("L4" + seed + "-S1", "foo:");
+    }
 
 	private EntityIdValue getTestEntityIdValue(int seed, String entityType) {
 		switch (entityType) {