Skip to content

Commit

Permalink
[Enhancement #294] Add support for primitive datatypes as fields
Browse files Browse the repository at this point in the history
- remove MetamodelInitializationException thrown in ClassFieldMetamodelProcessor
- check if Wrapper type can be converted to primitive type in DataPropertyFieldStrategy
  • Loading branch information
luxbe committed Dec 16, 2024
1 parent f48345b commit 4a30c29
Show file tree
Hide file tree
Showing 8 changed files with 281 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ void processField(Field field) {
}
return;
}
if (field.getType().isPrimitive()) {
throw new MetamodelInitializationException("Primitive types cannot be used for entity fields. Field " + field + " in class " + cls);
}

final Class<?> fieldValueCls = getFieldValueType(field);
field.setAccessible(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package cz.cvut.kbss.jopa.oom;

import cz.cvut.kbss.jopa.datatype.DatatypeTransformer;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.metamodel.AbstractAttribute;
import cz.cvut.kbss.jopa.model.metamodel.EntityType;
Expand All @@ -43,6 +44,14 @@ boolean isValidRange(Object value) {
}

boolean canBeConverted(Object value) {
if(attribute.getJavaType() != null && attribute.getJavaType().isPrimitive()) {
// if the value is a wrapper for the primitive attribute type, it can be converted automatically
Class<?> primitiveClass = DatatypeTransformer.wrapperToPrimitive(value.getClass());
if (primitiveClass != null && primitiveClass.equals(attribute.getJavaType())) {
return true;
}
}

return converter.supportsAxiomValueType(value.getClass());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
* JOPA
* Copyright (C) 2024 Czech Technical University in Prague
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3.0 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library.
*/
package cz.cvut.kbss.jopa.test;

import cz.cvut.kbss.jopa.model.annotations.Id;
import cz.cvut.kbss.jopa.model.annotations.OWLClass;
import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty;

import java.net.URI;

@OWLClass(iri = Vocabulary.C_OWL_CLASS_BB)
public class OWLClassBB implements HasUri {
@Id
private URI uri;

@OWLDataProperty(iri = Vocabulary.P_BB_INT_ATTRIBUTE)
private int intAttribute;

@OWLDataProperty(iri = Vocabulary.P_BB_BOOLEAN_ATTRIBUTE)
private boolean booleanAttribute;

@OWLDataProperty(iri = Vocabulary.P_BB_BYTE_ATTRIBUTE)
private byte byteAttribute;

@OWLDataProperty(iri = Vocabulary.P_BB_SHORT_ATTRIBUTE)
private short shortAttribute;

@OWLDataProperty(iri = Vocabulary.P_BB_LONG_ATTRIBUTE)
private long longAttribute;

@OWLDataProperty(iri = Vocabulary.P_BB_FLOAT_ATTRIBUTE)
private float floatAttribute;

@OWLDataProperty(iri = Vocabulary.P_BB_DOUBLE_ATTRIBUTE)
private double doubleAttribute;

public OWLClassBB() {
}

public OWLClassBB(URI uri) {
this.uri = uri;
}

public void setUri(URI uri) {
this.uri = uri;
}

@Override
public URI getUri() {
return uri;
}

public int getIntAttribute() {
return intAttribute;
}

public void setIntAttribute(int intAttribute) {
this.intAttribute = intAttribute;
}

public byte getByteAttribute() {
return byteAttribute;
}

public void setByteAttribute(byte byteAttribute) {
this.byteAttribute = byteAttribute;
}

public double getDoubleAttribute() {
return doubleAttribute;
}

public void setDoubleAttribute(double doubleAttribute) {
this.doubleAttribute = doubleAttribute;
}

public float getFloatAttribute() {
return floatAttribute;
}

public void setFloatAttribute(float floatAttribute) {
this.floatAttribute = floatAttribute;
}

public long getLongAttribute() {
return longAttribute;
}

public void setLongAttribute(long longAttribute) {
this.longAttribute = longAttribute;
}

public short getShortAttribute() {
return shortAttribute;
}

public void setShortAttribute(short shortAttribute) {
this.shortAttribute = shortAttribute;
}

public boolean getBooleanAttribute() {
return booleanAttribute;
}

public void setBooleanAttribute(boolean booleanAttribute) {
this.booleanAttribute = booleanAttribute;
}

@Override
public String toString() {
return "OWLClassBB{" +
"uri=" + uri +
", intAttribute=" + intAttribute +
", booleanAttribute=" + booleanAttribute +
", byteAttribute=" + byteAttribute +
", shortAttribute=" + shortAttribute +
", longAttribute=" + longAttribute +
", floatAttribute=" + floatAttribute +
", doubleAttribute=" + doubleAttribute +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class Vocabulary {
public static final String C_OWL_CLASS_Y = CLASS_IRI_BASE + "OWLClassY";
public static final String C_OWL_CLASS_Z = CLASS_IRI_BASE + "OWLClassZ";
public static final String C_OWL_CLASS_PART_CONSTR_IN_PARENT = CLASS_IRI_BASE + "OWLClassWithPartConstraintsInInterfaceParent";
public static final String C_OWL_CLASS_BB = CLASS_IRI_BASE + "OWLClassBB";
public static final String C_OWL_CLASS_Z_CHILD = CLASS_IRI_BASE + "OWLClassZChild";
public static final String C_OwlClassWithQueryAttr = CLASS_IRI_BASE + "OWLClassWithQueryAttr";
public static final String C_OwlClassWithQueryAttr2 = CLASS_IRI_BASE + "OWLClassWithQueryAttr2";
Expand Down Expand Up @@ -149,6 +150,15 @@ public class Vocabulary {
public static final String P_Y_PLURAL_MULTILINGUAL_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "yPluralMultilingual";

public static final String P_AA_DYNAMIC_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "aaDynamicAttribute";
public static final String P_BB_INT_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbIntAttribute";
public static final String P_BB_BOOLEAN_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbBooleanAttribute";
public static final String P_BB_BYTE_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbByteAttribute";
public static final String P_BB_SHORT_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbShortAttribute";
public static final String P_BB_LONG_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbLongAttribute";
public static final String P_BB_FLOAT_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbFloatAttribute";
public static final String P_BB_DOUBLE_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbDoubleAttribute";
public static final String P_BB_CHAR_ATTRIBUTE = ATTRIBUTE_IRI_BASE + "bbCharAttribute";


public static final String P_HAS_H = ATTRIBUTE_IRI_BASE + "hasH";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import cz.cvut.kbss.jopa.test.OWLClassA;
import cz.cvut.kbss.jopa.test.OWLClassAA;
import cz.cvut.kbss.jopa.test.OWLClassB;
import cz.cvut.kbss.jopa.test.OWLClassBB;
import cz.cvut.kbss.jopa.test.OWLClassC;
import cz.cvut.kbss.jopa.test.OWLClassD;
import cz.cvut.kbss.jopa.test.OWLClassE;
Expand Down Expand Up @@ -95,6 +96,8 @@ public abstract class BaseRunner {
protected OWLClassWithQueryAttr7 entityWithQueryAttr7;
// Dynamic attributes
protected OWLClassAA entityAA;
// Primitive attributes
protected OWLClassBB entityBB;

protected final DataAccessor dataAccessor;
protected final PersistenceFactory persistenceFactory;
Expand Down Expand Up @@ -179,6 +182,15 @@ private void init() {
entityWithQueryAttr7.setUri(URI.create("http://krizik.felk.cvut.cz/ontologies/jopa/tests/entityWithQueryAttr7"));
this.entityAA = new OWLClassAA();
this.entityAA.setUri(URI.create("http://krizik.felk.cvut.cz/ontologies/jopa/tests/entityAA"));
this.entityBB = new OWLClassBB();
this.entityBB.setUri(URI.create("http://krizik.felk.cvut.cz/ontologies/jopa/tests/entityBB"));
entityBB.setIntAttribute(15);
entityBB.setBooleanAttribute(true);
entityBB.setByteAttribute((byte) 5);
entityBB.setShortAttribute((short) 10);
entityBB.setLongAttribute(20L);
entityBB.setFloatAttribute(25.5f);
entityBB.setDoubleAttribute(30.7d);
}

@AfterEach
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
import cz.cvut.kbss.jopa.test.OWLClassA;
import cz.cvut.kbss.jopa.test.OWLClassB;
import cz.cvut.kbss.jopa.test.OWLClassBB;
import cz.cvut.kbss.jopa.test.OWLClassD;
import cz.cvut.kbss.jopa.test.OWLClassE;
import cz.cvut.kbss.jopa.test.OWLClassG;
Expand Down Expand Up @@ -244,6 +245,23 @@ void testPersistEntityWithBasicTypeAttributes() {
assertEquals(entityM.getDateAttribute(), res.getDateAttribute());
}

@Test
void testPersistEntityWithBasicPrimitiveTypeAttributes() {
this.em = getEntityManager("PersistEntityWithBasicPrimitiveTypeAttributes", false);
persist(entityBB);
em.clear();

final OWLClassBB res = findRequired(OWLClassBB.class, entityBB.getUri());
assertEquals(entityBB.getUri(), res.getUri());
assertEquals(entityBB.getIntAttribute(), res.getIntAttribute());
assertEquals(entityBB.getBooleanAttribute(), res.getBooleanAttribute());
assertEquals(entityBB.getByteAttribute(), res.getByteAttribute());
assertEquals(entityBB.getShortAttribute(), res.getShortAttribute());
assertEquals(entityBB.getLongAttribute(), res.getLongAttribute());
assertEquals(entityBB.getFloatAttribute(), res.getFloatAttribute());
assertEquals(entityBB.getDoubleAttribute(), res.getDoubleAttribute());
}

@Test
void testPersistAndUpdateAttributeBeforeCommit() {
this.em = getEntityManager("PersistAndUpdateBeforeCommit", false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
package cz.cvut.kbss.jopa.test.runner;

import cz.cvut.kbss.jopa.model.JOPAPersistenceProperties;
import cz.cvut.kbss.jopa.model.SequencesVocabulary;
import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
import cz.cvut.kbss.jopa.model.descriptors.EntityDescriptor;
import cz.cvut.kbss.jopa.model.query.TypedQuery;
import cz.cvut.kbss.jopa.proxy.lazy.LazyLoadingProxy;
import cz.cvut.kbss.jopa.test.OWLClassA;
import cz.cvut.kbss.jopa.test.OWLClassAA;
import cz.cvut.kbss.jopa.test.OWLClassB;
import cz.cvut.kbss.jopa.test.OWLClassBB;
import cz.cvut.kbss.jopa.test.OWLClassC;
import cz.cvut.kbss.jopa.test.OWLClassD;
import cz.cvut.kbss.jopa.test.OWLClassE;
Expand Down Expand Up @@ -69,6 +71,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -106,6 +109,47 @@ void testRetrieveSimple() {
assertTrue(em.contains(res));
}

@Test
void testRetrievePrimitive() {
this.em = getEntityManager("RetrieveSimplePrimitive", false);
persist(entityBB);

em.getEntityManagerFactory().getCache().evictAll();
final OWLClassBB res = findRequired(OWLClassBB.class, entityBB.getUri());
assertEquals(entityBB.getUri(), res.getUri());
assertEquals(entityBB.getIntAttribute(), res.getIntAttribute());
assertEquals(entityBB.getBooleanAttribute(), res.getBooleanAttribute());
assertEquals(entityBB.getByteAttribute(), res.getByteAttribute());
assertEquals(entityBB.getShortAttribute(), res.getShortAttribute());
assertEquals(entityBB.getLongAttribute(), res.getLongAttribute());
assertEquals(entityBB.getFloatAttribute(), res.getFloatAttribute());
assertEquals(entityBB.getDoubleAttribute(), res.getDoubleAttribute());
assertTrue(em.contains(res));
}

@Test
void testRetrieveMissingPrimitive() throws Exception {
this.em = getEntityManager("RetrieveSimplePrimitive", false);
final List<Quad> data = new ArrayList<>(List.of(
new Quad(entityBB.getUri(), URI.create(RDF.TYPE), URI.create(Vocabulary.C_OWL_CLASS_BB))
));
persistTestData(data, em);

em.getEntityManagerFactory().getCache().evictAll();
final OWLClassBB res = findRequired(OWLClassBB.class, entityBB.getUri());

// if primitives are not set, they should fall back to their default values
assertEquals(entityBB.getUri(), res.getUri());
assertEquals(0, res.getIntAttribute());
assertEquals(false, res.getBooleanAttribute());
assertEquals(0, res.getByteAttribute());
assertEquals((short) 0, res.getShortAttribute());
assertEquals(0L, res.getLongAttribute());
assertEquals(0.0f, res.getFloatAttribute());
assertEquals(0.0d, res.getDoubleAttribute());
assertTrue(em.contains(res));
}

@Test
void testRetrieveWithLazyAttribute() {
this.em = getEntityManager("RetrieveLazy", false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import cz.cvut.kbss.jopa.test.OWLClassA;
import cz.cvut.kbss.jopa.test.OWLClassAA;
import cz.cvut.kbss.jopa.test.OWLClassB;
import cz.cvut.kbss.jopa.test.OWLClassBB;
import cz.cvut.kbss.jopa.test.OWLClassD;
import cz.cvut.kbss.jopa.test.OWLClassE;
import cz.cvut.kbss.jopa.test.OWLClassG;
Expand Down Expand Up @@ -1050,6 +1051,56 @@ void updateSupportsUpdatingSimpleLiteralValue() {
updateSimpleLiteralAndVerify();
}

@Test
void updateSupportsUpdatingPrimitiveLiteralValue() {
this.em = getEntityManager("updateSupportsUpdatingSimpleLiteralValue", true);
entityBB.setIntAttribute(15);
entityBB.setBooleanAttribute(true);
entityBB.setByteAttribute((byte) 5);
entityBB.setShortAttribute((short) 10);
entityBB.setLongAttribute(20L);
entityBB.setFloatAttribute(25.5f);
entityBB.setDoubleAttribute(30.7);
persist(entityBB);

final int newIntValue = 20;
final boolean newBooleanValue = false;
final byte newByteValue = (byte) 8;
final short newShortValue = (short) 7;
final long newLongValue = 9L;
final float newFloatValue = 3.2f;
final double newDoubleValue = 8.9d;

em.getTransaction().begin();
final OWLClassBB toUpdate = findRequired(OWLClassBB.class, entityBB.getUri());
toUpdate.setIntAttribute(newIntValue);
toUpdate.setBooleanAttribute(newBooleanValue);
toUpdate.setByteAttribute(newByteValue);
toUpdate.setShortAttribute(newShortValue);
toUpdate.setLongAttribute(newLongValue);
toUpdate.setFloatAttribute(newFloatValue);
toUpdate.setDoubleAttribute(newDoubleValue);
em.getTransaction().commit();

verifyValueDatatype(entityBB.getUri(), Vocabulary.P_BB_INT_ATTRIBUTE, XSD.INT);
verifyValueDatatype(entityBB.getUri(), Vocabulary.P_BB_BOOLEAN_ATTRIBUTE, XSD.BOOLEAN);
verifyValueDatatype(entityBB.getUri(), Vocabulary.P_BB_BYTE_ATTRIBUTE, XSD.BYTE);
verifyValueDatatype(entityBB.getUri(), Vocabulary.P_BB_SHORT_ATTRIBUTE, XSD.SHORT);
verifyValueDatatype(entityBB.getUri(), Vocabulary.P_BB_LONG_ATTRIBUTE, XSD.LONG);
verifyValueDatatype(entityBB.getUri(), Vocabulary.P_BB_FLOAT_ATTRIBUTE, XSD.FLOAT);
verifyValueDatatype(entityBB.getUri(), Vocabulary.P_BB_DOUBLE_ATTRIBUTE, XSD.DOUBLE);
final OWLClassBB res = findRequired(OWLClassBB.class, entityBB.getUri());
assertEquals(entityBB.getUri(), res.getUri());
assertEquals(newIntValue, res.getIntAttribute());
assertEquals(newBooleanValue, res.getBooleanAttribute());
assertEquals(newByteValue, res.getByteAttribute());
assertEquals(newShortValue, res.getShortAttribute());
assertEquals(newLongValue, res.getLongAttribute());
assertEquals(newFloatValue, res.getFloatAttribute());
assertEquals(newDoubleValue, res.getDoubleAttribute());
}


private void updateSimpleLiteralAndVerify() {
em.getTransaction().begin();
final String newValue = "new test value";
Expand Down

0 comments on commit 4a30c29

Please sign in to comment.