Skip to content

Commit

Permalink
[Enhancement #176] Implement MEMBER OF in SOQL.
Browse files Browse the repository at this point in the history
  • Loading branch information
ledsoft committed Jul 21, 2023
1 parent 4ecb097 commit de3f5c2
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 8 deletions.
@@ -0,0 +1,13 @@
package cz.cvut.kbss.jopa.exception;

import cz.cvut.kbss.jopa.exceptions.OWLPersistenceException;

/**
* Indicates an error during parsing and translation of SOQL to SPARQL.
*/
public class SoqlException extends OWLPersistenceException {

public SoqlException(String message) {
super(message);
}
}
@@ -0,0 +1,32 @@
package cz.cvut.kbss.jopa.query.soql;

/**
* SOQL ({@code NOT}) {@code MEMBER OF} operator.
*/
public class MemberOfOperator implements FilterableExpression {

private final boolean isNot;

private MemberOfOperator(boolean isNot) {
this.isNot = isNot;
}

@Override
public String toFilterExpression(String parameter, String value) {
// TODO FILTER NOT EXISTS
return "";
}

@Override
public boolean requiresFilterExpression() {
return isNot;
}

static MemberOfOperator memberOf() {
return new MemberOfOperator(false);
}

static MemberOfOperator notMemberOf() {
return new MemberOfOperator(true);
}
}
Expand Up @@ -14,6 +14,7 @@
*/
package cz.cvut.kbss.jopa.query.soql;

import cz.cvut.kbss.jopa.query.sparql.SparqlConstants;
import cz.cvut.kbss.jopa.utils.IdentifierTransformer;

import java.util.Collections;
Expand Down Expand Up @@ -162,6 +163,7 @@ public String getBasicGraphPattern(String rootVariable) {
}

private static String toIri(SoqlNode node) {
return IdentifierTransformer.stringifyIri(node.getIri());
final String nodeIri = node.getIri();
return SparqlConstants.RDF_TYPE_SHORTCUT.equals(nodeIri) ? nodeIri : IdentifierTransformer.stringifyIri(nodeIri);
}
}
Expand Up @@ -14,13 +14,15 @@
*/
package cz.cvut.kbss.jopa.query.soql;

import cz.cvut.kbss.jopa.exception.SoqlException;
import cz.cvut.kbss.jopa.model.MetamodelImpl;
import cz.cvut.kbss.jopa.model.metamodel.Attribute;
import cz.cvut.kbss.jopa.model.metamodel.EntityType;
import cz.cvut.kbss.jopa.model.metamodel.IdentifiableEntityType;
import cz.cvut.kbss.jopa.model.metamodel.PluralAttribute;
import cz.cvut.kbss.jopa.model.metamodel.SingularAttribute;
import cz.cvut.kbss.jopa.model.metamodel.Type;
import cz.cvut.kbss.jopa.query.sparql.SparqlConstants;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ErrorNode;
import org.antlr.v4.runtime.tree.ParseTree;
Expand Down Expand Up @@ -366,6 +368,26 @@ public void exitLikeExpression(SoqlParser.LikeExpressionContext ctx) {
this.isInObjectIdentifierExpression = false;
}

@Override
public void enterMemberOfExpression(SoqlParser.MemberOfExpressionContext ctx) {

}

@Override
public void exitMemberOfExpression(SoqlParser.MemberOfExpressionContext ctx) {
if (ctx.getChildCount() > 2 && ctx.getChild(1).getText().equals(SoqlConstants.NOT)) {
// TODO
attrPointer.setOperator(MemberOfOperator.notMemberOf());
ParseTree whereClauseValue = ctx.getChild(3);
attrPointer.setValue(whereClauseValue.getText());
} else {
attrPointer.setOperator(MemberOfOperator.memberOf());
ParseTree whereClauseValue = ctx.getChild(0);
attrPointer.setValue(whereClauseValue.getText());
}
this.isInObjectIdentifierExpression = false;
}

@Override
public void enterComparisonExpression(SoqlParser.ComparisonExpressionContext ctx) {
}
Expand Down Expand Up @@ -689,10 +711,19 @@ private IdentifiableEntityType<?> getEntityType(String name) {
}

