Skip to content

Commit

Permalink
Query DSL: Added matches support for PolyString
Browse files Browse the repository at this point in the history
Matches for polystring is supported in following forms:
name matches (orig = "Foo" ) - this is equivalent to PolyStringOrig matching strategy
name matches (norm = "foo" ) this is equivalent to PolyStringNorm matching strategy
name matches (orig = "Foo" and norm="foo") this is equivalent to full polystring matching strategy

Signed-off-by: Tony Tkacik <tonydamage@gmail.com>
  • Loading branch information
tonydamage committed Jan 12, 2021
1 parent afb3d22 commit 82af262
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 13 deletions.
Expand Up @@ -23,6 +23,7 @@
import com.evolveum.axiom.lang.antlr.query.AxiomQueryParser.ValueSpecificationContext;
import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
Expand Down Expand Up @@ -113,17 +114,18 @@ ObjectFilter valueFilter(PrismPropertyDefinition<?> definition, ItemPath path, Q
}
};

private static final QName EQUALS_NAME = queryName("equals");

private static final Map<String, QName> ALIASES_TO_NAME = ImmutableMap.<String, QName>builder()
.put("=", queryName("equal"))
.put("=", queryName("equals"))
.put("<", queryName("less"))
.put(">", queryName("greater"))
.put("<=", queryName("lessOrEquals"))
.put(">=", queryName("greaterOrEquals"))
.build();

