Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support primitive java types #299

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ public class DatatypeTransformer {

private static final Map<Pair, Function<Object, ?>> TRANSFORMERS = initTransformers();

private static final Map<Class<?>, Class<?>> WRAPPER_TO_PRIMITIVES = Map.of(
Integer.class, int.class,
Boolean.class, boolean.class,
Byte.class, byte.class,
Short.class, short.class,
Long.class, long.class,
Float.class, float.class,
Double.class, double.class
);

private DatatypeTransformer() {
throw new AssertionError();
}
Expand Down Expand Up @@ -79,6 +89,22 @@ private DatatypeTransformer() {
return map;
}

/**
* Converts the specified wrapper class to its corresponding primitive class.
*
* If the class parameter is a wrapper type, the equivalent primitive type will be returned (e.g. int.class for Integer.class)
* In all other cases, the return value is null.
*
* @param cls - the class to convert
* @return
*/
public static Optional<Class<?>> wrapperTypeToPrimitiveType(Class<?> cls) {
if(cls != null && WRAPPER_TO_PRIMITIVES.containsKey(cls)) {
return Optional.of(WRAPPER_TO_PRIMITIVES.get(cls));
}
return Optional.empty();
}

/**
* Maps the specified value to the target type (if possible).
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,36 @@

import java.net.InetAddress;
import java.net.URL;
import java.util.Optional;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.*;

class DatatypeTransformerTest {

@Test
void wrapperTypeToPrimitiveTypeReturnsNullForNullInput() {
assertEquals(Optional.empty(), DatatypeTransformer.wrapperTypeToPrimitiveType(null));
}

@ParameterizedTest
@MethodSource("wrapperTypeToPrimitiveTypeTestValues")
void wrapperTypeToPrimitiveType(Class<?> wrapper, Class<?> primitive) {
assertEquals(Optional.of(primitive), DatatypeTransformer.wrapperTypeToPrimitiveType(wrapper));
}

private static Stream<Arguments> wrapperTypeToPrimitiveTypeTestValues() {
return Stream.of(
Arguments.arguments(Integer.class, int.class),
Arguments.arguments(Boolean.class, boolean.class),
Arguments.arguments(Byte.class, byte.class),
Arguments.arguments(Short.class, short.class),
Arguments.arguments(Long.class, long.class),
Arguments.arguments(Float.class, float.class),
Arguments.arguments(Double.class, double.class)
);
}

@Test
void transformReturnsNullForNullInput() {
assertNull(DatatypeTransformer.transform(null, String.class));
Expand Down
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 @@ -29,6 +29,7 @@
import cz.cvut.kbss.jopa.oom.converter.ToStringConverter;
import cz.cvut.kbss.jopa.oom.converter.ToURIConverter;
import cz.cvut.kbss.jopa.oom.converter.ToURLConverter;
import cz.cvut.kbss.jopa.oom.converter.CharacterConverter;
import cz.cvut.kbss.jopa.oom.converter.datetime.DateConverter;
import cz.cvut.kbss.jopa.oom.converter.datetime.InstantConverter;
import cz.cvut.kbss.jopa.oom.converter.datetime.LocalDateTimeConverter;
Expand Down Expand Up @@ -91,7 +92,9 @@ void registerConverter(Class<?> attributeType, ConverterWrapper<?, ?> converter)
Map.entry(String.class, new ToStringConverter()),
Map.entry(LangString.class, new ToLangStringConverter()),
Map.entry(URI.class, new ToURIConverter()),
Map.entry(URL.class, new ToURLConverter()));
Map.entry(URL.class, new ToURLConverter()),
Map.entry(Character.class, new CharacterConverter()),
Map.entry(char.class, new CharacterConverter()));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package cz.cvut.kbss.jopa.model.metamodel;

import cz.cvut.kbss.jopa.datatype.DatatypeTransformer;
import cz.cvut.kbss.jopa.model.IRI;
import cz.cvut.kbss.jopa.model.MultilingualString;
import cz.cvut.kbss.jopa.model.annotations.CascadeType;
Expand Down Expand Up @@ -133,7 +134,9 @@ private void resolveEnumType(PropertyInfo propertyInfo, Class<?> fieldValueCls)
}

String resolveLanguage(Class<?> fieldValueCls) {
return MultilingualString.class.equals(fieldValueCls) ? null : typeBuilderContext.getPuLanguage();
return MultilingualString.class.equals(fieldValueCls) || Character.class.equals(fieldValueCls) || char.class.equals(fieldValueCls)
? null
: typeBuilderContext.getPuLanguage();
}

static PropertyAttributes create(PropertyInfo field, FieldMappingValidator validator, TypeBuilderContext<?> context) {
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 @@ -27,6 +28,7 @@

import java.util.Collection;
import java.util.Collections;
import java.util.Optional;

abstract class DataPropertyFieldStrategy<A extends AbstractAttribute<? super X, ?>, X> extends FieldStrategy<A, X> {

Expand All @@ -43,6 +45,14 @@ boolean isValidRange(Object value) {
}

boolean canBeConverted(Object value) {
if(attribute.getJavaType().isPrimitive()) {
// if the value is a wrapper for the primitive attribute type, it can be converted automatically
Optional<Class<?>> primitiveClass = DatatypeTransformer.wrapperTypeToPrimitiveType(value.getClass());
if (primitiveClass.isPresent() && primitiveClass.get().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,51 @@
/*
* 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.oom.converter;

import cz.cvut.kbss.jopa.datatype.exception.DatatypeMappingException;
import cz.cvut.kbss.ontodriver.model.LangString;

/**
* Converts between {@link Character} and a xsd:string representation.
* <p>
* This converter supports {@link String} and {@link LangString} as character representations.
*/
public class CharacterConverter implements ConverterWrapper<Character, Object> {

@Override
public Object convertToAxiomValue(Character value) {
assert value != null;
return value.toString();
}

@Override
public Character convertToAttribute(Object value) {
assert value != null && value instanceof String;

if(((String) value).length() > 1) {
throw new DatatypeMappingException("Unable to map literal " + value + " to " + Character.class.getCanonicalName() + ", because its length is greater than 1");
}

return ((String) value).charAt(0);
}

@Override
public boolean supportsAxiomValueType(Class<?> type) {
return String.class.isAssignableFrom(type);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public class OWLClassM {
@OWLDataProperty(iri = Vocabulary.p_m_dateAttribute)
private Date dateAttribute;

@OWLDataProperty(iri = Vocabulary.p_m_characterAttribute)
private Character characterAttribute;

@OWLDataProperty(iri = Vocabulary.p_m_enumAttribute)
private Severity enumAttribute;

Expand Down Expand Up @@ -133,6 +136,14 @@ public void setDateAttribute(Date dateAttribute) {
this.dateAttribute = dateAttribute;
}

public Character getCharacterAttribute() {
return characterAttribute;
}

public void setCharacterAttribute(Character characterAttribute) {
this.characterAttribute = characterAttribute;
}

public Severity getEnumAttribute() {
return enumAttribute;
}
Expand Down Expand Up @@ -191,12 +202,14 @@ public void setObjectOneOfEnumAttribute(OneOfEnum objectOneOfEnumAttribute) {

@Override
public String toString() {
return "OWLCLassM{" +
return "OWLClassM{" +
"key='" + key + '\'' +
", booleanAttribute=" + booleanAttribute +
", intAttribute=" + intAttribute +
", longAttribute=" + longAttribute +
", doubleAttribute=" + doubleAttribute +
", dateAttribute=" + dateAttribute +
", characterAttribute=" + characterAttribute +
", enumAttribute=" + enumAttribute +
", ordinalEnumAttribute=" + ordinalEnumAttribute +
", integerSet=" + integerSet +
Expand All @@ -217,6 +230,7 @@ public void initializeTestValues(boolean includingKey) {
this.longAttribute = 365L;
this.doubleAttribute = 3.14D;
this.dateAttribute = new Date();
this.characterAttribute = 'j';
this.enumAttribute = Severity.MEDIUM;
this.ordinalEnumAttribute = enumAttribute;
this.integerSet = IntStream.generate(Generators::randomInt).limit(10).boxed().collect(Collectors.toSet());
Expand Down Expand Up @@ -263,6 +277,14 @@ public static PropertyInfo getDateAttributeFieldPropertyInfo() throws Exception
return PropertyInfo.from(OWLClassM.getDateAttributeField());
}

public static Field getCharacterAttributeField() throws Exception {
return OWLClassM.class.getDeclaredField("characterAttribute");
}

public static PropertyInfo getCharacterAttributeFieldPropertyInfo() throws Exception {
return PropertyInfo.from(OWLClassM.getCharacterAttributeField());
}

public static Field getEnumAttributeField() throws Exception {
return OWLClassM.class.getDeclaredField("enumAttribute");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ public class Vocabulary {
public static final String p_m_longAttribute = ATTRIBUTE_BASE + "m-longAttribute";
public static final String p_m_doubleAttribute = ATTRIBUTE_BASE + "m-doubleAttribute";
public static final String p_m_dateAttribute = ATTRIBUTE_BASE + "m-dateAttribute";
public static final String p_m_characterAttribute = ATTRIBUTE_BASE + "m-characterAttribute";
public static final String p_m_enumAttribute = ATTRIBUTE_BASE + "m-enumAttribute";
public static final String p_m_ordinalEnumAttribute = ATTRIBUTE_BASE + "m-ordinalEnumAttribute";
public static final String p_m_IntegerSet = ATTRIBUTE_BASE + "m-pluralIntAttribute";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import cz.cvut.kbss.jopa.oom.converter.ToIntegerConverter;
import cz.cvut.kbss.jopa.oom.converter.ToLexicalFormConverter;
import cz.cvut.kbss.jopa.oom.converter.ToLongConverter;
import cz.cvut.kbss.jopa.oom.converter.CharacterConverter;
import cz.cvut.kbss.jopa.oom.converter.datetime.LocalDateTimeConverter;
import cz.cvut.kbss.jopa.utils.Configuration;
import cz.cvut.kbss.jopa.vocabulary.DC;
Expand Down Expand Up @@ -735,7 +736,7 @@ public static void initOWLClassLMocks(IdentifiableEntityType<OWLClassL> etMock,

public static void initOWLClassMMock(IdentifiableEntityType<OWLClassM> etMock, AbstractAttribute booleanAtt,
AbstractAttribute intAtt, SingularAttributeImpl longAtt,
AbstractAttribute doubleAtt, AbstractAttribute dateAtt,
AbstractAttribute doubleAtt, AbstractAttribute dateAtt, AbstractAttribute characterAtt,
AbstractAttribute enumAtt, AbstractAttribute ordinalEnumAtt,
AbstractPluralAttribute intSetAtt, SingularAttributeImpl lexicalFormAtt,
SingularAttributeImpl simpleLiteralAtt,
Expand All @@ -755,13 +756,13 @@ public static void initOWLClassMMock(IdentifiableEntityType<OWLClassM> etMock, A
when(etMock.getFieldSpecification(idMock.getName())).thenReturn(idMock);
when(etMock.getAttributes()).thenReturn(
new HashSet<>(Arrays.<Attribute<? super OWLClassM, ?>>asList(booleanAtt, intAtt, longAtt, doubleAtt,
dateAtt, enumAtt, ordinalEnumAtt,
dateAtt, characterAtt, enumAtt, ordinalEnumAtt,
intSetAtt, lexicalFormAtt,
simpleLiteralAtt, explicitDatatypeAtt,
mObjectOneOfEnumAttribute)));
when(etMock.getFieldSpecifications()).thenReturn(new HashSet<>(
Arrays.<FieldSpecification<? super OWLClassM, ?>>asList(booleanAtt, intAtt, longAtt, doubleAtt, dateAtt,
enumAtt, ordinalEnumAtt, intSetAtt,
characterAtt, enumAtt, ordinalEnumAtt, intSetAtt,
lexicalFormAtt, simpleLiteralAtt,
explicitDatatypeAtt, mObjectOneOfEnumAttribute,
idMock)));
Expand Down Expand Up @@ -836,6 +837,20 @@ public static void initOWLClassMMock(IdentifiableEntityType<OWLClassM> etMock, A
when(etMock.getFieldSpecification(OWLClassM.getDateAttributeField().getName())).thenReturn(dateAtt);
when(etMock.getAttribute(OWLClassM.getDateAttributeField().getName())).thenReturn(dateAtt);

when(characterAtt.getJavaField()).thenReturn(OWLClassM.getCharacterAttributeField());
when(characterAtt.getName()).thenReturn(OWLClassM.getCharacterAttributeField().getName());
when(characterAtt.getJavaType()).thenReturn(OWLClassM.getCharacterAttributeField().getType());
when(characterAtt.getIRI()).thenReturn(IRI.create(Vocabulary.p_m_characterAttribute));
when(characterAtt.getPersistentAttributeType()).thenReturn(Attribute.PersistentAttributeType.DATA);
when(characterAtt.isCollection()).thenReturn(false);
when(characterAtt.getDeclaringType()).thenReturn(etMock);
when(characterAtt.getConstraints()).thenReturn(new ParticipationConstraint[0]);
when(characterAtt.getConverter()).thenReturn(new CharacterConverter());
when(characterAtt.hasLanguage()).thenReturn(true);
when(characterAtt.getLanguage()).thenReturn(Generators.LANG);
when(etMock.getFieldSpecification(OWLClassM.getCharacterAttributeField().getName())).thenReturn(characterAtt);
when(etMock.getAttribute(OWLClassM.getCharacterAttributeField().getName())).thenReturn(characterAtt);

when(enumAtt.getJavaField()).thenReturn(OWLClassM.getEnumAttributeField());
when(enumAtt.getName()).thenReturn(OWLClassM.getEnumAttributeField().getName());
when(enumAtt.getJavaType()).thenReturn(OWLClassM.getEnumAttributeField().getType());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,8 @@ public class MetamodelMocks {
@Mock
private SingularAttributeImpl<OWLClassM, Date> mDateAtt;
@Mock
private SingularAttributeImpl<OWLClassM, Character> mCharacterAtt;
@Mock
private SingularAttributeImpl<OWLClassM, OWLClassM.Severity> mEnumAtt;
@Mock
private SingularAttributeImpl<OWLClassM, OWLClassM.Severity> mOrdinalEnumAtt;
Expand Down Expand Up @@ -388,7 +390,7 @@ public MetamodelMocks() throws Exception {
MetamodelFactory.initOWLClassJMocks(etJ, jSetAtt, idJ);
MetamodelFactory.initOWLClassKMocks(etK, kOwlClassEAtt, idK);
MetamodelFactory.initOWLClassLMocks(etL, lReferencedList, lSimpleList, lSetAtt, lOwlClassAAtt, idL);
MetamodelFactory.initOWLClassMMock(etM, mBooleanAtt, mIntegerAtt, mLongAtt, mDoubleAtt, mDateAtt, mEnumAtt,
MetamodelFactory.initOWLClassMMock(etM, mBooleanAtt, mIntegerAtt, mLongAtt, mDoubleAtt, mDateAtt, mCharacterAtt, mEnumAtt,
mOrdinalEnumAtt, mIntegerSetAtt, mLexicalFormAtt, mSimpleLiteralAtt,
mExplicitDatatypeAtt, mWithConverterAtt, mObjectOneOfEnumAttribute, idM);
MetamodelFactory.initOWLClassNMock(etN, nAnnotationAtt, nAnnotationUriAtt, nStringAtt, nPluralAnnotationAtt,
Expand Down Expand Up @@ -786,6 +788,10 @@ public AbstractAttribute<OWLClassM, Date> dateAttribute() {
return MetamodelMocks.this.mDateAtt;
}

public AbstractAttribute<OWLClassM, Character> characterAttribute() {
return MetamodelMocks.this.mCharacterAtt;
}

public AbstractAttribute<OWLClassM, OWLClassM.Severity> enumAttribute() {
return MetamodelMocks.this.mEnumAtt;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ void buildsSingleEntityWithMultipleSingularDataProperties() throws Exception {
FetchType.EAGER, false, dateField.getAnnotation(OWLDataProperty.class).iri(),
dateField.getType(),
new CascadeType[]{});
final Field characterField = OWLClassM.getCharacterAttributeField();
final FieldSpecification<? super OWLClassM, ?> characterAtt = et.getFieldSpecification(characterField.getName());
checkSingularAttribute(characterAtt, et, characterField.getName(), Attribute.PersistentAttributeType.DATA, characterField,
FetchType.EAGER, false, characterField.getAnnotation(OWLDataProperty.class).iri(),
characterField.getType(),
new CascadeType[]{});
final Field enumField = OWLClassM.getEnumAttributeField();
final FieldSpecification<? super OWLClassM, ?> enumAtt = et.getFieldSpecification(enumField.getName());
checkSingularAttribute(enumAtt, et, enumField.getName(), Attribute.PersistentAttributeType.DATA, enumField,
Expand Down
Loading
Loading