private void setAllNodesIris(EntityType<?> entityType, SoqlNode node) {
if (entityType.getIdentifier().getName().equals(node.getValue())) {
final String nodeName = node.getValue();
if (entityType.getIdentifier().getName().equals(nodeName)) {
return;
}
final Attribute<?, ?> abstractAttribute = entityType.getAttribute(node.getValue());
if (abstractAttribute == null) {
if (entityType.getTypes() != null && entityType.getTypes().getName().equals(node.getValue())) {
node.setIri(SparqlConstants.RDF_TYPE_SHORTCUT);
return;
} else {
throw new SoqlException("No matching attribute with name '" + node.getValue() + "' found on entity type '" + entityType.getName() + "'.");
}
}
//not implemented case of 3 or more fragments (chained SoqlNodes)
node.setIri(abstractAttribute.getIRI().toString());
if (node.hasChild()) {
Expand Down
@@ -0,0 +1,26 @@
package cz.cvut.kbss.jopa.query.sparql;

/**
* Constants of SPARQL.
*/
public class SparqlConstants {

/**
* The {@literal SELECT} keyword.
*/
public static final String SELECT = "SELECT";

/**
* The {@literal WHERE} keyword.
*/
public static final String WHERE = "WHERE";

/**
* The {@literal a} keyword representing the rdf:type IRI.
*/
public static final String RDF_TYPE_SHORTCUT = "a";

private SparqlConstants() {
throw new AssertionError();
}
}
Expand Up @@ -34,9 +34,6 @@
*/
public class SparqlQueryParser implements QueryParser {

private static final String SELECT = "SELECT";
private static final String WHERE = "WHERE";

private final ParameterValueFactory parameterValueFactory;

private String query;
Expand Down Expand Up @@ -195,9 +192,9 @@ private QueryParameter<?> getQueryParameter(Integer position) {
}

private void wordEnd() {
if (SELECT.equalsIgnoreCase(currentWord.toString())) {
if (SparqlConstants.SELECT.equalsIgnoreCase(currentWord.toString())) {
this.inProjection = true;
} else if (inProjection && WHERE.equalsIgnoreCase(currentWord.toString())) {
} else if (inProjection && SparqlConstants.WHERE.equalsIgnoreCase(currentWord.toString())) {
this.inProjection = false;
}
currentWord = new StringBuilder();
Expand Down
Expand Up @@ -14,6 +14,7 @@

import cz.cvut.kbss.jopa.environment.Vocabulary;
import cz.cvut.kbss.jopa.environment.utils.MetamodelMocks;
import cz.cvut.kbss.jopa.exception.SoqlException;
import cz.cvut.kbss.jopa.model.MetamodelImpl;
import cz.cvut.kbss.jopa.query.QueryHolder;
import cz.cvut.kbss.jopa.query.QueryParser;
Expand All @@ -31,8 +32,11 @@
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
Expand Down Expand Up @@ -767,4 +771,11 @@ void parseQuerySupportsCountById() {
final String expectedSparql = "SELECT (COUNT(?id) AS ?count) WHERE { ?id a " + strUri(Vocabulary.c_OwlClassA) + " . }";
parseAndAssertEquality(soql, expectedSparql);
}

@Test
void parseQueryThrowsSoqlExceptionWhenUnknownAttributeNameIsUsed() {
final String soql = "SELECT p FROM Person p WHERE p.unknownAttribute = :param";
final SoqlException ex = assertThrows(SoqlException.class, () -> sut.parseQuery(soql));
assertThat(ex.getMessage(), containsString("No matching attribute"));
}
}
Expand Up @@ -14,13 +14,16 @@
*/
package cz.cvut.kbss.jopa.utils;

import cz.cvut.kbss.jopa.environment.utils.Generators;
import org.junit.jupiter.api.Test;

import java.net.URI;
import java.net.URL;
import java.util.Date;

import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

class IdentifierTransformerTest {

Expand Down Expand Up @@ -53,4 +56,10 @@ void transformThrowsIllegalArgumentForUnsupportedTargetType() {
assertThrows(IllegalArgumentException.class,
() -> IdentifierTransformer.transformToIdentifier(IDENTIFIER, Date.class));
}

@Test
void stringifyIriReturnsSpecifiedIriInAngledBrackets() {
final URI uri = Generators.createIndividualIdentifier();
assertEquals("<" + uri + ">", IdentifierTransformer.stringifyIri(uri));
}
}

0 comments on commit de3f5c2

Please sign in to comment.