Skip to content

Commit

Permalink
Merge pull request #148 from kbss-cvut/development
Browse files Browse the repository at this point in the history
[0.21.0] release
  • Loading branch information
ledsoft authored Mar 6, 2023
2 parents c6bcd20 + 6cb074a commit 071ad8e
Show file tree
Hide file tree
Showing 68 changed files with 1,333 additions and 352 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# JOPA - Release Notes

## 0.21.0 - 2023-03-06
- Support for mapping Java enums to ontological individuals (`owl:ObjectOneOf`) (Feature #60).
- Support for mapping Java enums via ordinals or strings (Enhancement #134).

## 0.20.2 - 2023-02-23
- Support automatic conversion of language-less strings to `LangString` attributes.
- Support using `LangString` as query parameters.
Expand Down
2 changes: 1 addition & 1 deletion datatype/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<artifactId>jopa-all</artifactId>
<groupId>cz.cvut.kbss.jopa</groupId>
<version>0.20.2</version>
<version>0.21.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion jopa-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>cz.cvut.kbss.jopa</groupId>
<artifactId>jopa-all</artifactId>
<version>0.20.2</version>
<version>0.21.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package cz.cvut.kbss.jopa.model.annotations;

/**
* Defines mapping for enumerated types.
* <p>
* The constants of this enumerated type specify how a persistent property or field of an enumerated type should be
* persisted.
*/
public enum EnumType {
/**
* Persist enumerated type property or field as an individual assumed to be an element of a {@literal
* owl:ObjectOneOf} enumeration.
* <p>
* Note that in this case the enum constants must be annotated with {@link Individual} mapping them to ontological
* individuals.
*/
OBJECT_ONE_OF,

/**
* Persist enumerated type property or field as an integer representing the ordinal number of the enumerated
* constant.
*/
ORDINAL,

/**
* Persist enumerated type property or field as a string representation of the enumerated constant.
*/
STRING
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package cz.cvut.kbss.jopa.model.annotations;

import java.lang.annotation.*;

/**
* Specifies that a persistent property or field should be persisted as an enumerated type.
* <p>
* If the enumerated type is not specified or the Enumerated annotation is not used, the EnumType value is assumed to be
* {@code STRING}.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Enumerated {

/**
* (Optional) The type used in mapping an enum type.
*
* @return Enum mapping type
*/
EnumType value() default EnumType.STRING;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package cz.cvut.kbss.jopa.model.annotations;

import java.lang.annotation.*;

/**
* Represents an OWL individual (or an RDF resource) identified by the specified IRI.
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Individual {

/**
* Identifier of this individual.
*
* @return IRI as string
*/
String iri();
}
2 changes: 1 addition & 1 deletion jopa-distribution/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>cz.cvut.kbss.jopa</groupId>
<artifactId>jopa-all</artifactId>
<version>0.20.2</version>
<version>0.21.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
4 changes: 2 additions & 2 deletions jopa-impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>cz.cvut.kbss.jopa</groupId>
<artifactId>jopa-all</artifactId>
<version>0.20.2</version>
<version>0.21.0</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down Expand Up @@ -75,7 +75,7 @@
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<groupId>dev.aspectj</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration combine.self="override">
<complianceLevel>${jdk.version}</complianceLevel>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cz.cvut.kbss.jopa.exception;

/**
* Indicates that an enum mapping is not valid.
*/
public class InvalidEnumMappingException extends MetamodelInitializationException {

public InvalidEnumMappingException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ public String translateQuery(CriteriaParameterFiller parameterFiller) {
stringBuilder.append(" FROM ").append(((RootImpl) query.getRoot()).getJavaType().getSimpleName()).append(' ');
((RootImpl) query.getRoot()).setExpressionToQuery(stringBuilder, parameterFiller);

if (query.getWhere() != null) {
if (query.getWhere() != null && !query.getWhere().getExpressions().isEmpty()) {
stringBuilder.append(" WHERE ");
((AbstractPredicate) query.getWhere()).setExpressionToQuery(stringBuilder, parameterFiller);
}
Expand All @@ -195,7 +195,7 @@ public String translateQuery(CriteriaParameterFiller parameterFiller) {
}
}

if (query.getHaving() != null) {
if (query.getHaving() != null && !query.getHaving().getExpressions().isEmpty()) {
stringBuilder.append(" HAVING ");
((AbstractPredicate) query.getHaving()).setExpressionToQuery(stringBuilder, parameterFiller);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ public class ConverterResolver {
* {@code Optional} if no suitable converter is found (or needed)
*/
public Optional<ConverterWrapper<?, ?>> resolveConverter(Field field, PropertyAttributes config) {
if (config.getPersistentAttributeType() == Attribute.PersistentAttributeType.OBJECT) {
return Optional.empty();
}
final Class<?> attValueType = config.getType().getJavaType();
final Optional<ConverterWrapper<?, ?>> localCustomConverter = resolveCustomConverter(field);
if (localCustomConverter.isPresent()) {
Expand All @@ -71,7 +68,7 @@ public class ConverterResolver {
return globalCustomConverter;
}
if (attValueType.isEnum()) {
return Optional.of(new EnumConverter(attValueType));
return Optional.of(createEnumConverter(attValueType, config));
}
if (config.hasDatatype()) {
verifyTypeIsString(field, attValueType);
Expand All @@ -83,6 +80,17 @@ public class ConverterResolver {
return Converters.getDefaultConverter(attValueType);
}

private static ConverterWrapper<?, ?> createEnumConverter(Class<?> valueType, PropertyAttributes pa) {
switch (pa.getEnumType()) {
case OBJECT_ONE_OF:
return new ObjectOneOfEnumConverter(valueType);
case ORDINAL:
return new OrdinalEnumConverter(valueType);
default:
return new StringEnumConverter(valueType);
}
}

private static void verifyTypeIsString(Field field, Class<?> attValueType) {
if (!attValueType.equals(String.class)) {
throw new InvalidFieldMappingException(
Expand Down Expand Up @@ -154,7 +162,7 @@ private static Class<?> resolveConverterAxiomType(Class<?> converterType) {
public Optional<ConverterWrapper<?, ?>> resolveConverter(Type<?> type) {
final Class<?> attValueType = type.getJavaType();
if (attValueType.isEnum()) {
return Optional.of(new EnumConverter(attValueType));
return Optional.of(new StringEnumConverter(attValueType));
}

return converters.getCustomConverter(attValueType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
package cz.cvut.kbss.jopa.model.metamodel;

import cz.cvut.kbss.jopa.exception.InvalidFieldMappingException;
import cz.cvut.kbss.jopa.model.annotations.EnumType;
import cz.cvut.kbss.jopa.model.annotations.Enumerated;
import cz.cvut.kbss.jopa.model.annotations.Types;
import cz.cvut.kbss.jopa.utils.IdentifierTransformer;
import cz.cvut.kbss.jopa.vocabulary.RDF;
Expand Down Expand Up @@ -88,10 +90,15 @@ boolean isValidIdentifierType(Type type) {

void validateAttributeMapping(AbstractAttribute<?, ?> attribute) {
validateAttributeDoesNotMapRdfType(attribute);
if (attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.DATA
|| attribute.getPersistentAttributeType() == Attribute.PersistentAttributeType.ANNOTATION) {
validateLexicalFormAttribute(attribute);
validateSimpleLiteralField(attribute);
switch (attribute.getPersistentAttributeType()) {
case OBJECT:
validateObjectPropertyEnumMapping(attribute);
break;
case DATA: // Intentional fall-through
case ANNOTATION:
validateLexicalFormAttribute(attribute);
validateSimpleLiteralField(attribute);
break;
}
}

Expand All @@ -103,22 +110,34 @@ private static void validateAttributeDoesNotMapRdfType(AbstractAttribute<?, ?> a
}

private static void validateLexicalFormAttribute(AbstractAttribute<?, ?> attribute) {
if (attribute.isLexicalForm() && !String.class.isAssignableFrom(getLiteralFieldType(attribute))) {
if (attribute.isLexicalForm() && !String.class.isAssignableFrom(getBindableType(attribute))) {
throw new InvalidFieldMappingException(
attribute + " - lexicalForm mapping can be used only on fields of type String.");
}
}

private static void validateSimpleLiteralField(AbstractAttribute<?, ?> attribute) {
final Class<?> fieldType = getLiteralFieldType(attribute);
final Class<?> fieldType = getBindableType(attribute);
if (attribute.isSimpleLiteral() && (!String.class.isAssignableFrom(fieldType) && !Enum.class.isAssignableFrom(
fieldType) && !attribute.getConverter().supportsAxiomValueType(String.class))) {
throw new InvalidFieldMappingException(
attribute + " - simpleLiteral mapping can only be used on fields of type String or Enum or using a suitable converter.");
}
}

private static Class<?> getLiteralFieldType(AbstractAttribute<?, ?> attribute) {
return attribute.isCollection() ? ((AbstractPluralAttribute) attribute).getBindableJavaType() : attribute.getJavaType();
private static Class<?> getBindableType(AbstractAttribute<?, ?> attribute) {
return attribute.isCollection() ? ((AbstractPluralAttribute) attribute).getBindableJavaType() :
attribute.getJavaType();
}

private static void validateObjectPropertyEnumMapping(Attribute<?, ?> attribute) {
if (!attribute.getJavaType().isEnum()) {
return;
}
final Enumerated enumeratedAnn = attribute.getJavaField().getAnnotation(Enumerated.class);
if (enumeratedAnn == null || enumeratedAnn.value() != EnumType.OBJECT_ONE_OF) {
throw new InvalidFieldMappingException(
"Attribute " + attribute + " maps an enum but is not annotated with " + Enumerated.class + " with " + EnumType.OBJECT_ONE_OF + " value.");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ void resolve(Field field, MetamodelBuilder metamodelBuilder, Class<?> fieldValue
this.persistentAttributeType = Attribute.PersistentAttributeType.OBJECT;
this.iri = IRI.create(typeBuilderContext.resolveNamespace(oop.iri()));

if (validator.isValidIdentifierType(fieldValueCls)) {
initPlainIdentifierAttribute(fieldValueCls);
if (validator.isValidIdentifierType(fieldValueCls) || fieldValueCls.isEnum()) {
initBasicTypeAttribute(fieldValueCls);
} else {
this.type = metamodelBuilder.getEntityClass(fieldValueCls);
this.cascadeTypes = oop.cascade();
this.fetchType = oop.fetch();
}
}

private void initPlainIdentifierAttribute(Class<?> targetType) {
private void initBasicTypeAttribute(Class<?> targetType) {
this.type = BasicTypeImpl.get(targetType);
this.cascadeTypes = new CascadeType[0];
this.fetchType = FetchType.EAGER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ abstract class PropertyAttributes {
String language;
private boolean nonEmpty = false;
private ParticipationConstraint[] participationConstraints = new ParticipationConstraint[]{};
private EnumType enumType = null;

PropertyAttributes(FieldMappingValidator validator) {
this.validator = validator;
Expand Down Expand Up @@ -94,12 +95,13 @@ ParticipationConstraint[] getParticipationConstraints() {
return participationConstraints;
}

void resolve(Field field, MetamodelBuilder metamodelBuilder, Class<?> fieldValueCls) {
resolveParticipationConstraints(field);
public EnumType getEnumType() {
return enumType;
}

String resolveLanguage(Class<?> fieldValueCls) {
return MultilingualString.class.equals(fieldValueCls) ? null : typeBuilderContext.getPuLanguage();
void resolve(Field field, MetamodelBuilder metamodelBuilder, Class<?> fieldValueCls) {
resolveParticipationConstraints(field);
resolveEnumType(field, fieldValueCls);
}

private void resolveParticipationConstraints(Field field) {
Expand All @@ -113,6 +115,20 @@ private void resolveParticipationConstraints(Field field) {
}
}

private void resolveEnumType(Field field, Class<?> fieldValueCls) {
final Enumerated enumAnn = field.getAnnotation(Enumerated.class);
if (enumAnn != null) {
this.enumType = enumAnn.value();
} else if (fieldValueCls.isEnum()) {
// As per default of Enumerated.value()
this.enumType = EnumType.STRING;
}
}

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

static PropertyAttributes create(Field field, FieldMappingValidator validator, TypeBuilderContext<?> context) {
final PropertyAttributes instance;
if (field.getAnnotation(OWLObjectProperty.class) != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
/**
* Copyright (C) 2022 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 <http://www.gnu.org/licenses/>.
* <p>
* 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.
* <p>
* 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
* <http://www.gnu.org/licenses/>.
*/
package cz.cvut.kbss.jopa.oom;

Expand Down Expand Up @@ -51,11 +49,15 @@ <K> void addListElementsToListValueDescriptor(ListValueDescriptor listDescriptor
if (list == null) {
return;
}
if (IdentifierTransformer.isValidIdentifierType(attribute.getBindableJavaType())) {
final Class<?> elemType = attribute.getBindableJavaType();
if (IdentifierTransformer.isValidIdentifierType(elemType)) {
list.stream().filter(Objects::nonNull)
.forEach(item -> listDescriptor.addValue(NamedResource.create(IdentifierTransformer.valueAsUri(item))));
} else if (elemType.isEnum()) {
assert attribute.getConverter() != null;
list.stream().filter(Objects::nonNull).forEach(item -> listDescriptor.addValue(
(NamedResource) attribute.getConverter().convertToAxiomValue(item)));
} else {
final Class<?> elemType = attribute.getBindableJavaType();
final EntityType<?> valueType = mapper.getEntityType(elemType);
addItemsToDescriptor(listDescriptor, list, valueType);
}
Expand All @@ -67,10 +69,8 @@ static void addItemsToDescriptor(ListValueDescriptor listDescriptor, List<?> lis
}

<K> List<K> resolveUnpersistedItems(List<K> list) {
if (list == null) {
return Collections.emptyList();
}
if (IdentifierTransformer.isValidIdentifierType(attribute.getBindableJavaType())) {
if (list == null || IdentifierTransformer.isValidIdentifierType(
attribute.getBindableJavaType()) || attribute.getBindableJavaType().isEnum()) {
return Collections.emptyList();
} else {
return list.stream().filter(item -> item != null && !referenceSavingResolver
Expand Down
Loading

0 comments on commit 071ad8e

Please sign in to comment.