diff --git a/pom.xml b/pom.xml index 4c3049c..fbc4f21 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ cz.cvut.kbss.jsonld jb4jsonld - 0.8.3 + 0.8.4 JB4JSON-LD Java Binding for JSON-LD allows serialization and deserialization of Java POJOs to/from JSON-LD. This is the core implementation, which has to be integrated with Jackson, Jersey etc. diff --git a/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java b/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java index cced9cc..53c2864 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java +++ b/src/main/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessor.java @@ -1,11 +1,11 @@ /** * Copyright (C) 2020 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 @@ -20,6 +20,7 @@ import cz.cvut.kbss.jsonld.annotation.JsonLdAttributeOrder; import cz.cvut.kbss.jsonld.exception.JsonLdSerializationException; +import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; @@ -70,7 +71,72 @@ public static String getOwlClass(Class cls) { if (owlClass == null) { throw new IllegalArgumentException(cls + " is not an OWL class entity."); } - return owlClass.iri(); + return expandIriIfNecessary(owlClass.iri(), cls); + } + + /** + * Attempts to expand the specified IRI in case it is compacted (see {@link IdentifierUtil#isCompactIri(String)}) using JOPA namespace declarations. + *

+ * If the IRI is not compact or no matching namespace is found, the original IRI is returned. + * + * @param iri IRI to expand (if necessary and possible) + * @param declaringClass Class in/on which the IRI is declared. It is used as base for namespace search + * @return Expanded IRI if it was possible to expand it, original argument if not + * @see IdentifierUtil#isCompactIri(String) + * @see Namespaces + * @see Namespace + */ + public static String expandIriIfNecessary(String iri, Class declaringClass) { + Objects.requireNonNull(declaringClass); + return IdentifierUtil.isCompactIri(iri) ? expandIri(iri, declaringClass).orElse(iri) : iri; + } + + /** + * Attempts to expand the specified compact IRI by finding a corresponding {@link Namespace} annotation in the specified class's ancestor hierarchy. + *

+ * That is, it tries to find a {@link Namespace} annotation with matching prefix on the specified class or any of its ancestors. If such an annotation + * is found, its namespace is concatenated with the suffix from the specified {@code iri} to produce the expanded version of the IRI. + *

+ * If no matching {@link Namespace} annotation is found, an empty {@link Optional} is returned. + * + * @param iri Compact IRI to expand + * @param declaringClass Class in which the IRI was declared. Used to start search for namespace declaration + * @return Expanded IRI if a matching namespace declaration is found, empty {@code Optional} if not + */ + private static Optional expandIri(String iri, Class declaringClass) { + assert IdentifierUtil.isCompactIri(iri); + + final int colonIndex = iri.indexOf(':'); + final String prefix = iri.substring(0, colonIndex); + final String suffix = iri.substring(colonIndex + 1); + Optional ns = resolveNamespace(declaringClass, prefix); + if (ns.isPresent()) { + return ns.map(v -> v + suffix); + } + if (declaringClass.getPackage() != null) { + ns = resolveNamespace(declaringClass.getPackage(), prefix); + if (ns.isPresent()) { + return ns.map(v -> v + suffix); + } + } + return declaringClass.getSuperclass() != null ? expandIri(iri, declaringClass.getSuperclass()) : + Optional.empty(); + } + + private static Optional resolveNamespace(AnnotatedElement annotated, String prefix) { + Namespace ns = annotated.getDeclaredAnnotation(Namespace.class); + if (ns != null && ns.prefix().equals(prefix)) { + return Optional.of(ns.namespace()); + } + Namespaces namespaces = annotated.getDeclaredAnnotation(Namespaces.class); + if (namespaces != null) { + final Optional namespace = + Arrays.stream(namespaces.value()).filter(n -> n.prefix().equals(prefix)).findAny(); + if (namespace.isPresent()) { + return namespace.map(Namespace::namespace); + } + } + return Optional.empty(); } /** @@ -100,7 +166,7 @@ public static Set getOwlClasses(Class cls) { getAncestors(cls).forEach(c -> { final OWLClass owlClass = c.getDeclaredAnnotation(OWLClass.class); if (owlClass != null) { - classes.add(owlClass.iri()); + classes.add(expandIriIfNecessary(owlClass.iri(), c)); } }); return classes; @@ -319,15 +385,15 @@ public static String getAttributeIdentifier(Field field) { } final OWLDataProperty dp = field.getDeclaredAnnotation(OWLDataProperty.class); if (dp != null) { - return dp.iri(); + return expandIriIfNecessary(dp.iri(), field.getDeclaringClass()); } final OWLObjectProperty op = field.getDeclaredAnnotation(OWLObjectProperty.class); if (op != null) { - return op.iri(); + return expandIriIfNecessary(op.iri(), field.getDeclaringClass()); } final OWLAnnotationProperty ap = field.getDeclaredAnnotation(OWLAnnotationProperty.class); if (ap != null) { - return ap.iri(); + return expandIriIfNecessary(ap.iri(), field.getDeclaringClass()); } if (field.getDeclaredAnnotation(Types.class) != null) { return JsonLd.TYPE; diff --git a/src/main/java/cz/cvut/kbss/jsonld/common/IdentifierUtil.java b/src/main/java/cz/cvut/kbss/jsonld/common/IdentifierUtil.java index 4474b3d..b3f09c8 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/common/IdentifierUtil.java +++ b/src/main/java/cz/cvut/kbss/jsonld/common/IdentifierUtil.java @@ -1,11 +1,11 @@ /** * Copyright (C) 2020 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 @@ -14,6 +14,7 @@ */ package cz.cvut.kbss.jsonld.common; +import java.util.Objects; import java.util.Random; /** @@ -40,4 +41,22 @@ public class IdentifierUtil { public static String generateBlankNodeId() { return B_NODE_PREFIX + RANDOM.nextInt(Integer.MAX_VALUE); } + + /** + * Checks whether the specified value is a compact IRI, as defined by the JSON-LD specification + * par. 4.1.5. + * + * @param value The value to examine + * @return {@code true} if the specified value is a compact IRI, {@code false} otherwise + */ + public static boolean isCompactIri(String value) { + Objects.requireNonNull(value); + final int colonIndex = value.indexOf(':'); + if (colonIndex >= 0) { + final String prefixWithColon = value.substring(0, colonIndex + 1); + final String suffix = value.substring(colonIndex + 1); + return !B_NODE_PREFIX.equals(prefixWithColon) && !suffix.startsWith("//"); + } + return false; + } } diff --git a/src/main/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilder.java b/src/main/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilder.java index ac052e7..294e24f 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilder.java +++ b/src/main/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilder.java @@ -111,20 +111,19 @@ public void openObject(String id, Class cls) { } else { if (knownInstances.containsKey(id)) { final InstanceContext context = reopenExistingInstance(id, cls); - replaceCurrentContext(context.getInstance(), context); + replaceCurrentContext(context); } else { final T instance = BeanClassProcessor.createInstance(cls); final InstanceContext context = new SingularObjectContext<>(instance, BeanAnnotationProcessor.mapFieldsForDeserialization(cls), knownInstances); - replaceCurrentContext(instance, context); + replaceCurrentContext(context); currentInstance.setIdentifierValue(id); } } } - private void replaceCurrentContext(T instance, InstanceContext ctx) { + private void replaceCurrentContext(InstanceContext ctx) { if (currentInstance != null) { - currentInstance.addItem(instance); openInstances.push(currentInstance); } this.currentInstance = ctx; @@ -137,7 +136,10 @@ public void closeObject() { pendingReferenceRegistry.resolveReferences(currentInstance.getIdentifier(), currentInstance.getInstance()); } if (!openInstances.isEmpty()) { + final InstanceContext closing = this.currentInstance; this.currentInstance = openInstances.pop(); + // Add the item to the instance after closing it, so that all its fields have been initialized already (if they are needed by equals/hashCode) + currentInstance.addItem(closing.getInstance()); } } @@ -199,7 +201,7 @@ private Object getCollectionForField(Field targetField) { public void openCollection(CollectionType collectionType) { final Collection collection = BeanClassProcessor.createCollection(collectionType); final InstanceContext context = new CollectionInstanceContext<>(collection, knownInstances); - replaceCurrentContext(collection, context); + replaceCurrentContext(context); } @Override diff --git a/src/main/java/cz/cvut/kbss/jsonld/deserialization/InstanceContext.java b/src/main/java/cz/cvut/kbss/jsonld/deserialization/InstanceContext.java index 5e5cd62..8970481 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/deserialization/InstanceContext.java +++ b/src/main/java/cz/cvut/kbss/jsonld/deserialization/InstanceContext.java @@ -109,7 +109,7 @@ Field getFieldForProperty(String property) { * @param value The value to set */ void setFieldValue(Field field, Object value) { - throw new UnsupportedOperationException("Not supported by this type of instance context."); + // Do nothing } /** @@ -118,7 +118,7 @@ void setFieldValue(Field field, Object value) { * @param item Item to add */ void addItem(Object item) { - throw new UnsupportedOperationException("Not supported by this type of instance context."); + // Do nothing } /** diff --git a/src/main/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializer.java b/src/main/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializer.java index 364cf86..bef07c2 100644 --- a/src/main/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializer.java +++ b/src/main/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializer.java @@ -15,6 +15,7 @@ import cz.cvut.kbss.jopa.model.annotations.OWLClass; import cz.cvut.kbss.jsonld.ConfigParam; import cz.cvut.kbss.jsonld.Configuration; +import cz.cvut.kbss.jsonld.common.BeanAnnotationProcessor; import cz.cvut.kbss.jsonld.common.Configurable; import cz.cvut.kbss.jsonld.deserialization.expanded.ExpandedJsonLdDeserializer; import cz.cvut.kbss.jsonld.deserialization.util.ClasspathScanner; @@ -49,7 +50,7 @@ private TargetClassResolver initializeTargetClassResolver() { new ClasspathScanner(c -> { final OWLClass ann = c.getDeclaredAnnotation(OWLClass.class); if (ann != null) { - typeMap.register(ann.iri(), c); + typeMap.register(BeanAnnotationProcessor.expandIriIfNecessary(ann.iri(), c), c); } }).processClasses(scanPath); return new TargetClassResolver(typeMap, diff --git a/src/test/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessorTest.java b/src/test/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessorTest.java index cd0b40b..f3e67f1 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessorTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/common/BeanAnnotationProcessorTest.java @@ -1,11 +1,11 @@ /** * Copyright (C) 2020 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 @@ -14,11 +14,11 @@ */ package cz.cvut.kbss.jsonld.common; -import cz.cvut.kbss.jopa.model.annotations.Id; -import cz.cvut.kbss.jopa.model.annotations.OWLAnnotationProperty; -import cz.cvut.kbss.jopa.model.annotations.OWLClass; -import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty; +import cz.cvut.kbss.jopa.model.annotations.*; +import cz.cvut.kbss.jopa.vocabulary.DC; +import cz.cvut.kbss.jopa.vocabulary.RDF; import cz.cvut.kbss.jopa.vocabulary.RDFS; +import cz.cvut.kbss.jopa.vocabulary.SKOS; import cz.cvut.kbss.jsonld.JsonLd; import cz.cvut.kbss.jsonld.annotation.JsonLdAttributeOrder; import cz.cvut.kbss.jsonld.annotation.JsonLdProperty; @@ -33,6 +33,7 @@ import java.util.*; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; @@ -218,6 +219,48 @@ void getOwlClassThrowsIllegalArgumentWhenNonOwlClassJavaTypeIsPassedAsArgument() assertEquals(Integer.class + " is not an OWL class entity.", result.getMessage()); } + @Test + void getOwlClassExpandsIriIfItUsesNamespacePrefix() { + assertEquals(DC.Terms.AGENT, BeanAnnotationProcessor.getOwlClass(ClassWithNamespace.class)); + } + + @Namespace(prefix = "dc", namespace = DC.Terms.NAMESPACE) + @OWLClass(iri = "dc:Agent") + private static class ClassWithNamespace { + } + + @Test + void getOWLClassExpandsIriBasedOnNamespacesDeclarationIfItUsesPrefix() { + assertEquals(SKOS.CONCEPT, BeanAnnotationProcessor.getOwlClass(ClassWithNamespaces.class)); + } + + @Namespaces({@Namespace(prefix = "rdf", namespace = RDF.NAMESPACE), + @Namespace(prefix = "skos", namespace = SKOS.NAMESPACE)}) + @OWLClass(iri = "skos:Concept") + private static class ClassWithNamespaces { + + @OWLDataProperty(iri = "skos:prefLabel") + private String prefLabel; + + @OWLObjectProperty(iri = "rdfs:range") + private URI range; + } + + @Test + void getOWLClassExpandsIriBasedOnNamespaceDeclaredInAncestorIfItUsesPrefix() { + assertEquals(SKOS.CONCEPT_SCHEME, BeanAnnotationProcessor.getOwlClass(ClassWithParentNamespaces.class)); + } + + @OWLClass(iri = "skos:ConceptScheme") + private static class ClassWithParentNamespaces extends ClassWithNamespaces { + } + + @Test + void getOwlClassesExpandsCompactIrisBasedOnNamespaces() { + final Set result = BeanAnnotationProcessor.getOwlClasses(ClassWithParentNamespaces.class); + assertThat(result, hasItems(SKOS.CONCEPT_SCHEME, SKOS.CONCEPT)); + } + @Test void getSerializableFieldsReturnsPropertiesFieldAsWell() throws Exception { final List fields = BeanAnnotationProcessor.getSerializableFields(new Person()); @@ -290,7 +333,20 @@ void hasTypesFieldReturnsFalseWhenClassDoesNotHaveTypesAttribute() { @Test void isAnnotationPropertyReturnsTrueForAnnotationPropertyField() throws Exception { - assertTrue(BeanAnnotationProcessor.isAnnotationProperty(ObjectWithAnnotationProperties.class.getDeclaredField("changedValue"))); + assertTrue(BeanAnnotationProcessor + .isAnnotationProperty(ObjectWithAnnotationProperties.class.getDeclaredField("changedValue"))); assertFalse(BeanAnnotationProcessor.isAnnotationProperty(Person.class.getDeclaredField("firstName"))); } + + @Test + void getAttributeIdentifierExpandsCompactedIriBasedOnNamespaceDeclaration() throws Exception { + assertEquals(SKOS.PREF_LABEL, BeanAnnotationProcessor + .getAttributeIdentifier(ClassWithNamespaces.class.getDeclaredField("prefLabel"))); + } + + @Test + void getAttributeIdentifierExpandsCompactedIriBasedOnPackageLevelNamespaceDeclaration() throws Exception { + assertEquals(RDFS.RANGE, + BeanAnnotationProcessor.getAttributeIdentifier(ClassWithNamespaces.class.getDeclaredField("range"))); + } } diff --git a/src/test/java/cz/cvut/kbss/jsonld/common/IdentifierUtilTest.java b/src/test/java/cz/cvut/kbss/jsonld/common/IdentifierUtilTest.java new file mode 100644 index 0000000..5809804 --- /dev/null +++ b/src/test/java/cz/cvut/kbss/jsonld/common/IdentifierUtilTest.java @@ -0,0 +1,26 @@ +package cz.cvut.kbss.jsonld.common; + +import cz.cvut.kbss.jsonld.environment.Generator; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class IdentifierUtilTest { + + @Test + void isCompactIriReturnsFalseForExpandedAbsoluteIri() { + assertFalse(IdentifierUtil.isCompactIri(Generator.generateUri().toString())); + } + + @Test + void isCompactIriReturnsFalseForBlankNodeIdentifier() { + assertFalse(IdentifierUtil.isCompactIri(IdentifierUtil.generateBlankNodeId())); + } + + @Test + void isCompactIriReturnsTrueForCompactIri() { + final String compact = "dc:description"; + assertTrue(IdentifierUtil.isCompactIri(compact)); + } +} diff --git a/src/test/java/cz/cvut/kbss/jsonld/common/package-info.java b/src/test/java/cz/cvut/kbss/jsonld/common/package-info.java new file mode 100644 index 0000000..b097da0 --- /dev/null +++ b/src/test/java/cz/cvut/kbss/jsonld/common/package-info.java @@ -0,0 +1,7 @@ +@Namespaces(@Namespace(prefix = "rdfs", namespace = RDFS.NAMESPACE)) +package cz.cvut.kbss.jsonld.common; + +import cz.cvut.kbss.jopa.model.annotations.Namespace; +import cz.cvut.kbss.jopa.model.annotations.Namespaces; +import cz.cvut.kbss.jopa.vocabulary.RDFS; + diff --git a/src/test/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilderTest.java b/src/test/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilderTest.java index 8f4e6a3..03f2f9c 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilderTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/deserialization/DefaultInstanceBuilderTest.java @@ -39,6 +39,7 @@ import java.util.*; import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; @@ -90,7 +91,7 @@ void openCollectionInCollectionPushesOriginalCollectionToStack() throws Exceptio } @Test - void openObjectAddsObjectToCurrentlyOpenCollectionAndBecomesCurrentInstance() throws Exception { + void closeObjectAddsObjectToCurrentlyOpenCollectionAndBecomesCurrentInstance() throws Exception { sut.openCollection(CollectionType.SET); sut.openObject(TestUtil.PALMER_URI.toString(), Employee.class); final Object root = sut.getCurrentRoot(); @@ -99,7 +100,12 @@ void openObjectAddsObjectToCurrentlyOpenCollectionAndBecomesCurrentInstance() th final Object stackTop = getOpenInstances().peek().getInstance(); assertTrue(stackTop instanceof Set); final Set set = (Set) stackTop; - assertEquals(1, set.size()); + assertTrue(((Set) stackTop).isEmpty()); + sut.closeObject(); + final Object newRoot = sut.getCurrentRoot(); + assertThat(newRoot, instanceOf(Set.class)); + final Set newSet = (Set) newRoot; + assertEquals(1, newSet.size()); assertSame(root, set.iterator().next()); } diff --git a/src/test/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializerTest.java b/src/test/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializerTest.java index b678f04..5e599cd 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializerTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/deserialization/JsonLdDeserializerTest.java @@ -1,11 +1,11 @@ /** * Copyright (C) 2020 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 @@ -21,6 +21,7 @@ import cz.cvut.kbss.jsonld.deserialization.util.TypeMap; import cz.cvut.kbss.jsonld.environment.Vocabulary; import cz.cvut.kbss.jsonld.environment.model.Study; +import cz.cvut.kbss.jsonld.environment.model.StudyWithNamespaces; import cz.cvut.kbss.jsonld.environment.model.User; import cz.cvut.kbss.jsonld.exception.JsonLdDeserializationException; import org.junit.jupiter.api.Test; @@ -28,6 +29,8 @@ import java.lang.reflect.Field; import static cz.cvut.kbss.jsonld.environment.TestUtil.readAndExpand; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.hasItem; import static org.junit.jupiter.api.Assertions.*; class JsonLdDeserializerTest { @@ -64,4 +67,13 @@ void deserializationThrowsJsonLdDeserializationExceptionWhenInputIsNotValidJsonL final Object input = readAndExpand("invalidJsonLd.json"); assertThrows(JsonLdDeserializationException.class, () -> deserializer.deserialize(input, User.class)); } + + @Test + void constructionExpandsCompactIrisWhenBuildingTypeMap() throws Exception { + final Configuration config = new Configuration(); + config.set(ConfigParam.SCAN_PACKAGE, "cz.cvut.kbss.jsonld.environment.model"); + final JsonLdDeserializer deserializer = JsonLdDeserializer.createExpandedDeserializer(config); + assertFalse(typeMap(deserializer).get(Vocabulary.STUDY).isEmpty()); + assertThat(typeMap(deserializer).get(Vocabulary.STUDY), hasItem(StudyWithNamespaces.class)); + } } diff --git a/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java b/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java index 65558a2..32a0133 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/deserialization/expanded/ExpandedJsonLdDeserializerTest.java @@ -693,4 +693,22 @@ void deserializationThrowsAmbiguousTargetTypeExceptionForAmbiguousTargetTypeWith final Object input = readAndExpand("objectWithPluralOptimisticallyTypedReference.json"); assertThrows(AmbiguousTargetTypeException.class, () -> sut.deserialize(input, StudyOnPersons.class)); } + + @Test + void deserializationEnsuresEqualityAndHashCodeBasedCollectionsArePopulatedCorrectly() throws Exception { + final Object input = readAndExpand("objectWithPluralReference.json"); + final Organization result = sut.deserialize(input, Organization.class); + assertFalse(result.getEmployees().isEmpty()); + result.getEmployees().forEach(e -> assertFalse(result.getEmployees().add(e))); + } + + @Test + void deserializationSupportsCompactedIrisBasedOnJOPANamespaces() throws Exception { + sut.configuration().set(ConfigParam.IGNORE_UNKNOWN_PROPERTIES, Boolean.toString(true)); + final Object input = readAndExpand("objectWithReadOnlyPropertyValue.json"); + final StudyWithNamespaces result = sut.deserialize(input, StudyWithNamespaces.class); + assertEquals("LupusStudy", result.getName()); + assertFalse(result.getMembers().isEmpty()); + assertFalse(result.getParticipants().isEmpty()); + } } diff --git a/src/test/java/cz/cvut/kbss/jsonld/deserialization/reference/PendingReferenceRegistryTest.java b/src/test/java/cz/cvut/kbss/jsonld/deserialization/reference/PendingReferenceRegistryTest.java index 2a8bb16..3788378 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/deserialization/reference/PendingReferenceRegistryTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/deserialization/reference/PendingReferenceRegistryTest.java @@ -129,8 +129,10 @@ void resolveReferencesSupportsMultiplePendingReferences() throws Exception { final Organization referencedObject = Generator.generateOrganization(); final String iri = referencedObject.getUri().toString(); final Employee targetObject = new Employee(); + targetObject.setUri(Generator.generateUri()); final Field targetField = Employee.class.getDeclaredField("employer"); final Employee targetTwo = new Employee(); + targetTwo.setUri(Generator.generateUri()); pendingReferences.put(iri, new HashSet<>(Arrays.asList( new SingularPendingReference(targetObject, targetField), new SingularPendingReference(targetTwo, targetField)))); diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/Vocabulary.java b/src/test/java/cz/cvut/kbss/jsonld/environment/Vocabulary.java index d1494c4..997b861 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/environment/Vocabulary.java +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/Vocabulary.java @@ -1,11 +1,11 @@ /** * Copyright (C) 2020 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 @@ -14,35 +14,39 @@ */ package cz.cvut.kbss.jsonld.environment; +import cz.cvut.kbss.jopa.vocabulary.DC; + public class Vocabulary { + public static final String DEFAULT_PREFIX = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/"; + public static final String PERSON = "http://onto.fel.cvut.cz/ontologies/ufo/Person"; - public static final String USER = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/User"; - public static final String EMPLOYEE = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/Employee"; - public static final String ORGANIZATION = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/Organization"; - public static final String STUDY = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/Study"; + public static final String USER = DEFAULT_PREFIX + "User"; + public static final String EMPLOYEE = DEFAULT_PREFIX + "Employee"; + public static final String ORGANIZATION = DEFAULT_PREFIX + "Organization"; + public static final String STUDY = DEFAULT_PREFIX + "Study"; public static final String AGENT = "http://onto.fel.cvut.cz/ontologies/ufo/Agent"; - public static final String OBJECT_WITH_ANNOTATIONS = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/ObjectWithAnnotations"; - public static final String GENERIC_MEMBER = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/GenericMember"; + public static final String OBJECT_WITH_ANNOTATIONS = DEFAULT_PREFIX + "ObjectWithAnnotations"; + public static final String GENERIC_MEMBER = DEFAULT_PREFIX + "GenericMember"; public static final String FIRST_NAME = "http://xmlns.com/foaf/0.1/firstName"; public static final String LAST_NAME = "http://xmlns.com/foaf/0.1/lastName"; public static final String USERNAME = "http://xmlns.com/foaf/0.1/accountName"; public static final String KNOWS = "http://xmlns.com/foaf/0.1/knows"; - public static final String DATE_CREATED = "http://purl.org/dc/terms/created"; - public static final String IS_MEMBER_OF = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/isMemberOf"; - public static final String HAS_MEMBER = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/hasMember"; - public static final String HAS_PARTICIPANT = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/hasParticipant"; - public static final String HAS_ADMIN = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/hasAdmin"; - public static final String BRAND = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/brand"; - public static final String IS_ADMIN = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/isAdmin"; - public static final String ORIGIN = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/origin"; - public static final String HAS_EVENT_TYPE = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/hasEventType"; - public static final String ROLE = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/role"; - public static final String PASSWORD = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/password"; - public static final String NUMBER_OF_PEOPLE_INVOLVED = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/numberOfPeopleInvolved"; + public static final String DATE_CREATED = DC.Terms.NAMESPACE + "created"; + public static final String IS_MEMBER_OF = DEFAULT_PREFIX + "isMemberOf"; + public static final String HAS_MEMBER = DEFAULT_PREFIX + "hasMember"; + public static final String HAS_PARTICIPANT = DEFAULT_PREFIX + "hasParticipant"; + public static final String HAS_ADMIN = DEFAULT_PREFIX + "hasAdmin"; + public static final String BRAND = DEFAULT_PREFIX + "brand"; + public static final String IS_ADMIN = DEFAULT_PREFIX + "isAdmin"; + public static final String ORIGIN = DEFAULT_PREFIX + "origin"; + public static final String HAS_EVENT_TYPE = DEFAULT_PREFIX + "hasEventType"; + public static final String ROLE = DEFAULT_PREFIX + "role"; + public static final String PASSWORD = DEFAULT_PREFIX + "password"; + public static final String NUMBER_OF_PEOPLE_INVOLVED = DEFAULT_PREFIX + "numberOfPeopleInvolved"; - public static final String CHANGED_VALUE = "http://krizik.felk.cvut.cz/ontologies/jb4jsonld/changedValue"; + public static final String CHANGED_VALUE = DEFAULT_PREFIX + "changedValue"; private Vocabulary() { throw new AssertionError(); diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/model/Employee.java b/src/test/java/cz/cvut/kbss/jsonld/environment/model/Employee.java index 6deb855..4768979 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/environment/model/Employee.java +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/model/Employee.java @@ -1,16 +1,14 @@ /** * Copyright (C) 2020 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 - * details. You should have received a copy of the GNU General Public License - * along with this program. If not, see . + *

+ * 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 + * details. You should have received a copy of the GNU General Public License along with this program. If not, see + * . */ package cz.cvut.kbss.jsonld.environment.model; @@ -19,6 +17,7 @@ import cz.cvut.kbss.jsonld.environment.Vocabulary; import java.lang.reflect.Field; +import java.util.Objects; @OWLClass(iri = Vocabulary.EMPLOYEE) public class Employee extends User { @@ -37,4 +36,17 @@ public void setEmployer(Organization employer) { public static Field getEmployerField() throws NoSuchFieldException { return Employee.class.getDeclaredField("employer"); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Employee)) return false; + Employee employee = (Employee) o; + return Objects.equals(getUri(), employee.getUri()); + } + + @Override + public int hashCode() { + return Objects.hash(getUri()); + } } diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/model/Organization.java b/src/test/java/cz/cvut/kbss/jsonld/environment/model/Organization.java index b92e6b2..4e69155 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/environment/model/Organization.java +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/model/Organization.java @@ -17,7 +17,6 @@ import cz.cvut.kbss.jopa.model.annotations.*; import cz.cvut.kbss.jopa.vocabulary.RDFS; import cz.cvut.kbss.jsonld.environment.Vocabulary; -import org.mockito.internal.matchers.Or; import java.lang.reflect.Field; import java.net.URI; diff --git a/src/test/java/cz/cvut/kbss/jsonld/environment/model/StudyWithNamespaces.java b/src/test/java/cz/cvut/kbss/jsonld/environment/model/StudyWithNamespaces.java new file mode 100644 index 0000000..7caa987 --- /dev/null +++ b/src/test/java/cz/cvut/kbss/jsonld/environment/model/StudyWithNamespaces.java @@ -0,0 +1,58 @@ +package cz.cvut.kbss.jsonld.environment.model; + +import cz.cvut.kbss.jopa.model.annotations.*; +import cz.cvut.kbss.jopa.vocabulary.RDFS; +import cz.cvut.kbss.jsonld.environment.Vocabulary; + +import java.net.URI; +import java.util.Set; + +@Namespaces({@Namespace(prefix = "jb4jsonld", namespace = Vocabulary.DEFAULT_PREFIX), + @Namespace(prefix = "rdfs", namespace = RDFS.NAMESPACE)}) +@OWLClass(iri = "jb4jsonld:Study") +public class StudyWithNamespaces { + + @Id + private URI uri; + + @OWLAnnotationProperty(iri = "rdfs:label") + private String name; + + @OWLObjectProperty(iri = "jb4jsonld:hasMember") + private Set members; + + @OWLObjectProperty(iri = "jb4jsonld:hasParticipant") + private Set participants; + + public URI getUri() { + return uri; + } + + public void setUri(URI uri) { + this.uri = uri; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getMembers() { + return members; + } + + public void setMembers(Set members) { + this.members = members; + } + + public Set getParticipants() { + return participants; + } + + public void setParticipants(Set participants) { + this.participants = participants; + } +} diff --git a/src/test/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializerTest.java b/src/test/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializerTest.java index 4225593..76c039e 100644 --- a/src/test/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializerTest.java +++ b/src/test/java/cz/cvut/kbss/jsonld/serialization/CompactedJsonLdSerializerTest.java @@ -522,4 +522,19 @@ void serializationSerializesMultilingualStringInTypedUnmappedProperties() throws assertEquals(ms.get(m.get(JsonLd.LANGUAGE).toString()), m.get(JsonLd.VALUE)); } } + + @Test + void serializationSupportsCompactedIrisBasedOnJOPANamespaces() throws Exception { + final Study study = new Study(); + study.setUri(Generator.generateUri()); + study.setName("Test study"); + study.setParticipants(Collections.singleton(Generator.generateEmployee())); + study.setMembers(Collections.singleton(Generator.generateEmployee())); + sut.serialize(study); + Object jsonObject = JsonUtils.fromString(jsonWriter.getResult()); + final Map json = (Map) jsonObject; + assertThat(json, hasKey(RDFS.LABEL)); + assertThat(json, hasKey(Vocabulary.HAS_PARTICIPANT)); + assertThat(json, hasKey(Vocabulary.HAS_MEMBER)); + } }