diff --git a/TODO.md b/TODO.md
index f3fa28748..c947e310d 100644
--- a/TODO.md
+++ b/TODO.md
@@ -19,6 +19,8 @@
- This could be overwritten with a descriptor passed to EM
- [ ] Add support for `orphanRemoval` attribute in object properties
- [ ] Modify OntoDriver API to support Fetch joins
+- [ ] Allow static (annotation, attribute of `@OWLDataProperty`) specification of language of String attributes.
+ This can be overridden on EM operation level (in descriptor).
## Low Priority
@@ -37,3 +39,5 @@
- [ ] Research whether we could replace aspectj with cglib-generated proxies
### Currently in Progress
+
+- [ ] Allow specification of language for string attributes of an entity.
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/Descriptor.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/Descriptor.java
index d2dd27063..c49110618 100644
--- a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/Descriptor.java
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/Descriptor.java
@@ -18,22 +18,19 @@
import java.lang.reflect.Field;
import java.net.URI;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
+import java.util.*;
/**
* Defines base descriptor, which is used to specify context information for entities and their fields.
*
* The descriptor hierarchy is a classical Composite pattern.
- *
- * @author ledvima1
*/
public abstract class Descriptor {
protected final URI context;
+ protected String language;
+
protected Descriptor() {
this(null);
}
@@ -53,6 +50,26 @@ public URI getContext() {
return context;
}
+ /**
+ * Gets the language set for this descriptor.
+ *
+ * @return Language tag (e.g. en, cs).
+ */
+ public Optional getLanguage() {
+ return Optional.ofNullable(language);
+ }
+
+ /**
+ * Sets language tag of this descriptor.
+ *
+ * Applies to any possible sub-descriptors as well.
+ *
+ * @param languageTag The language tag to use, possibly null, meaning no language preference should be used
+ */
+ public void setLanguage(String languageTag) {
+ this.language = languageTag;
+ }
+
/**
* Gets attribute descriptors specified in this descriptor.
*
@@ -88,6 +105,17 @@ public URI getContext() {
*/
public abstract void addAttributeContext(Field attribute, URI context);
+ /**
+ * Sets language to be used when working (retrieving, persisting) with values of the specified attribute.
+ *
+ * Note that setting language in this manner will not have any effect on descriptors of the
+ * specified attribute previously retrieved from this descriptor.
+ *
+ * @param attribute The attribute concerned
+ * @param languageTag Language tag to use, possibly {@code null}
+ */
+ public abstract void setAttributeLanguage(Field attribute, String languageTag);
+
/**
* Gets all contexts present in this descriptor.
*
@@ -100,7 +128,7 @@ public URI getContext() {
public Set getAllContexts() {
Set contexts = new HashSet<>();
contexts = getContextsInternal(contexts, new HashSet<>());
- return contexts != null ? contexts : Collections.emptySet();
+ return contexts != null ? contexts : Collections.emptySet();
}
/**
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/EntityDescriptor.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/EntityDescriptor.java
index dec889b93..2b2d403bd 100644
--- a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/EntityDescriptor.java
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/EntityDescriptor.java
@@ -59,11 +59,26 @@ public void addAttributeContext(Field attribute, URI context) {
fieldDescriptors.put(attribute, new FieldDescriptor(context, attribute));
}
+ @Override
+ public void setLanguage(String languageTag) {
+ super.setLanguage(languageTag);
+ fieldDescriptors.values().forEach(d -> d.setLanguage(languageTag));
+ }
+
+ @Override
+ public void setAttributeLanguage(Field attribute, String languageTag) {
+ Objects.requireNonNull(attribute);
+
+ fieldDescriptors.putIfAbsent(attribute, new FieldDescriptor(null, attribute));
+ fieldDescriptors.get(attribute).setLanguage(languageTag);
+ }
+
@Override
public Descriptor getAttributeDescriptor(FieldSpecification, ?> attribute) {
Descriptor d = getFieldDescriptor(attribute.getJavaField());
if (d == null) {
d = createDescriptor(attribute, context);
+ d.setLanguage(language);
}
return d;
}
diff --git a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/FieldDescriptor.java b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/FieldDescriptor.java
index c23d016ff..dc0e9c30e 100644
--- a/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/FieldDescriptor.java
+++ b/jopa-api/src/main/java/cz/cvut/kbss/jopa/model/descriptors/FieldDescriptor.java
@@ -59,6 +59,14 @@ public void addAttributeContext(Field attribute, URI context) {
// Do nothing
}
+ /**
+ * Use {@link #setLanguage(String)} instead.
+ */
+ @Override
+ public void setAttributeLanguage(Field attribute, String languageTag) {
+ // Do nothing
+ }
+
private Descriptor getFieldDescriptor(Field field) {
if (this.field.equals(field)) {
return this;
@@ -97,8 +105,6 @@ public boolean equals(Object obj) {
if (getClass() != obj.getClass())
return false;
FieldDescriptor other = (FieldDescriptor) obj;
- if (!field.equals(other.field))
- return false;
- return true;
+ return field.equals(other.field);
}
}
diff --git a/jopa-api/src/test/java/cz/cvut/kbss/jopa/model/descriptors/EntityDescriptorTest.java b/jopa-api/src/test/java/cz/cvut/kbss/jopa/model/descriptors/EntityDescriptorTest.java
new file mode 100644
index 000000000..e3c3b65d9
--- /dev/null
+++ b/jopa-api/src/test/java/cz/cvut/kbss/jopa/model/descriptors/EntityDescriptorTest.java
@@ -0,0 +1,122 @@
+package cz.cvut.kbss.jopa.model.descriptors;
+
+import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty;
+import cz.cvut.kbss.jopa.model.metamodel.Attribute;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.net.URI;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class EntityDescriptorTest {
+
+ private static final URI CONTEXT_ONE = URI.create("http://krizik.felk.cvut.cz/ontologies/jopa/contextOne");
+ private static final URI CONTEXT_TWO = URI.create("http://krizik.felk.cvut.cz/ontologies/jopa/contextTwo");
+ private static final String LANG = "en";
+
+ @Test
+ public void fieldDescriptorByDefaultInheritsEntityContext() throws Exception {
+ final EntityDescriptor descriptor = new EntityDescriptor(CONTEXT_ONE);
+ final Attribute att = mock(Attribute.class);
+ when(att.getJavaField()).thenReturn(TestClass.stringAttField());
+ final Descriptor result = descriptor.getAttributeDescriptor(att);
+ assertEquals(CONTEXT_ONE, result.getContext());
+ }
+
+ @Test
+ public void fieldDescriptorHasItsOwnContextWhenItIsSetForIt() throws Exception {
+ final EntityDescriptor descriptor = new EntityDescriptor(CONTEXT_ONE);
+ final Attribute att = mock(Attribute.class);
+ when(att.getJavaField()).thenReturn(TestClass.stringAttField());
+ descriptor.addAttributeContext(att.getJavaField(), CONTEXT_TWO);
+
+ final Descriptor result = descriptor.getAttributeDescriptor(att);
+ assertEquals(CONTEXT_TWO, result.getContext());
+ }
+
+ @Test
+ public void fieldDescriptorByDefaultInheritsEntityLanguageTag() throws Exception {
+ final EntityDescriptor descriptor = new EntityDescriptor();
+ final Attribute att = mock(Attribute.class);
+ when(att.getJavaField()).thenReturn(TestClass.stringAttField());
+ descriptor.setLanguage(LANG);
+ assertTrue(descriptor.getLanguage().isPresent());
+
+ final Descriptor result = descriptor.getAttributeDescriptor(att);
+ assertTrue(result.getLanguage().isPresent());
+ assertEquals(LANG, result.getLanguage().get());
+ }
+
+ @Test
+ public void fieldDescriptorInheritsChangeOfLanguageTagFromEntityDescriptor() throws Exception {
+ final EntityDescriptor descriptor = new EntityDescriptor();
+ final Attribute att = mock(Attribute.class);
+ when(att.getJavaField()).thenReturn(TestClass.stringAttField());
+ descriptor.setLanguage(LANG);
+ final String newLang = "cs";
+ descriptor.setLanguage(newLang);
+ final Descriptor result = descriptor.getAttributeDescriptor(att);
+ assertTrue(result.getLanguage().isPresent());
+ assertEquals(newLang, result.getLanguage().get());
+ }
+
+ @Test
+ public void fieldDescriptorHasLanguageSetToItThroughEntityDescriptor() throws Exception {
+ final EntityDescriptor descriptor = new EntityDescriptor();
+ final Attribute att = mock(Attribute.class);
+ when(att.getJavaField()).thenReturn(TestClass.stringAttField());
+ descriptor.setLanguage(LANG);
+ final String newLang = "cs";
+ descriptor.setAttributeLanguage(att.getJavaField(), newLang);
+
+ final Descriptor result = descriptor.getAttributeDescriptor(att);
+ assertTrue(result.getLanguage().isPresent());
+ assertEquals(newLang, result.getLanguage().get());
+ }
+
+ @Test
+ public void twoEntityDescriptorsAreEqualWhenTheirFieldDescriptorsHaveTheSameContexts() throws Exception {
+ final EntityDescriptor dOne = new EntityDescriptor(CONTEXT_ONE);
+ final EntityDescriptor dTwo = new EntityDescriptor(CONTEXT_ONE);
+ dOne.addAttributeContext(TestClass.stringAttField(), CONTEXT_TWO);
+ dTwo.addAttributeContext(TestClass.stringAttField(), CONTEXT_TWO);
+ dOne.addAttributeDescriptor(TestClass.intAttField(), new FieldDescriptor(CONTEXT_ONE, TestClass.intAttField()));
+ dTwo.addAttributeDescriptor(TestClass.intAttField(), new FieldDescriptor(CONTEXT_ONE, TestClass.intAttField()));
+
+ assertTrue(dOne.equals(dTwo));
+ assertTrue(dTwo.equals(dOne));
+ assertEquals(dOne.hashCode(), dTwo.hashCode());
+ }
+
+ @Test
+ public void twoEntityDescriptorsAreNotEqualWhenTheyDifferInFieldContext() throws Exception {
+ final EntityDescriptor dOne = new EntityDescriptor(CONTEXT_ONE);
+ final EntityDescriptor dTwo = new EntityDescriptor(CONTEXT_ONE);
+ dOne.addAttributeContext(TestClass.stringAttField(), CONTEXT_TWO);
+ dTwo.addAttributeContext(TestClass.stringAttField(), CONTEXT_ONE);
+
+ assertFalse(dOne.equals(dTwo));
+ assertNotEquals(dOne.hashCode(), dTwo.hashCode());
+ }
+
+ @SuppressWarnings("unused")
+ private static class TestClass {
+
+ @OWLDataProperty(iri = "http://krizik.felk.cvut.cz/ontologies/jopa/attributes/stringAtt")
+ private String stringAtt;
+
+ @OWLDataProperty(iri = "http://krizik.felk.cvut.cz/ontologies/jopa/attributes/intAtt")
+ private Integer intAtt;
+
+ private static Field stringAttField() throws NoSuchFieldException {
+ return TestClass.class.getDeclaredField("stringAtt");
+ }
+
+ private static Field intAttField() throws NoSuchFieldException {
+ return TestClass.class.getDeclaredField("intAtt");
+ }
+ }
+}
\ No newline at end of file
diff --git a/jopa-integration-tests-owlapi/src/test/java/cz/cvut/kbss/jopa/test/environment/OwlapiDataPersist.java b/jopa-integration-tests-owlapi/src/test/java/cz/cvut/kbss/jopa/test/environment/OwlapiDataPersist.java
index b1f3a21c9..23f8e4bef 100644
--- a/jopa-integration-tests-owlapi/src/test/java/cz/cvut/kbss/jopa/test/environment/OwlapiDataPersist.java
+++ b/jopa-integration-tests-owlapi/src/test/java/cz/cvut/kbss/jopa/test/environment/OwlapiDataPersist.java
@@ -1,11 +1,11 @@
/**
* Copyright (C) 2016 Czech Technical University in Prague
- *
+ *
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@@ -16,7 +16,6 @@
import cz.cvut.kbss.jopa.CommonVocabulary;
import cz.cvut.kbss.jopa.model.EntityManager;
-import cz.cvut.kbss.jopa.test.environment.Triple;
import cz.cvut.kbss.ontodriver.owlapi.util.OwlapiUtils;
import org.semanticweb.owlapi.model.*;
@@ -41,11 +40,11 @@ public void persistTestData(Collection data, EntityManager em) throws Ex
axiom = new AddAxiom(ontology, df.getOWLObjectPropertyAssertionAxiom(op, ind, obj));
} else if (t.getProperty().toString().equals(CommonVocabulary.RDFS_LABEL)) {
final OWLAnnotationProperty ap = df.getOWLAnnotationProperty(IRI.create(t.getProperty()));
- final OWLLiteral value = OwlapiUtils.createOWLLiteralFromValue(t.getValue(), df, "en");
+ final OWLLiteral value = OwlapiUtils.createOWLLiteralFromValue(t.getValue(), df, t.getLanguage());
axiom = new AddAxiom(ontology, df.getOWLAnnotationAssertionAxiom(ap, ind.getIRI(), value));
} else {
final OWLDataProperty dp = df.getOWLDataProperty(IRI.create(t.getProperty()));
- final OWLLiteral value = OwlapiUtils.createOWLLiteralFromValue(t.getValue(), df, "en");
+ final OWLLiteral value = OwlapiUtils.createOWLLiteralFromValue(t.getValue(), df, t.getLanguage());
axiom = new AddAxiom(ontology, df.getOWLDataPropertyAssertionAxiom(dp, ind, value));
}
manager.applyChange(axiom);
diff --git a/jopa-integration-tests-sesame/src/test/java/cz/cvut/kbss/jopa/test/environment/SesameDataPersist.java b/jopa-integration-tests-sesame/src/test/java/cz/cvut/kbss/jopa/test/environment/SesameDataPersist.java
index c3856c4e5..f31d7be08 100644
--- a/jopa-integration-tests-sesame/src/test/java/cz/cvut/kbss/jopa/test/environment/SesameDataPersist.java
+++ b/jopa-integration-tests-sesame/src/test/java/cz/cvut/kbss/jopa/test/environment/SesameDataPersist.java
@@ -1,11 +1,11 @@
/**
* Copyright (C) 2016 Czech Technical University in Prague
- *
+ *
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@@ -27,8 +27,7 @@ public class SesameDataPersist {
public void persistTestData(Collection data, EntityManager em) throws Exception {
final Repository repository = em.unwrap(Repository.class);
- final RepositoryConnection connection = repository.getConnection();
- try {
+ try (final RepositoryConnection connection = repository.getConnection()) {
final ValueFactory vf = connection.getValueFactory();
connection.begin();
for (Triple t : data) {
@@ -37,13 +36,11 @@ public void persistTestData(Collection data, EntityManager em) throws Ex
vf.createIRI(t.getValue().toString()));
} else {
connection.add(vf.createIRI(t.getSubject().toString()), vf.createIRI(t.getProperty().toString()),
- SesameUtils.createDataPropertyLiteral(t.getValue(), "en", vf));
+ SesameUtils.createDataPropertyLiteral(t.getValue(), t.getLanguage(), vf));
}
}
connection.commit();
- } finally {
- connection.close();
}
}
}
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Triple.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Triple.java
index 58ab8ac41..6868a5f7e 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Triple.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/environment/Triple.java
@@ -1,11 +1,11 @@
/**
* Copyright (C) 2016 Czech Technical University in Prague
- *
+ *
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any
* later version.
- *
+ *
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
@@ -19,14 +19,23 @@
public class Triple {
- private URI subject;
- private URI property;
- private Object value;
+ private final URI subject;
+ private final URI property;
+ private final Object value;
+ private final String language;
public Triple(URI subject, URI property, Object value) {
this.subject = Objects.requireNonNull(subject);
this.property = Objects.requireNonNull(property);
this.value = Objects.requireNonNull(value);
+ this.language = "en";
+ }
+
+ public Triple(URI subject, URI property, Object value, String language) {
+ this.subject = Objects.requireNonNull(subject);
+ this.property = Objects.requireNonNull(property);
+ this.value = Objects.requireNonNull(value);
+ this.language = Objects.requireNonNull(language);
}
public URI getSubject() {
@@ -41,6 +50,10 @@ public Object getValue() {
return value;
}
+ public String getLanguage() {
+ return language;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -48,9 +61,8 @@ public boolean equals(Object o) {
Triple triple = (Triple) o;
- if (!subject.equals(triple.subject)) return false;
- if (!property.equals(triple.property)) return false;
- return value.equals(triple.value);
+ return subject.equals(triple.subject) && property.equals(triple.property) && language.equals(triple.language) &&
+ value.equals(triple.value);
}
@@ -59,6 +71,7 @@ public int hashCode() {
int result = subject.hashCode();
result = 31 * result + property.hashCode();
result = 31 * result + value.hashCode();
+ result = 31 * result + language.hashCode();
return result;
}
}
diff --git a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java
index 7c44044c2..68a6e9628 100644
--- a/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java
+++ b/jopa-integration-tests/src/main/java/cz/cvut/kbss/jopa/test/runner/RetrieveOperationsRunner.java
@@ -14,8 +14,11 @@
*/
package cz.cvut.kbss.jopa.test.runner;
+import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
+import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
import cz.cvut.kbss.jopa.test.*;
import cz.cvut.kbss.jopa.test.environment.Generators;
+import cz.cvut.kbss.jopa.test.environment.Triple;
import org.junit.Test;
import org.slf4j.Logger;
@@ -198,4 +201,23 @@ public void retrieveLoadsUnmappedPropertiesTogetherWithObjectPropertyValues() {
assertEquals(v.getThings().size(), result.getThings().size());
result.getThings().forEach(t -> assertTrue(expectedUris.contains(t.getUri())));
}
+
+ @Test
+ public void retrieveGetsStringAttributeWithCorrectLanguageWhenItIsSpecifiedInDescriptor() throws Exception {
+ this.em = getEntityManager("retrieveGetsStringAttributeWithCorrectLanguageWhenItIsSpecifiedInDescriptor",
+ false);
+ persist(entityA);
+ final String value = "v cestine";
+ final String lang = "cs";
+ persistTestData(Collections
+ .singleton(new Triple(entityA.getUri(), URI.create(Vocabulary.P_A_STRING_ATTRIBUTE), value, lang)), em);
+
+ final Descriptor descriptor = new EntityDescriptor();
+ descriptor.setLanguage(lang);
+
+ final OWLClassA result = em.find(OWLClassA.class, entityA.getUri());
+ assertNotNull(result);
+ assertEquals(value, result.getStringAttribute());
+ assertEquals(entityA.getTypes(), result.getTypes());
+ }
}