private final Map<QName, ItemFilterFactory> filterFactories = ImmutableMap.<QName, ItemFilterFactory>builder()
.put(queryName("equal"), new PropertyFilterFactory() {
.put(queryName("equals"), new PropertyFilterFactory() {

@Override
public ObjectFilter valueFilter(PrismPropertyDefinition<?> definition, ItemPath path, QName matchingRule,
Expand Down Expand Up @@ -218,9 +220,8 @@ public ObjectFilter create(PrismContainerDefinition<?> parentDef, ItemPath itemP
.build()
;



private final PrismContext context;
private Map<String, String> namespaceContext;

public PrismQueryLanguageParser(PrismContext context) {
this.context = context;
Expand Down Expand Up @@ -258,6 +259,8 @@ private ObjectFilter parseFilter(PrismContainerDefinition<?> complexType, Filter
throw new IllegalStateException("Unsupported Filter Context");
}



private ObjectFilter andFilter(PrismContainerDefinition<?> complexType, AndFilterContext root) throws SchemaException {
Builder<ObjectFilter> filters = ImmutableList.builder();
filters.addAll(expandIf(AndFilter.class, parseFilter(complexType,root.left)));
Expand Down Expand Up @@ -316,7 +319,7 @@ private ObjectFilter createItemFilter(ItemFilterFactory factory, PrismContainerD

private ItemPath path(PrismContainerDefinition<?> complexType, PathContext path) {
// FIXME: Implement proper parsing of decomposed item path from Antlr
UniformItemPath ret = ItemPathHolder.parseFromString(path.getText());
UniformItemPath ret = ItemPathHolder.parseFromString(path.getText(), namespaceContext);
return ret;
}

Expand Down Expand Up @@ -345,12 +348,16 @@ ObjectFilter createEqual(PrismPropertyDefinition<?> definition, ItemPath path, Q

private Object parseLiteral(PrismPropertyDefinition<?> definition, LiteralValueContext string) {
Class<?> targetType = definition.getTypeClass();
String text = extractText(string);
return parseLiteral(targetType, string);
}

private Object parseLiteral(Class<?> targetType, LiteralValueContext string) {
String text = extractTextForm(string);
return XmlTypeConverter.toJavaValue(text, new HashMap<>(), targetType);
}


private String extractText(LiteralValueContext string) {
private String extractTextForm(LiteralValueContext string) {
if(string instanceof StringValueContext) {
return AxiomAntlrLiterals.convertString((StringValueContext) string);
}
Expand All @@ -374,9 +381,66 @@ private ObjectFilter matchesFilter(PrismContainerDefinition<?> parent, ItemPath
throw new UnsupportedOperationException("Unknown schema type");
}

/**
*
* <code>
* name matches (orig = "foo")
* name matches (norm = "bar")
* name matches (orig = "foo" and norm = "bar")
*
* </code>
*
* @param path
* @param definition
* @param filter
* @return
* @throws SchemaException
*/
private ObjectFilter matchesPolystringFilter(ItemPath path, PrismPropertyDefinition<?> definition,
FilterContext filter) {
throw new UnsupportedOperationException("Matches for reference not implemented");
FilterContext filter) throws SchemaException {
String orig = null;
String norm = null;

if (filter instanceof AndFilterContext) {
AndFilterContext andFilter = (AndFilterContext) filter;
orig = valueFromFilters(String.class, "orig", andFilter.left, andFilter.right);
norm = valueFromFilters(String.class, "norm", andFilter.left, andFilter.right);
schemaCheck(orig != null, "orig must be defined in matches polystring filter.");
schemaCheck(norm != null, "norm must be defined in matches polystring filter.");
PolyString value = new PolyString(orig, norm);
return EqualFilterImpl.createEqual(path, definition, null, context, value);
} else if (filter instanceof GenFilterContext) {
orig = valueFromFilters(String.class, "orig", filter);
norm = valueFromFilters(String.class, "norm", filter);

if(norm != null) {
return EqualFilterImpl.createEqual(path, definition, PrismConstants.POLY_STRING_NORM_MATCHING_RULE_NAME, context, new PolyString(norm, norm));
} else if (orig != null) {
return EqualFilterImpl.createEqual(path, definition, PrismConstants.POLY_STRING_ORIG_MATCHING_RULE_NAME, context, new PolyString(orig, norm));
}
}
throw new SchemaException("Incorrect syntax for matches polystring");
}

private <T> T valueFromFilters(Class<T> type, String name, FilterContext... children) throws SchemaException {
for(FilterContext child : children) {
if (child instanceof GenFilterContext) {
ItemFilterContext filter = ((GenFilterContext) child).itemFilter();
if (EQUALS_NAME.equals(filterName(filter.filterName()))) {
if (name.equals(filter.path().getText())) {
return extractValue(type, filter.subfilterOrValue());
}
}
}
}
return null;
}

private <T> T extractValue(Class<T> type, SubfilterOrValueContext subfilterOrValue) throws SchemaException {
schemaCheck(subfilterOrValue.valueSpecification() != null, "Literal value required");
LiteralValueContext literalContext = subfilterOrValue.valueSpecification().literalValue();
schemaCheck(literalContext != null, "Literal value required");
return type.cast(parseLiteral(type, literalContext));
}

private ObjectFilter matchesReferenceFilter(ItemPath path, PrismReferenceDefinition definition,
Expand Down
Expand Up @@ -61,8 +61,14 @@ private void verify(String query, ObjectFilter original) throws SchemaException,

private void verify(String query, ObjectFilter original, PrismObject<?> user) throws SchemaException {
ObjectFilter dslFilter = parse(query);
assertEquals(dslFilter.toString(), original.toString());
assertEquals(ObjectQuery.match(user, dslFilter, MATCHING_RULE_REGISTRY), ObjectQuery.match(user, original, MATCHING_RULE_REGISTRY));
boolean javaResult = ObjectQuery.match(user, original, MATCHING_RULE_REGISTRY);
boolean dslResult = ObjectQuery.match(user, dslFilter, MATCHING_RULE_REGISTRY);
try {
assertEquals(dslFilter.toString(), original.toString());
assertEquals(dslResult, javaResult, "Filters do not match.");
} catch (AssertionError e) {
throw new AssertionError(e.getMessage() + "for filter: \n " + query);
}
}

@Test
Expand Down Expand Up @@ -170,6 +176,15 @@ public void testPolystringMatchEqualFilter() throws Exception {
.item(UserType.F_NAME).eq(name)
.buildFilter();
verify("name matches (orig = \"jack\" and norm = \"jack\")", filter);
verify("name matches (norm = \"jack\")",
getPrismContext().queryFor(UserType.class)
.item(UserType.F_NAME).eq(name).matchingNorm()
.buildFilter());
verify("name matches (orig = \"jack\")",
getPrismContext().queryFor(UserType.class)
.item(UserType.F_NAME).eq(name).matchingOrig()
.buildFilter());

}


Expand Down Expand Up @@ -248,7 +263,7 @@ public void testRefRelationNegative() throws Exception {
public void testGtFilter() throws Exception {
ObjectFilter filter =
getPrismContext().queryFor(UserType.class)
.item(UserType.F_NAME).gt("j").matchingOrig()
.item(UserType.F_NAME).gt(new PolyString("j")).matchingOrig()
.buildFilter();
verify("name >[polyStringOrig] \"j\"",filter);
}
Expand All @@ -257,7 +272,7 @@ public void testGtFilter() throws Exception {
public void testLtFilter() throws Exception {
ObjectFilter filter =
getPrismContext().queryFor(UserType.class)
.item(UserType.F_NAME).lt("j").matchingNorm()
.item(UserType.F_NAME).lt(new PolyString("j")).matchingNorm()
.buildFilter();
verify("name <[polyStringNorm] \"j\"",filter);
}
Expand Down

0 comments on commit 82af262

Please sign in to comment.