diff --git a/src/main/java/net/fortuna/ical4j/filter/AbstractFilter.java b/src/main/java/net/fortuna/ical4j/filter/AbstractFilter.java index fe23ed562..997fe9ab1 100644 --- a/src/main/java/net/fortuna/ical4j/filter/AbstractFilter.java +++ b/src/main/java/net/fortuna/ical4j/filter/AbstractFilter.java @@ -37,7 +37,7 @@ import net.fortuna.ical4j.data.DefaultPropertyFactorySupplier; import net.fortuna.ical4j.filter.expression.BinaryExpression; import net.fortuna.ical4j.filter.expression.LiteralExpression; -import net.fortuna.ical4j.filter.expression.SpecificationExpression; +import net.fortuna.ical4j.filter.expression.TargetExpression; import net.fortuna.ical4j.filter.expression.UnaryExpression; import net.fortuna.ical4j.model.Parameter; import net.fortuna.ical4j.model.ParameterBuilder; @@ -59,13 +59,13 @@ protected V literal(FilterExpression expression) { throw new IllegalArgumentException("Not a valid filter"); } - protected FilterSpec specification(FilterExpression expression) { + protected FilterTarget target(FilterExpression expression) { if (expression instanceof UnaryExpression - && ((UnaryExpression) expression).operand instanceof SpecificationExpression) { - return ((SpecificationExpression) ((UnaryExpression) expression).operand).getValue(); + && ((UnaryExpression) expression).operand instanceof TargetExpression) { + return ((TargetExpression) ((UnaryExpression) expression).operand).getValue(); } else if (expression instanceof BinaryExpression - && ((BinaryExpression) expression).left instanceof SpecificationExpression) { - return ((SpecificationExpression) ((BinaryExpression) expression).left).getValue(); + && ((BinaryExpression) expression).left instanceof TargetExpression) { + return ((TargetExpression) ((BinaryExpression) expression).left).getValue(); } throw new IllegalArgumentException("Not a valid filter"); } @@ -90,7 +90,7 @@ protected Property property(FilterExpression expression) { * @return */ protected Property property(UnaryExpression expression) { - return property(specification(expression)); + return property(target(expression)); } /** @@ -100,7 +100,7 @@ protected Property property(UnaryExpression expression) { */ protected Property property(BinaryExpression expression) { // todo: support for function, integer, etc. on right side (currently only supports string) - return property(specification(expression), literal(expression)); + return property(target(expression), literal(expression)); } /** @@ -109,7 +109,7 @@ protected Property property(BinaryExpression expression) { * @return */ protected List> properties(BinaryExpression expression) { - FilterSpec operand = specification(expression); + FilterTarget operand = target(expression); List literal = literal(expression); return literal.stream().map(l -> property(operand, l)).collect(Collectors.toList()); } @@ -119,7 +119,7 @@ protected List> properties(BinaryExpression expression) { * @param operand * @return */ - protected Property property(FilterSpec operand) { + protected Property property(FilterTarget operand) { PropertyBuilder spec = new PropertyBuilder(new DefaultPropertyFactorySupplier().get()).name(operand.getName()); if (operand.getValue().isPresent()) { spec.value(operand.getValue().get()); @@ -140,7 +140,7 @@ protected Property property(FilterSpec operand) { * @param value * @return */ - protected Property property(FilterSpec operand, String value) { + protected Property property(FilterTarget operand, String value) { PropertyBuilder spec = new PropertyBuilder(new DefaultPropertyFactorySupplier().get()).name(operand.getName()); if (value != null) { spec.value(value); @@ -161,11 +161,11 @@ protected Property property(FilterSpec operand, String value) { * @return */ protected Parameter parameter(UnaryExpression expression) { - FilterSpec specification = specification(expression); + FilterTarget specification = target(expression); if (specification.getValue().isPresent()) { return parameter(specification.getName(), specification.getValue().get()); } else { - return parameter(specification(expression).getName(), null); + return parameter(target(expression).getName(), null); } } @@ -175,7 +175,7 @@ protected Parameter parameter(UnaryExpression expression) { * @return */ protected Parameter parameter(BinaryExpression expression) { - return parameter(specification(expression).getName(), literal(expression)); + return parameter(target(expression).getName(), literal(expression)); } /** @@ -185,12 +185,12 @@ protected Parameter parameter(BinaryExpression expression) { */ protected List> parameters(BinaryExpression expression) { // only applicable for operand expressions.. - FilterSpec specification = specification(expression); + FilterTarget specification = target(expression); List literal = literal(expression); return literal.stream().map(l -> parameter(specification.getName(), l)).collect(Collectors.toList()); } - protected Parameter parameter(FilterSpec.Attribute a) { + protected Parameter parameter(FilterTarget.Attribute a) { try { return new ParameterBuilder(new DefaultParameterFactorySupplier().get()) .name(a.getName()).value(a.getValue()).build(); diff --git a/src/main/java/net/fortuna/ical4j/filter/FilterExpression.java b/src/main/java/net/fortuna/ical4j/filter/FilterExpression.java index c290336ba..22232d5eb 100644 --- a/src/main/java/net/fortuna/ical4j/filter/FilterExpression.java +++ b/src/main/java/net/fortuna/ical4j/filter/FilterExpression.java @@ -5,6 +5,7 @@ import java.time.temporal.Temporal; import java.util.Collection; import java.util.Date; +import java.util.List; public interface FilterExpression { @@ -20,48 +21,52 @@ enum Op { and, or, not } - static FilterExpression equalTo(String operand, String value) { - return new BinaryExpression(new SpecificationExpression(operand), Op.equalTo, new StringExpression(value)); + static FilterExpression equalTo(String target, String value) { + return new BinaryExpression(new TargetExpression(target), Op.equalTo, new StringExpression(value)); } - static FilterExpression equalTo(String operand, Date value) { - return new BinaryExpression(new SpecificationExpression(operand), Op.equalTo, new DateExpression(value)); + static FilterExpression equalTo(String target, List attributes, String value) { + return new BinaryExpression(new TargetExpression(target, attributes), Op.equalTo, new StringExpression(value)); } - static FilterExpression equalTo(String operand, Integer value) { - return new BinaryExpression(new SpecificationExpression(operand), Op.equalTo, new NumberExpression(value)); + static FilterExpression equalTo(String target, Date value) { + return new BinaryExpression(new TargetExpression(target), Op.equalTo, new DateExpression(value)); } - static FilterExpression in(String name, Collection value) { - return new BinaryExpression(new SpecificationExpression(name), Op.in, new CollectionExpression(value)); + static FilterExpression equalTo(String target, Integer value) { + return new BinaryExpression(new TargetExpression(target), Op.equalTo, new NumberExpression(value)); } - static FilterExpression greaterThan(String name, Temporal value) { - return new BinaryExpression(new SpecificationExpression(name), Op.greaterThan, new StringExpression(value.toString())); + static FilterExpression in(String target, Collection value) { + return new BinaryExpression(new TargetExpression(target), Op.in, new CollectionExpression(value)); } - static FilterExpression greaterThan(String name, Number value) { - return new BinaryExpression(new SpecificationExpression(name), Op.greaterThan, new StringExpression(value.toString())); + static FilterExpression greaterThan(String target, Temporal value) { + return new BinaryExpression(new TargetExpression(target), Op.greaterThan, new StringExpression(value.toString())); } - static FilterExpression greaterThanEqual(String name, Temporal value) { - return new BinaryExpression(new SpecificationExpression(name), Op.greaterThanEqual, new TemporalExpression(value)); + static FilterExpression greaterThan(String target, Number value) { + return new BinaryExpression(new TargetExpression(target), Op.greaterThan, new StringExpression(value.toString())); } - static FilterExpression lessThan(String name, Temporal value) { - return new BinaryExpression(new SpecificationExpression(name), Op.lessThan, new TemporalExpression(value)); + static FilterExpression greaterThanEqual(String target, Temporal value) { + return new BinaryExpression(new TargetExpression(target), Op.greaterThanEqual, new TemporalExpression(value)); } - static FilterExpression lessThanEqual(String name, String value) { - return new BinaryExpression(new SpecificationExpression(name), Op.lessThanEqual, new StringExpression(value)); + static FilterExpression lessThan(String target, Temporal value) { + return new BinaryExpression(new TargetExpression(target), Op.lessThan, new TemporalExpression(value)); } - static FilterExpression contains(String name, String value) { - return new BinaryExpression(new SpecificationExpression(name), Op.contains, new StringExpression(value)); + static FilterExpression lessThanEqual(String target, String value) { + return new BinaryExpression(new TargetExpression(target), Op.lessThanEqual, new StringExpression(value)); } - static FilterExpression matches(String name, String value) { - return new BinaryExpression(new SpecificationExpression(name), Op.matches, new StringExpression(value)); + static FilterExpression contains(String target, String value) { + return new BinaryExpression(new TargetExpression(target), Op.contains, new StringExpression(value)); + } + + static FilterExpression matches(String target, String value) { + return new BinaryExpression(new TargetExpression(target), Op.matches, new StringExpression(value)); } default FilterExpression and(FilterExpression expression) { @@ -76,12 +81,12 @@ static FilterExpression not(FilterExpression expression) { return new UnaryExpression(Op.not, expression); } - static FilterExpression exists(String name) { - return new UnaryExpression(Op.exists, new SpecificationExpression(name)); + static FilterExpression exists(String target) { + return new UnaryExpression(Op.exists, new TargetExpression(target)); } - static FilterExpression notExists(String name) { - return new UnaryExpression(Op.notExists, new SpecificationExpression(name)); + static FilterExpression notExists(String target) { + return new UnaryExpression(Op.notExists, new TargetExpression(target)); } static FilterExpression parse(String filterExpression) { diff --git a/src/main/java/net/fortuna/ical4j/filter/FilterExpressionParser.java b/src/main/java/net/fortuna/ical4j/filter/FilterExpressionParser.java index 0cffc2e94..3169c39ad 100644 --- a/src/main/java/net/fortuna/ical4j/filter/FilterExpressionParser.java +++ b/src/main/java/net/fortuna/ical4j/filter/FilterExpressionParser.java @@ -3,8 +3,8 @@ import net.fortuna.ical4j.filter.FilterExpression.Op; import net.fortuna.ical4j.filter.expression.BinaryExpression; import net.fortuna.ical4j.filter.expression.NumberExpression; -import net.fortuna.ical4j.filter.expression.SpecificationExpression; import net.fortuna.ical4j.filter.expression.StringExpression; +import net.fortuna.ical4j.filter.expression.TargetExpression; import net.fortuna.ical4j.model.TemporalAmountAdapter; import org.jparsec.*; @@ -47,19 +47,20 @@ public class FilterExpressionParser { Terminals.IntegerLiteral.TOKENIZER, Terminals.StringLiteral.SINGLE_QUOTE_TOKENIZER, TERMS.tokenizer()); - static final Parser ATTRIBUTE_PARSER = Parsers.sequence(Terminals.Identifier.PARSER, - term(":"), Terminals.Identifier.PARSER); + static final Parser ATTRIBUTE_PARSER = Parsers.sequence(Terminals.Identifier.PARSER, + term(":"), Terminals.Identifier.PARSER, (name, x, value) -> new FilterTarget.Attribute(name, value)) + .or(Terminals.Identifier.PARSER.map(FilterTarget.Attribute::new)); - static final Parser> ATTRIBUTE_LIST_PARSER = ATTRIBUTE_PARSER + static final Parser> ATTRIBUTE_LIST_PARSER = ATTRIBUTE_PARSER .between(term("["), term("]")).sepBy(term(",")); static final Parser NUMBER = Terminals.IntegerLiteral.PARSER.map(NumberExpression::new); static final Parser STRING = Terminals.StringLiteral.PARSER.map(StringExpression::new); - static final Parser NAME = Parsers.sequence( - Terminals.Identifier.PARSER, ATTRIBUTE_LIST_PARSER, (name, attr) -> new SpecificationExpression(name)) - .or(Terminals.Identifier.PARSER.map(SpecificationExpression::new)); + static final Parser NAME = Parsers.sequence( + Terminals.Identifier.PARSER, ATTRIBUTE_LIST_PARSER, TargetExpression::new) + .or(Terminals.Identifier.PARSER.map(TargetExpression::new)); // static final Parser NAME = Terminals.Identifier.PARSER // .map(SpecificationExpression::new); diff --git a/src/main/java/net/fortuna/ical4j/filter/FilterSpec.java b/src/main/java/net/fortuna/ical4j/filter/FilterTarget.java similarity index 82% rename from src/main/java/net/fortuna/ical4j/filter/FilterSpec.java rename to src/main/java/net/fortuna/ical4j/filter/FilterTarget.java index 363656433..3c4c18301 100644 --- a/src/main/java/net/fortuna/ical4j/filter/FilterSpec.java +++ b/src/main/java/net/fortuna/ical4j/filter/FilterTarget.java @@ -38,7 +38,7 @@ import java.util.Objects; import java.util.Optional; -public class FilterSpec { +public class FilterTarget { private final String name; @@ -46,11 +46,11 @@ public class FilterSpec { private final List attributes; - public FilterSpec(String spec) { + public FilterTarget(String spec) { this(spec, Collections.emptyList()); } - public FilterSpec(String spec, List attributes) { + public FilterTarget(String spec, List attributes) { this.name = spec.split(":")[0].replace("_", "-"); this.value = Optional.ofNullable(spec.split(":").length > 1 ? spec.split(":")[1] : null); this.attributes = attributes; @@ -72,7 +72,7 @@ public List getAttributes() { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - FilterSpec that = (FilterSpec) o; + FilterTarget that = (FilterTarget) o; return name.equals(that.name) && Objects.equals(value, that.value) && Objects.equals(attributes, that.attributes); } @@ -87,6 +87,10 @@ public static class Attribute { private String value; + public Attribute(String name) { + this.name = name; + } + public Attribute(String name, String value) { this.name = name; this.value = value; @@ -100,6 +104,19 @@ public String getValue() { return value; } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Attribute attribute = (Attribute) o; + return name.equals(attribute.name) && Objects.equals(value, attribute.value); + } + + @Override + public int hashCode() { + return Objects.hash(name, value); + } + public static Attribute parse(String string) { String name = string.split(":")[0]; String value = string.contains(":") ? string.split(":")[1] : null; diff --git a/src/main/java/net/fortuna/ical4j/filter/expression/SpecificationExpression.java b/src/main/java/net/fortuna/ical4j/filter/expression/TargetExpression.java similarity index 80% rename from src/main/java/net/fortuna/ical4j/filter/expression/SpecificationExpression.java rename to src/main/java/net/fortuna/ical4j/filter/expression/TargetExpression.java index 5721ddb5f..26c825e99 100644 --- a/src/main/java/net/fortuna/ical4j/filter/expression/SpecificationExpression.java +++ b/src/main/java/net/fortuna/ical4j/filter/expression/TargetExpression.java @@ -33,19 +33,24 @@ package net.fortuna.ical4j.filter.expression; import net.fortuna.ical4j.filter.FilterExpression; -import net.fortuna.ical4j.filter.FilterSpec; +import net.fortuna.ical4j.filter.FilterTarget; +import java.util.List; import java.util.Objects; -public class SpecificationExpression implements FilterExpression { +public class TargetExpression implements FilterExpression { - public final FilterSpec value; + public final FilterTarget value; - public SpecificationExpression(String value) { - this.value = new FilterSpec(value); + public TargetExpression(String value) { + this.value = new FilterTarget(value); } - public FilterSpec getValue() { + public TargetExpression(String value, List attributes) { + this.value = new FilterTarget(value, attributes); + } + + public FilterTarget getValue() { return value; } @@ -53,7 +58,7 @@ public FilterSpec getValue() { public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; - SpecificationExpression that = (SpecificationExpression) o; + TargetExpression that = (TargetExpression) o; return value.equals(that.value); } diff --git a/src/test/groovy/net/fortuna/ical4j/filter/FilterExpressionParserTest.groovy b/src/test/groovy/net/fortuna/ical4j/filter/FilterExpressionParserTest.groovy index 9708c10e8..fa4772735 100644 --- a/src/test/groovy/net/fortuna/ical4j/filter/FilterExpressionParserTest.groovy +++ b/src/test/groovy/net/fortuna/ical4j/filter/FilterExpressionParserTest.groovy @@ -32,7 +32,7 @@ package net.fortuna.ical4j.filter import net.fortuna.ical4j.filter.expression.NumberExpression -import net.fortuna.ical4j.filter.expression.SpecificationExpression +import net.fortuna.ical4j.filter.expression.TargetExpression import org.jparsec.Parser import spock.lang.Specification @@ -48,10 +48,10 @@ class FilterExpressionParserTest extends Specification { where: expression | expectedResult '1' | new NumberExpression('1') - 'due' | new SpecificationExpression('due') + 'due' | new TargetExpression('due') "due = 12" | FilterExpression.equalTo('due', 12) "related_to = '1234-1234-1234'" | FilterExpression.equalTo("related_to", '1234-1234-1234') - "related_to[rel_type:SIBLING] = '1234-1234-1234'" | FilterExpression.equalTo("related_to", '1234-1234-1234') - "attendee[role:CHAIR] = '1234-1234-1234'" | FilterExpression.equalTo("attendee", '1234-1234-1234') + "related_to[rel_type:SIBLING] = '1234-1234-1234'" | FilterExpression.equalTo("related_to", Collections.singletonList(new FilterTarget.Attribute("rel_type", "SIBLING")), '1234-1234-1234') + "attendee[role:CHAIR] = '1234-1234-1234'" | FilterExpression.equalTo("attendee", Collections.singletonList(new FilterTarget.Attribute("role", "CHAIR")), '1234-1234-1234') } }