Repository: Wikidata/Wikidata-Toolkit
Pull request #453 (2019-10-21 14:24:07)

Adds some modification methods to Lexeme, Form and Sense
Adds some easy modifications methods for lexemes.

Still to do: adds and remove forms and senses


Patch:
diff --git a/.travis.yml b/.travis.yml
index b484b6da..84ca2405 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,9 +1,10 @@
 language: java
 dist: xenial
-sudo: false
 jdk:
   - openjdk8
-  - openjdk11
+  - oraclejdk8
+  - oraclejdk9
+  - oraclejdk11
 
 script:
   - mvn test jacoco:report
@@ -11,6 +12,9 @@ script:
 after_success:
   - mvn coveralls:report
 
+dist: trusty
+sudo: false
+
 cache:
   directories:
     - $HOME/.m2
diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImpl.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImpl.java
index f4791760..cfbd703d 100644
--- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImpl.java
+++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImpl.java
@@ -39,9 +39,9 @@ import java.util.*;
 @JsonIgnoreProperties(ignoreUnknown = true)
 public class FormDocumentImpl extends StatementDocumentImpl implements FormDocument {
 
-	private List<ItemIdValue> grammaticalFeatures;
+	private final List<ItemIdValue> grammaticalFeatures;
 
-	private Map<String,MonolingualTextValue> representations;
+	private final Map<String,MonolingualTextValue> representations;
 
 	/**
 	 * Constructor.
@@ -98,10 +98,9 @@ public class FormDocumentImpl extends StatementDocumentImpl implements FormDocum
 				? Collections.emptyList()
 				: constructGrammaticalFeatures(grammaticalFeatures, siteIri);
 	}
-	
+
 	/**
-	 * Copy constructor. Does not perform any checks on
-	 * its values.
+	 * Copy constructor, used when creating modified copies of forms.
 	 */
 	private FormDocumentImpl(
 			FormIdValue id,
@@ -186,6 +185,30 @@ public class FormDocumentImpl extends StatementDocumentImpl implements FormDocum
 		return ToString.toString(this);
 	}
 	
+	@Override
+	public FormDocument withRevisionId(long newRevisionId) {
+		return new FormDocumentImpl(getEntityId(),
+				representations, grammaticalFeatures,
+				claims, newRevisionId);
+	}
+
+	@Override
+	public FormDocument withRepresentation(MonolingualTextValue representation) {
+		Map<String, MonolingualTextValue> newRepresentations = new HashMap<>(representations);
+		newRepresentations.put(representation.getLanguageCode(), representation);
+		return new FormDocumentImpl(getEntityId(), newRepresentations, grammaticalFeatures, claims, revisionId);
+	}
+
+	@Override
+	public FormDocument withGrammaticalFeature(ItemIdValue grammaticalFeature) {
+		if (grammaticalFeatures.contains(grammaticalFeature)) {
+			return this;
+		}
+		List<ItemIdValue> newGrammaticalFeatures = new ArrayList<>(grammaticalFeatures);
+		newGrammaticalFeatures.add(grammaticalFeature);
+		return new FormDocumentImpl(getEntityId(), representations, newGrammaticalFeatures, claims, revisionId);
+	}
+
 	@Override
 	public FormDocument withStatement(Statement statement) {
 		Map<String, List<Statement>> newGroups = addStatementToGroups(statement, claims);
@@ -201,11 +224,4 @@ public class FormDocumentImpl extends StatementDocumentImpl implements FormDocum
 				representations, grammaticalFeatures,
 				newGroups, revisionId);
 	}
-	
-	@Override
-	public FormDocument withRevisionId(long newRevisionId) {
-		return new FormDocumentImpl(getEntityId(),
-				representations, grammaticalFeatures,
-				claims, newRevisionId);
-	}
 }
diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImpl.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImpl.java
index 008aa23f..4050839b 100644
--- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImpl.java
+++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImpl.java
@@ -42,15 +42,15 @@ import java.util.Set;
 @JsonIgnoreProperties(ignoreUnknown = true)
 public class LexemeDocumentImpl extends StatementDocumentImpl implements LexemeDocument {
 
-	private ItemIdValue lexicalCategory;
+	private final ItemIdValue lexicalCategory;
 
-	private ItemIdValue language;
+	private final ItemIdValue language;
 
-	private Map<String,MonolingualTextValue> lemmas;
+	private final Map<String,MonolingualTextValue> lemmas;
 
-	private List<FormDocument> forms;
+	private final List<FormDocument> forms;
 
-	private List<SenseDocument> senses;
+	private final List<SenseDocument> senses;
 
 	/**
 	 * Constructor.
@@ -137,12 +137,14 @@ public class LexemeDocumentImpl extends StatementDocumentImpl implements LexemeD
 			Map<String, MonolingualTextValue> lemmas,
 			Map<String, List<Statement>> statements,
 			List<FormDocument> forms,
+			List<SenseDocument> senses,
 			long revisionId) {
 		super(id, statements, revisionId);
 		this.lexicalCategory = lexicalCategory;
 		this.language = language;
 		this.lemmas = lemmas;
 		this.forms = forms;
+		this.senses = senses;
 	}
 
 	private static Map<String, MonolingualTextValue> constructTermMap(List<MonolingualTextValue> terms) {
@@ -242,35 +244,44 @@ public class LexemeDocumentImpl extends StatementDocumentImpl implements LexemeD
 	public String toString() {
 		return ToString.toString(this);
 	}
-	/*
-	 * LexemeIdValue id,
-			ItemIdValue lexicalCategory,
-			ItemIdValue language,
-			Map<String, MonolingualTextValue> lemmas,
-			Map<String, List<Statement>> statements,
-			List<FormDocument> forms,
-			long revisionId
-			(non-Javadoc)
-	 * @see org.wikidata.wdtk.datamodel.interfaces.StatementDocument#withStatement(org.wikidata.wdtk.datamodel.interfaces.Statement)
-	 */
+
+	@Override
+	public LexemeDocument withLexicalCategory(ItemIdValue newLexicalCategory) {
+		return new LexemeDocumentImpl(getEntityId(), newLexicalCategory,
+				language, lemmas, claims, forms, senses, revisionId);
+	}
+
+	@Override
+	public LexemeDocument withLanguage(ItemIdValue newLanguage) {
+		return new LexemeDocumentImpl(getEntityId(), lexicalCategory,
+				newLanguage, lemmas, claims, forms, senses, revisionId);
+	}
+
+	@Override
+	public LexemeDocument withLemma(MonolingualTextValue lemma) {
+		Map<String, MonolingualTextValue> newLemmas = new HashMap<>(lemmas);
+		newLemmas.put(lemma.getLanguageCode(), lemma);
+		return new LexemeDocumentImpl(getEntityId(), lexicalCategory,
+				language, newLemmas, claims, forms, senses, revisionId);
+	}
 	
 	@Override
 	public LexemeDocument withStatement(Statement statement) {
 		Map<String, List<Statement>> newGroups = addStatementToGroups(statement, claims);
 		return new LexemeDocumentImpl(getEntityId(), lexicalCategory,
-				language, lemmas, newGroups, forms, revisionId);
+				language, lemmas, newGroups, forms, senses, revisionId);
 	}
 
 	@Override
 	public LexemeDocument withoutStatementIds(Set<String> statementIds) {
 		Map<String, List<Statement>> newGroups = removeStatements(statementIds, claims);
 		return new LexemeDocumentImpl(getEntityId(), lexicalCategory,
-				language, lemmas, newGroups, forms, revisionId);
+				language, lemmas, newGroups, forms, senses, revisionId);
 	}
 	
 	@Override
 	public LexemeDocument withRevisionId(long newRevisionId) {
 		return new LexemeDocumentImpl(getEntityId(), lexicalCategory,
-				language, lemmas, claims, forms, newRevisionId);
+				language, lemmas, claims, forms, senses, newRevisionId);
 	}
 }
diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImpl.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImpl.java
index 4ba4af69..ef8c8179 100644
--- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImpl.java
+++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImpl.java
@@ -42,7 +42,7 @@ import java.util.Set;
 @JsonIgnoreProperties(ignoreUnknown = true)
 public class SenseDocumentImpl extends StatementDocumentImpl implements SenseDocument {
 
-	private Map<String,MonolingualTextValue> glosses;
+	private final Map<String,MonolingualTextValue> glosses;
 
 	/**
 	 * Constructor.
@@ -90,15 +90,14 @@ public class SenseDocumentImpl extends StatementDocumentImpl implements SenseDoc
 		}
 		this.glosses = glosses;
 	}
-	
+
 	/**
-	 * Protected constructor, meant to be used to create modified copies
-	 * of instances.
+	 * Copy constructor, used when creating modified copies of senses.
 	 */
-	protected SenseDocumentImpl(
+	private SenseDocumentImpl(
 			SenseIdValue subject,
-			Map<String,MonolingualTextValue> glosses,
-			Map<String, List<Statement>> claims, 
+			Map<String, MonolingualTextValue> glosses,
+			Map<String, List<Statement>> claims,
 			long revisionId) {
 		super(subject, claims, revisionId);
 		this.glosses = glosses;
@@ -158,6 +157,13 @@ public class SenseDocumentImpl extends StatementDocumentImpl implements SenseDoc
 				newRevisionId);
 	}
 
+	@Override
+	public SenseDocument withGloss(MonolingualTextValue gloss) {
+		Map<String, MonolingualTextValue> newGlosses = new HashMap<>(glosses);
+		newGlosses.put(gloss.getLanguageCode(), gloss);
+		return new SenseDocumentImpl(getEntityId(), newGlosses, claims, revisionId);
+	}
+
 	@Override
 	public SenseDocument withStatement(Statement statement) {
 		return new SenseDocumentImpl(getEntityId(),
diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/FormDocument.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/FormDocument.java
index 6b75f975..099443e0 100644
--- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/FormDocument.java
+++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/FormDocument.java
@@ -22,6 +22,7 @@ package org.wikidata.wdtk.datamodel.interfaces;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Interface for lexemes forms.
@@ -52,4 +53,39 @@ public interface FormDocument extends StatementDocument {
 	 * @return item ids
 	 */
 	List<ItemIdValue> getGrammaticalFeatures();
+
+	/**
+	 * Returns a copy of this document with an updated revision id.
+	 */
+	@Override
+	FormDocument withRevisionId(long newRevisionId);
+
+	FormDocument withRepresentation(MonolingualTextValue representation);
+
+	FormDocument withGrammaticalFeature(ItemIdValue grammaticalFeature);
+
+	/**
+	 * Returns a new version of this document which includes the
+	 * statement provided. If the identifier of this statement matches
+	 * that of any other statement for the same property, then the
+	 * existing statement will be replaced by the new one. Otherwise,
+	 * the new statement will be added at the end of the list of statements
+	 * in this group.
+	 *
+	 * @param statement
+	 * 		the statement to add or update in the document
+	 */
+	@Override
+	FormDocument withStatement(Statement statement);
+
+	/**
+	 * Returns a new version of this document where all statements matching
+	 * any of the statement ids provided have been removed. These statements
+	 * can use different properties.
+	 *
+	 * @param statementIds
+	 *       the identifiers of the statements to remove
+	 */
+	@Override
+	FormDocument withoutStatementIds(Set<String> statementIds);
 }
diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/LexemeDocument.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/LexemeDocument.java
index 869e7cb9..d9de6d36 100644
--- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/LexemeDocument.java
+++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/LexemeDocument.java
@@ -22,6 +22,7 @@ package org.wikidata.wdtk.datamodel.interfaces;
 
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Interface for datasets that describe lexemes.
@@ -89,4 +90,41 @@ public interface LexemeDocument extends StatementDocument {
 	 * @throws IndexOutOfBoundsException if there is no sense with this id in the document
 	 */
 	SenseDocument getSense(SenseIdValue formId);
+
+	/**
+	 * Returns a copy of this document with an updated revision id.
+	 */
+	@Override
+	LexemeDocument withRevisionId(long newRevisionId);
+
+	LexemeDocument withLexicalCategory(ItemIdValue newLexicalCategory);
+
+	LexemeDocument withLanguage(ItemIdValue newLanguage);
+
+	LexemeDocument withLemma(MonolingualTextValue lemma);
+
+	/**
+	 * Returns a new version of this document which includes the
+	 * statement provided. If the identifier of this statement matches
+	 * that of any other statement for the same property, then the
+	 * existing statement will be replaced by the new one. Otherwise,
+	 * the new statement will be added at the end of the list of statements
+	 * in this group.
+	 *
+	 * @param statement
+	 * 		the statement to add or update in the document
+	 */
+	@Override
+	LexemeDocument withStatement(Statement statement);
+
+	/**
+	 * Returns a new version of this document where all statements matching
+	 * any of the statement ids provided have been removed. These statements
+	 * can use different properties.
+	 *
+	 * @param statementIds
+	 *       the identifiers of the statements to remove
+	 */
+	@Override
+	LexemeDocument withoutStatementIds(Set<String> statementIds);
 }
diff --git a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/SenseDocument.java b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/SenseDocument.java
index 990a861b..13f55638 100644
--- a/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/SenseDocument.java
+++ b/wdtk-datamodel/src/main/java/org/wikidata/wdtk/datamodel/interfaces/SenseDocument.java
@@ -51,6 +51,8 @@ public interface SenseDocument extends StatementDocument {
 	 */
 	@Override
 	SenseDocument withRevisionId(long newRevisionId);
+
+	SenseDocument withGloss(MonolingualTextValue gloss);
 	
 	/**
 	 * Returns a new version of this document which includes the


Test patch:
diff --git a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImplTest.java b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImplTest.java
index f9019855..71c57fc0 100644
--- a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImplTest.java
+++ b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/FormDocumentImplTest.java
@@ -49,17 +49,17 @@ public class FormDocumentImplTest {
 	private final MonolingualTextValue rep = new TermImpl("en", "rep");
 	private final List<MonolingualTextValue> repList = Collections.singletonList(rep);
 
-	private final FormDocument ld1 = new FormDocumentImpl(fid, repList, gramFeatures, statementGroups, 1234);
-	private final FormDocument ld2 = new FormDocumentImpl(fid, repList, gramFeatures, statementGroups, 1234);
+	private final FormDocument fd1 = new FormDocumentImpl(fid, repList, gramFeatures, statementGroups, 1234);
+	private final FormDocument fd2 = new FormDocumentImpl(fid, repList, gramFeatures, statementGroups, 1234);
 
 	private final String JSON_FORM = "{\"type\":\"form\",\"id\":\"L42-F1\",\"grammaticalFeatures\":[\"Q1\",\"Q2\"],\"representations\":{\"en\":{\"language\":\"en\",\"value\":\"rep\"}},\"claims\":{\"P42\":[{\"rank\":\"normal\",\"id\":\"MyId\",\"mainsnak\":{\"property\":\"P42\",\"snaktype\":\"somevalue\"},\"type\":\"statement\"}]},\"lastrevid\":1234}";
 
 	@Test
 	public void fieldsAreCorrect() {
-		assertEquals(ld1.getEntityId(), fid);
-		assertEquals(ld1.getRepresentations(), Collections.singletonMap(rep.getLanguageCode(), rep));
-		assertEquals(ld1.getGrammaticalFeatures(), gramFeatures);
-		assertEquals(ld1.getStatementGroups(), statementGroups);
+		assertEquals(fd1.getEntityId(), fid);
+		assertEquals(fd1.getRepresentations(), Collections.singletonMap(rep.getLanguageCode(), rep));
+		assertEquals(fd1.getGrammaticalFeatures(), gramFeatures);
+		assertEquals(fd1.getStatementGroups(), statementGroups);
 	}
 
 	@Test
@@ -77,21 +77,21 @@ public class FormDocumentImplTest {
 				new FormIdValueImpl("L42-F2", "http://example.com/entity/"),
 				repList, gramFeatures, Collections.emptyList(), 1235);
 
-		assertEquals(ld1, ld1);
-		assertEquals(ld1, ld2);
-		assertNotEquals(ld1, irDiffRepresentations);
-		assertNotEquals(ld1, irDiffGramFeatures);
-		assertNotEquals(ld1, irDiffStatementGroups);
-		assertNotEquals(ld1, irDiffRevisions);
+		assertEquals(fd1, fd1);
+		assertEquals(fd1, fd2);
+		assertNotEquals(fd1, irDiffRepresentations);
+		assertNotEquals(fd1, irDiffGramFeatures);
+		assertNotEquals(fd1, irDiffStatementGroups);
+		assertNotEquals(fd1, irDiffRevisions);
 		assertNotEquals(irDiffStatementGroups, irDiffFormIdValue);
-		assertNotEquals(ld1, pr);
-		assertNotEquals(ld1, null);
-		assertNotEquals(ld1, this);
+		assertNotEquals(fd1, pr);
+		assertNotEquals(fd1, null);
+		assertNotEquals(fd1, this);
 	}
 
 	@Test
 	public void hashBasedOnContent() {
-		assertEquals(ld1.hashCode(), ld2.hashCode());
+		assertEquals(fd1.hashCode(), fd2.hashCode());
 	}
 
 	@Test(expected = NullPointerException.class)
@@ -138,21 +138,71 @@ public class FormDocumentImplTest {
 
 	@Test
 	public void iterateOverAllStatements() {
-		Iterator<Statement> statements = ld1.getAllStatements();
+		Iterator<Statement> statements = fd1.getAllStatements();
 
 		assertTrue(statements.hasNext());
 		assertEquals(s, statements.next());
 		assertFalse(statements.hasNext());
 	}
 
+	@Test
+	public void testWithRevisionId() {
+		assertEquals(1235L, fd1.withRevisionId(1235L).getRevisionId());
+		assertEquals(fd1, fd1.withRevisionId(1325L).withRevisionId(fd1.getRevisionId()));
+	}
+
+	@Test
+	public void testWithRepresentationInNewLanguage() {
+		MonolingualTextValue newRepresentation = new MonolingualTextValueImpl("Foo", "fr");
+		FormDocument withRepresentation = fd1.withRepresentation(newRepresentation);
+		assertEquals(newRepresentation, withRepresentation.getRepresentations().get("fr"));
+	}
+
+	@Test
+	public void testWithNewGrammaticalFeatures() {
+		ItemIdValue newGrammaticalFeature = new ItemIdValueImpl("Q3", "http://example.com/entity/");
+		FormDocument withGrammaticalFeature = fd1.withGrammaticalFeature(newGrammaticalFeature);
+		assertTrue(withGrammaticalFeature.getGrammaticalFeatures().containsAll(gramFeatures));
+		assertTrue(withGrammaticalFeature.getGrammaticalFeatures().contains(newGrammaticalFeature));
+	}
+
+	@Test
+	public void testWithExistingGrammaticalFeatures() {
+		ItemIdValue newGrammaticalFeature = new ItemIdValueImpl("Q2", "http://example.com/entity/");
+		FormDocument withGrammaticalFeature = fd1.withGrammaticalFeature(newGrammaticalFeature);
+		assertEquals(fd1, withGrammaticalFeature);
+	}
+
+	@Test
+	public void testAddStatement() {
+		Statement fresh = new StatementImpl("MyFreshId", StatementRank.NORMAL,
+				new SomeValueSnakImpl(new PropertyIdValueImpl("P29", "http://example.com/entity/")),
+				Collections.emptyList(), Collections.emptyList(), fid);
+		Claim claim = fresh.getClaim();
+		assertFalse(fd1.hasStatementValue(
+				claim.getMainSnak().getPropertyId(),
+				claim.getValue()));
+		FormDocument withStatement = fd1.withStatement(fresh);
+		assertTrue(withStatement.hasStatementValue(
+				claim.getMainSnak().getPropertyId(),
+				claim.getValue()));
+	}
+
+	@Test
+	public void testDeleteStatements() {
+		Statement toRemove = statementGroups.get(0).getStatements().get(0);
+		FormDocument withoutStatement = fd1.withoutStatementIds(Collections.singleton(toRemove.getStatementId()));
+		assertNotEquals(withoutStatement, fd1);
+	}
+
 	@Test
 	public void testFormToJson() throws JsonProcessingException {
-		JsonComparator.compareJsonStrings(JSON_FORM, mapper.writeValueAsString(ld1));
+		JsonComparator.compareJsonStrings(JSON_FORM, mapper.writeValueAsString(fd1));
 	}
 
 	@Test
 	public void testFormToJava() throws IOException {
-		assertEquals(ld1, mapper.readValue(JSON_FORM, FormDocumentImpl.class));
+		assertEquals(fd1, mapper.readValue(JSON_FORM, FormDocumentImpl.class));
 	}
 
 }
diff --git a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImplTest.java b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImplTest.java
index 5e02bd3c..dba92114 100644
--- a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImplTest.java
+++ b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/LexemeDocumentImplTest.java
@@ -205,6 +205,41 @@ public class LexemeDocumentImplTest {
 		assertTrue(doc.getSenses().isEmpty());
 	}
 
+	@Test
+	public void testWithRevisionId() {
+		assertEquals(1235L, ld1.withRevisionId(1235L).getRevisionId());
+		assertEquals(ld1, ld1.withRevisionId(1325L).withRevisionId(ld1.getRevisionId()));
+	}
+
+	@Test
+	public void testWithLemmaInNewLanguage() {
+		MonolingualTextValue newLemma = new MonolingualTextValueImpl("Foo", "fr");
+		LexemeDocument withLemma = ld1.withLemma(newLemma);
+		assertEquals(newLemma, withLemma.getLemmas().get("fr"));
+	}
+
+	@Test
+	public void testAddStatement() {
+		Statement fresh = new StatementImpl("MyFreshId", StatementRank.NORMAL,
+				new SomeValueSnakImpl(new PropertyIdValueImpl("P29", "http://example.com/entity/")),
+				Collections.emptyList(), Collections.emptyList(), lid);
+		Claim claim = fresh.getClaim();
+		assertFalse(ld1.hasStatementValue(
+				claim.getMainSnak().getPropertyId(),
+				claim.getValue()));
+		LexemeDocument withStatement = ld1.withStatement(fresh);
+		assertTrue(withStatement.hasStatementValue(
+				claim.getMainSnak().getPropertyId(),
+				claim.getValue()));
+	}
+
+	@Test
+	public void testDeleteStatements() {
+		Statement toRemove = statementGroups.get(0).getStatements().get(0);
+		LexemeDocument withoutStatement = ld1.withoutStatementIds(Collections.singleton(toRemove.getStatementId()));
+		assertNotEquals(withoutStatement, ld1);
+	}
+
 	@Test
 	public void testLexemeToJson() throws JsonProcessingException {
 		JsonComparator.compareJsonStrings(JSON_LEXEME, mapper.writeValueAsString(ld1));
diff --git a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImplTest.java b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImplTest.java
index 32c04113..b92a96d2 100644
--- a/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImplTest.java
+++ b/wdtk-datamodel/src/test/java/org/wikidata/wdtk/datamodel/implementation/SenseDocumentImplTest.java
@@ -35,33 +35,33 @@ public class SenseDocumentImplTest {
 
 	private final ObjectMapper mapper = new DatamodelMapper("http://example.com/entity/");
 
-	private final SenseIdValue fid = new SenseIdValueImpl("L42-S1", "http://example.com/entity/");
+	private final SenseIdValue sid = new SenseIdValueImpl("L42-S1", "http://example.com/entity/");
 	private final Statement s = new StatementImpl("MyId", StatementRank.NORMAL,
 			new SomeValueSnakImpl(new PropertyIdValueImpl("P42", "http://example.com/entity/")),
-			Collections.emptyList(), Collections.emptyList(), fid);
+			Collections.emptyList(), Collections.emptyList(), sid);
 	private final List<StatementGroup> statementGroups = Collections.singletonList(
 			new StatementGroupImpl(Collections.singletonList(s))
 	);
 	private final MonolingualTextValue rep = new TermImpl("en", "rep");
 	private final List<MonolingualTextValue> repList = Collections.singletonList(rep);
 
-	private final SenseDocument ld1 = new SenseDocumentImpl(fid, repList, statementGroups, 1234);
-	private final SenseDocument ld2 = new SenseDocumentImpl(fid, repList, statementGroups, 1234);
+	private final SenseDocument sd1 = new SenseDocumentImpl(sid, repList, statementGroups, 1234);
+	private final SenseDocument sd2 = new SenseDocumentImpl(sid, repList, statementGroups, 1234);
 
 	private final String JSON_SENSE = "{\"type\":\"sense\",\"id\":\"L42-S1\",\"glosses\":{\"en\":{\"language\":\"en\",\"value\":\"rep\"}},\"claims\":{\"P42\":[{\"rank\":\"normal\",\"id\":\"MyId\",\"mainsnak\":{\"property\":\"P42\",\"snaktype\":\"somevalue\"},\"type\":\"statement\"}]},\"lastrevid\":1234}";
 
 	@Test
 	public void fieldsAreCorrect() {
-		assertEquals(ld1.getEntityId(), fid);
-		assertEquals(ld1.getGlosses(), Collections.singletonMap(rep.getLanguageCode(), rep));
-		assertEquals(ld1.getStatementGroups(), statementGroups);
+		assertEquals(sd1.getEntityId(), sid);
+		assertEquals(sd1.getGlosses(), Collections.singletonMap(rep.getLanguageCode(), rep));
+		assertEquals(sd1.getStatementGroups(), statementGroups);
 	}
 
 	@Test
 	public void equalityBasedOnContent() {
-		SenseDocument irDiffGlosses = new SenseDocumentImpl(fid, Collections.singletonList(new MonolingualTextValueImpl("fr", "bar")), statementGroups, 1234);
-		SenseDocument irDiffStatementGroups = new SenseDocumentImpl(fid, repList, Collections.emptyList(), 1234);
-		SenseDocument irDiffRevisions = new SenseDocumentImpl(fid, repList, statementGroups, 1235);
+		SenseDocument irDiffGlosses = new SenseDocumentImpl(sid, Collections.singletonList(new MonolingualTextValueImpl("fr", "bar")), statementGroups, 1234);
+		SenseDocument irDiffStatementGroups = new SenseDocumentImpl(sid, repList, Collections.emptyList(), 1234);
+		SenseDocument irDiffRevisions = new SenseDocumentImpl(sid, repList, statementGroups, 1235);
 		PropertyDocument pr = new PropertyDocumentImpl(
 				new PropertyIdValueImpl("P42", "foo"),
 				repList, Collections.emptyList(), Collections.emptyList(),
@@ -71,20 +71,20 @@ public class SenseDocumentImplTest {
 				new SenseIdValueImpl("L42-S2", "http://example.com/entity/"),
 				repList, Collections.emptyList(), 1235);
 
-		assertEquals(ld1, ld1);
-		assertEquals(ld1, ld2);
-		assertNotEquals(ld1, irDiffGlosses);
-		assertNotEquals(ld1, irDiffStatementGroups);
-		assertNotEquals(ld1, irDiffRevisions);
+		assertEquals(sd1, sd1);
+		assertEquals(sd1, sd2);
+		assertNotEquals(sd1, irDiffGlosses);
+		assertNotEquals(sd1, irDiffStatementGroups);
+		assertNotEquals(sd1, irDiffRevisions);
 		assertNotEquals(irDiffStatementGroups, irDiffSenseIdValue);
-		assertNotEquals(ld1, pr);
-		assertNotEquals(ld1, null);
-		assertNotEquals(ld1, this);
+		assertNotEquals(sd1, pr);
+		assertNotEquals(sd1, null);
+		assertNotEquals(sd1, this);
 	}
 
 	@Test
 	public void hashBasedOnContent() {
-		assertEquals(ld1.hashCode(), ld2.hashCode());
+		assertEquals(sd1.hashCode(), sd2.hashCode());
 	}
 
 	@Test(expected = NullPointerException.class)
@@ -94,17 +94,17 @@ public class SenseDocumentImplTest {
 
 	@Test(expected = NullPointerException.class)
 	public void glossesNotNull() {
-		new SenseDocumentImpl(fid,  null, statementGroups, 1234);
+		new SenseDocumentImpl(sid,  null, statementGroups, 1234);
 	}
 
 	@Test(expected = IllegalArgumentException.class)
 	public void glossesNotEmpty() {
-		new SenseDocumentImpl(fid, Collections.emptyList(), statementGroups, 1234);
+		new SenseDocumentImpl(sid, Collections.emptyList(), statementGroups, 1234);
 	}
 
 	@Test
 	public void statementGroupsCanBeNull() {
-		SenseDocument doc = new SenseDocumentImpl(fid, repList, null, 1234);
+		SenseDocument doc = new SenseDocumentImpl(sid, repList, null, 1234);
 		assertTrue(doc.getStatementGroups().isEmpty());
 	}
 
@@ -120,26 +120,62 @@ public class SenseDocumentImplTest {
 		statementGroups2.add(statementGroups.get(0));
 		statementGroups2.add(sg2);
 
-		new SenseDocumentImpl(fid, repList, statementGroups2, 1234);
+		new SenseDocumentImpl(sid, repList, statementGroups2, 1234);
 	}
 
 	@Test
 	public void iterateOverAllStatements() {
-		Iterator<Statement> statements = ld1.getAllStatements();
+		Iterator<Statement> statements = sd1.getAllStatements();
 
 		assertTrue(statements.hasNext());
 		assertEquals(s, statements.next());
 		assertFalse(statements.hasNext());
 	}
 
+	@Test
+	public void testWithRevisionId() {
+		assertEquals(1235L, sd1.withRevisionId(1235L).getRevisionId());
+		assertEquals(sd1, sd1.withRevisionId(1325L).withRevisionId(sd1.getRevisionId()));
+	}
+
+	@Test
+	public void testWithGlossInNewLanguage() {
+		MonolingualTextValue newGloss = new MonolingualTextValueImpl("Foo", "fr");
+		SenseDocument withGloss = sd1.withGloss(newGloss);
+		assertEquals(newGloss, withGloss.getGlosses().get("fr"));
+	}
+
+	@Test
+	public void testAddStatement() {
+		Statement fresh = new StatementImpl("MyFreshId", StatementRank.NORMAL,
+				new SomeValueSnakImpl(new PropertyIdValueImpl("P29", "http://example.com/entity/")),
+				Collections.emptyList(), Collections.emptyList(), sid);
+		Claim claim = fresh.getClaim();
+		assertFalse(sd1.hasStatementValue(
+				claim.getMainSnak().getPropertyId(),
+				claim.getValue()));
+		SenseDocument withStatement = sd1.withStatement(fresh);
+		assertTrue(withStatement.hasStatementValue(
+				claim.getMainSnak().getPropertyId(),
+				claim.getValue()));
+	}
+
+	@Test
+	public void testDeleteStatements() {
+		Statement toRemove = statementGroups.get(0).getStatements().get(0);
+		SenseDocument withoutStatement = sd1.withoutStatementIds(Collections.singleton(toRemove.getStatementId()));
+		assertNotEquals(withoutStatement, sd1);
+	}
+
+
 	@Test
 	public void testSenseToJson() throws JsonProcessingException {
-		JsonComparator.compareJsonStrings(JSON_SENSE, mapper.writeValueAsString(ld1));
+		JsonComparator.compareJsonStrings(JSON_SENSE, mapper.writeValueAsString(sd1));
 	}
 
 	@Test
 	public void testSenseToJava() throws IOException {
-		assertEquals(ld1, mapper.readValue(JSON_SENSE, SenseDocumentImpl.class));
+		assertEquals(sd1, mapper.readValue(JSON_SENSE, SenseDocumentImpl.class));
 	}
 
 }