Skip to content

Commit 2c27c7d

Browse files
authored
Merge pull request #292 from kbss-cvut/development
[2.2.0] Release
2 parents 72c16eb + 853e5c3 commit 2c27c7d

File tree

54 files changed

+1463
-395
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1463
-395
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# JOPA - Change Log
22

3+
## 2.2.0 - 2024-11-22
4+
- Treat any non-entity type as immutable w.r.t. to change tracking so that we do not have to explicitly list them in code (GH #278).
5+
- Allow projecting entity attributes from SOQL/Criteria API queries (Enhancement #277).
6+
- Allow using `Object` as data property field type, resolve actual type from data (GH #283).
7+
- Use lists for plural query attributes when field type is `Collection` (Enhancement #276).
8+
- Fix lazy loading of plural query attributes (Bug #285).
9+
- Ignore SPARQL comments when parsing native SPARQL queries (Bug #275).
10+
- Fix Jena file storage statement removal issue (Bug #274).
11+
- Dependency updates: RDF4J 5.0.3.
12+
313
## 2.1.0 - 2024-10-16
414
- Add support for RDF containers (Task #52).
515
[Wiki](https://github.com/kbss-cvut/jopa/wiki/Object-ontological-Mapping#collection-mapping) contains info on collection mapping in general.

datatype/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<artifactId>jopa-all</artifactId>
88
<groupId>cz.cvut.kbss.jopa</groupId>
9-
<version>2.1.0</version>
9+
<version>2.2.0</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212

jopa-api/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>cz.cvut.kbss.jopa</groupId>
88
<artifactId>jopa-all</artifactId>
9-
<version>2.1.0</version>
9+
<version>2.2.0</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212

jopa-distribution/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>cz.cvut.kbss.jopa</groupId>
88
<artifactId>jopa-all</artifactId>
9-
<version>2.1.0</version>
9+
<version>2.2.0</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212

jopa-impl/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<parent>
77
<groupId>cz.cvut.kbss.jopa</groupId>
88
<artifactId>jopa-all</artifactId>
9-
<version>2.1.0</version>
9+
<version>2.2.0</version>
1010
<relativePath>../pom.xml</relativePath>
1111
</parent>
1212

jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/ClassFieldMetamodelProcessor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,11 @@ private void createQueryAttribute(Field field, Class<?> fieldValueCls) {
318318
final AbstractAttribute<X, ?> a;
319319
if (property.getAnnotation(RDFContainer.class) != null) {
320320
a = createRdfContainerAttribute(property, inference, propertyAttributes);
321+
} else if (property.getType().isAssignableFrom(Object.class)) {
322+
final SingularAttributeImpl.SingularAttributeBuilder builder = setCommonBuildParameters(SingularAttributeImpl.builder(propertyAttributes),
323+
property, inference);
324+
context.getConverterResolver().resolveConverter(property, propertyAttributes).ifPresent(builder::converter);
325+
a = builder.build();
321326
} else if (property.getType().isAssignableFrom(Collection.class)) {
322327
final AbstractPluralAttribute.PluralAttributeBuilder builder = setCommonBuildParameters(CollectionAttributeImpl.builder(propertyAttributes),
323328
property, inference);

jopa-impl/src/main/java/cz/cvut/kbss/jopa/model/metamodel/FieldMappingValidator.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,16 @@ private static void validateLexicalFormAttribute(AbstractAttribute<?, ?> attribu
122122

123123
private static void validateSimpleLiteralField(AbstractAttribute<?, ?> attribute) {
124124
final Class<?> fieldType = getBindableType(attribute);
125-
if (attribute.isSimpleLiteral() && (!String.class.isAssignableFrom(fieldType) && !Enum.class.isAssignableFrom(
126-
fieldType) && !attribute.getConverter().supportsAxiomValueType(String.class))) {
127-
throw new InvalidFieldMappingException(
128-
attribute.getJavaField(),"simpleLiteral mapping can only be used on fields of type String or Enum or using a suitable converter.");
125+
126+
if (!attribute.isSimpleLiteral() || String.class.isAssignableFrom(fieldType) || Enum.class.isAssignableFrom(fieldType)) {
127+
return;
129128
}
129+
130+
if(attribute.getConverter() != null && attribute.getConverter().supportsAxiomValueType(String.class)) {
131+
return;
132+
}
133+
134+
throw new InvalidFieldMappingException(attribute.getJavaField(),"simpleLiteral mapping can only be used on fields of type String or Enum or using a suitable converter.");
130135
}
131136

132137
private static Class<?> getBindableType(AbstractAttribute<?, ?> attribute) {

jopa-impl/src/main/java/cz/cvut/kbss/jopa/oom/query/PluralQueryAttributeStrategy.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public class PluralQueryAttributeStrategy<X> extends QueryFieldStrategy<PluralQu
3030

3131
public PluralQueryAttributeStrategy(EntityType<X> et, PluralQueryAttributeImpl<? super X, ?, ?> attribute) {
3232
super(et, attribute);
33-
this.values = CollectionFactory.createDefaultCollection(attribute.getCollectionType());
33+
this.values = CollectionFactory.createDefaultQueryCollection(attribute.getCollectionType());
3434
}
3535

3636
@Override

jopa-impl/src/main/java/cz/cvut/kbss/jopa/proxy/lazy/LazyLoadingProxyFactory.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@
1818
package cz.cvut.kbss.jopa.proxy.lazy;
1919

2020
import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;
21+
import cz.cvut.kbss.jopa.model.metamodel.CollectionType;
2122
import cz.cvut.kbss.jopa.model.metamodel.FieldSpecification;
2223
import cz.cvut.kbss.jopa.model.metamodel.PluralAttribute;
24+
import cz.cvut.kbss.jopa.model.metamodel.PluralQueryAttribute;
2325
import cz.cvut.kbss.jopa.proxy.lazy.gen.LazyLoadingEntityProxy;
2426
import cz.cvut.kbss.jopa.sessions.UnitOfWork;
2527

@@ -59,16 +61,26 @@ public <T> Object createProxy(T entity, FieldSpecification<? super T, ?> fieldSp
5961
} else if (Map.class.isAssignableFrom(type)) {
6062
return new LazyLoadingMapProxy<>(entity, (FieldSpecification) fieldSpec, uow);
6163
} else if (Collection.class.isAssignableFrom(type)) {
62-
final PluralAttribute<? super T, ?, ?> pa = (PluralAttribute<? super T, ?, ?>) fieldSpec;
63-
return switch (pa.getCollectionType()) {
64+
CollectionType collectionType;
65+
assert fieldSpec instanceof PluralAttribute<?, ?, ?> || fieldSpec instanceof PluralQueryAttribute<?, ?, ?>;
66+
67+
if (fieldSpec instanceof PluralAttribute<? super T, ?, ?> pa) {
68+
collectionType = pa.getCollectionType();
69+
} else {
70+
final PluralQueryAttribute<? super T, ?, ?> pa = (PluralQueryAttribute<? super T, ?, ?>) fieldSpec;
71+
collectionType = pa.getCollectionType();
72+
}
73+
74+
return switch (collectionType) {
6475
case LIST -> new LazyLoadingListProxy<>(entity, (FieldSpecification) fieldSpec, uow);
6576
case SET, COLLECTION -> new LazyLoadingSetProxy<>(entity, (FieldSpecification) fieldSpec, uow);
6677
default -> throw new IllegalArgumentException("Unsupported collection type for lazy proxying.");
6778
};
68-
}else if (uow.getMetamodel().isEntityType(type)) {
79+
} else if (uow.getMetamodel().isEntityType(type)) {
6980
try {
7081
final Class<?> proxyType = uow.getMetamodel().getLazyLoadingProxy(type);
71-
final LazyLoadingEntityProxy<?> proxy = (LazyLoadingEntityProxy<?>) proxyType.getDeclaredConstructor().newInstance();
82+
final LazyLoadingEntityProxy<?> proxy = (LazyLoadingEntityProxy<?>) proxyType.getDeclaredConstructor()
83+
.newInstance();
7284
proxy.setOwner(entity);
7385
proxy.setPersistenceContext(uow);
7486
proxy.setFieldSpec(fieldSpec);

jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/soql/SoqlQueryListener.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -784,19 +784,33 @@ public String getSparqlQuery() {
784784
return sparql;
785785
}
786786

787+
private String getSelectParameter(SoqlAttribute attribute) {
788+
SoqlNode firstNode = attribute.getFirstNode();
789+
if(!firstNode.hasChild()) {
790+
return rootVariable;
791+
}
792+
793+
setIris(firstNode); // we have to reassign the iris, because the table was not initialized when the first attribute was set
794+
return attribute.getAsValue(rootVariable);
795+
}
796+
787797
//Methods to build new Query
788798
private void buildSparqlQueryString() {
789799
if (attributes.isEmpty()) {
790800
return;
791801
}
802+
803+
// the first attribute is either a projected parameter or a type attribute
804+
String selectParameter = getSelectParameter(attributes.get(0));
805+
792806
StringBuilder newQueryBuilder = new StringBuilder(typeDef);
793807
if (isSelectedParamCount) {
794-
newQueryBuilder.append(getCountPart());
808+
newQueryBuilder.append(getCountPart(selectParameter));
795809
} else {
796810
if (isSelectedParamDistinct) {
797811
newQueryBuilder.append(' ').append(SoqlConstants.DISTINCT);
798812
}
799-
newQueryBuilder.append(' ').append(rootVariable).append(' ');
813+
newQueryBuilder.append(' ').append(selectParameter).append(' ');
800814
}
801815
newQueryBuilder.append("WHERE { ");
802816
newQueryBuilder.append(processSupremeAttributes());
@@ -818,12 +832,12 @@ private void buildSparqlQueryString() {
818832
LOG.trace("Translated SOQL query '{}' to SPARQL '{}'.", soql, sparql);
819833
}
820834

821-
private StringBuilder getCountPart() {
835+
private StringBuilder getCountPart(String selectParameter) {
822836
StringBuilder countPart = new StringBuilder(" (COUNT(");
823837
if (isSelectedParamDistinct) {
824838
countPart.append(SoqlConstants.DISTINCT).append(' ');
825839
}
826-
countPart.append(rootVariable).append(") AS ?count) ");
840+
countPart.append(selectParameter).append(") AS ?count) ");
827841
return countPart;
828842
}
829843

jopa-impl/src/main/java/cz/cvut/kbss/jopa/query/sparql/SparqlQueryParser.java

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ public class SparqlQueryParser implements QueryParser {
5454
private ParamType currentParamType;
5555
private StringBuilder currentWord;
5656
private boolean inProjection;
57+
private boolean inComment;
58+
private boolean inUri;
5759

5860
public SparqlQueryParser(ParameterValueFactory parameterValueFactory) {
5961
this.parameterValueFactory = parameterValueFactory;
@@ -67,45 +69,46 @@ private enum ParamType {
6769
@Override
6870
public SparqlQueryHolder parseQuery(String query) {
6971
this.query = query;
70-
this.queryParts = new ArrayList<>();
71-
this.uniqueParams = new HashMap<>();
72-
this.positionalCounter = 1;
73-
this.parameters = new ArrayList<>();
74-
this.inSQString = false;
75-
// In double-quoted string
76-
this.inDQString = false;
77-
this.inParam = false;
78-
this.lastParamEndIndex = 0;
79-
this.paramStartIndex = 0;
80-
this.currentParamType = null;
81-
this.currentWord = new StringBuilder();
72+
resetParser();
8273
int i;
8374
for (i = 0; i < query.length(); i++) {
8475
final char c = query.charAt(i);
8576
switch (c) {
77+
case '#':
78+
startComment(i, c);
79+
break;
8680
case '\'':
87-
inSQString = !inSQString;
81+
this.inSQString = inComment == inSQString; // ~ inComment ? inSQString : !inSQString
8882
break;
8983
case '"':
90-
inDQString = !inDQString;
84+
this.inDQString = inComment == inDQString; // ~ inComment ? inDQString : !inDQString
9185
break;
9286
case '$':
93-
parameterStart(i, ParamType.POSITIONAL);
87+
if (!inComment) {
88+
parameterStart(i, ParamType.POSITIONAL);
89+
}
9490
break;
9591
case '?':
92+
queryVariableStart(i);
93+
break;
94+
case '<':
95+
this.inUri = true;
9696
if (inParam) {
97-
parameterEnd(i); // Property path zero or one
98-
} else {
99-
parameterStart(i, ParamType.NAMED);
97+
parameterEnd(i);
10098
}
99+
wordEnd();
101100
break;
102-
case '{':
103-
this.inProjection = false; // Intentional fall-through
104-
case '<':
105101
case '>':
106-
case ',':
102+
this.inUri = false;
103+
wordEnd();
104+
break;
107105
case '\n':
106+
this.inComment = false; // Intentional fall-through
107+
case '{':
108+
// ~ inComment ? inProjection : !inProjection
109+
this.inProjection = inComment && inProjection; // Intentional fall-through
108110
case '\r':
111+
case ',':
109112
case ')':
110113
case ' ':
111114
case '.':
@@ -135,11 +138,49 @@ public SparqlQueryHolder parseQuery(String query) {
135138
return new SparqlQueryHolder(query, queryParts, parameters);
136139
}
137140

141+
private void resetParser() {
142+
this.queryParts = new ArrayList<>();
143+
this.uniqueParams = new HashMap<>();
144+
this.positionalCounter = 1;
145+
this.parameters = new ArrayList<>();
146+
this.inSQString = false;
147+
// In double-quoted string
148+
this.inDQString = false;
149+
this.inParam = false;
150+
this.inUri = false;
151+
this.inComment = false;
152+
this.lastParamEndIndex = 0;
153+
this.paramStartIndex = 0;
154+
this.currentParamType = null;
155+
this.currentWord = new StringBuilder();
156+
}
157+
158+
private void startComment(int index, char c) {
159+
if (!inUri) {
160+
if (inParam && !inComment) {
161+
parameterEnd(index);
162+
}
163+
this.inComment = true;
164+
} else {
165+
currentWord.append(c);
166+
}
167+
}
168+
169+
private void queryVariableStart(int i) {
170+
if (!inComment) {
171+
if (inParam) {
172+
parameterEnd(i); // Property path zero or one
173+
} else {
174+
parameterStart(i, ParamType.NAMED);
175+
}
176+
}
177+
}
178+
138179
private void parameterStart(int index, ParamType paramType) {
139180
if (!inSQString && !inDQString) {
140181
queryParts.add(query.substring(lastParamEndIndex, index));
141-
paramStartIndex = index + 1;
142-
inParam = true;
182+
this.paramStartIndex = index + 1;
183+
this.inParam = true;
143184
this.currentParamType = paramType;
144185
}
145186
}
@@ -200,6 +241,6 @@ private void wordEnd() {
200241
} else if (inProjection && SparqlConstants.WHERE.equalsIgnoreCase(currentWord.toString())) {
201242
this.inProjection = false;
202243
}
203-
currentWord = new StringBuilder();
244+
this.currentWord = new StringBuilder();
204245
}
205246
}

0 commit comments

Comments
 (0)