summaryrefslogtreecommitdiff
path: root/src/main/java/com/amazon/carbonado
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/amazon/carbonado')
-rw-r--r--src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java15
-rw-r--r--src/main/java/com/amazon/carbonado/filter/Binder.java2
-rw-r--r--src/main/java/com/amazon/carbonado/filter/ExistsFilter.java75
-rw-r--r--src/main/java/com/amazon/carbonado/filter/Filter.java37
-rw-r--r--src/main/java/com/amazon/carbonado/filter/FilterParser.java22
-rw-r--r--src/main/java/com/amazon/carbonado/filter/PropertyFilter.java2
6 files changed, 86 insertions, 67 deletions
diff --git a/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java b/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java
index 745ad7a..04ea2e8 100644
--- a/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java
+++ b/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java
@@ -349,6 +349,17 @@ class FilteredCursorGenerator {
}
public Object visit(ExistsFilter<S> filter, Object param) {
+ // Load join property value to stack.
+ CodeBuilder b = mIsAllowedBuilder;
+ loadChainedProperty(b, filter.getChainedProperty());
+
+ if (!filter.getChainedProperty().getLastProperty().isQuery()) {
+ // Checking for existence or non-existence of many-to-one join.
+ // Implement by comparing against null.
+ getScope().successIfNullElseFail(b, filter.isNotExists());
+ return null;
+ }
+
// Recursively gather all the properties to be passed to sub-filter.
final List<PropertyFilter> subPropFilters = new ArrayList<PropertyFilter>();
@@ -364,10 +375,6 @@ class FilteredCursorGenerator {
}
}, null);
- // Load join property value to stack. It is expected to be a Query.
- CodeBuilder b = mIsAllowedBuilder;
- loadChainedProperty(b, filter.getChainedProperty());
-
final TypeDesc queryType = TypeDesc.forClass(Query.class);
// Refine Query filter, if sub-filter isn't open.
diff --git a/src/main/java/com/amazon/carbonado/filter/Binder.java b/src/main/java/com/amazon/carbonado/filter/Binder.java
index 7b074c2..91a03e5 100644
--- a/src/main/java/com/amazon/carbonado/filter/Binder.java
+++ b/src/main/java/com/amazon/carbonado/filter/Binder.java
@@ -108,7 +108,7 @@ class Binder<S extends Storable> extends Visitor<S, Filter<S>, Object> {
// This should not happen.
throw new IllegalStateException(nj.toString());
}
- return ExistsFilter.getCanonical
+ return ExistsFilter.build
(filter.getChainedProperty(), nj.getNotJoinedFilter(), filter.isNotExists());
}
}
diff --git a/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java b/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java
index b75de28..0e5abf2 100644
--- a/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java
+++ b/src/main/java/com/amazon/carbonado/filter/ExistsFilter.java
@@ -28,7 +28,7 @@ import com.amazon.carbonado.info.StorableProperty;
/**
* Filter tree node that performs an existence or non-existence test against a
- * one-to-many join.
+ * join property.
*
* @author Brian S O'Neill
* @since 1.2
@@ -38,32 +38,18 @@ public class ExistsFilter<S extends Storable> extends Filter<S> {
* Returns a canonical instance, creating a new one if there isn't one
* already in the cache.
*/
- @SuppressWarnings("unchecked")
- static <S extends Storable> ExistsFilter<S> getCanonical(ChainedProperty<S> property,
- Filter<?> subFilter,
- boolean not)
+ static <S extends Storable> Filter<S> build(ChainedProperty<S> property,
+ Filter<?> subFilter,
+ boolean not)
{
- return (ExistsFilter<S>) cCanonical.put(new ExistsFilter<S>(property, subFilter, not));
- }
-
- private final ChainedProperty<S> mProperty;
- private final Filter<?> mSubFilter;
- private final boolean mNot;
-
- private transient volatile Filter<S> mJoinedSubFilter;
- private transient volatile boolean mNoParameters;
-
- ExistsFilter(ChainedProperty<S> property, Filter<?> subFilter, boolean not) {
- super(property == null ? null : property.getPrimeProperty().getEnclosingType());
+ if (property == null) {
+ throw new IllegalArgumentException();
+ }
StorableProperty<?> joinProperty = property.getLastProperty();
- if (!joinProperty.isQuery()) {
- throw new IllegalArgumentException("Not a one-to-many join property: " + property);
- }
+
if (subFilter == null) {
subFilter = Filter.getOpenFilter(joinProperty.getJoinedType());
- } else if (subFilter.isClosed()) {
- throw new IllegalArgumentException("Exists sub-filter cannot be closed: " + subFilter);
} else if (joinProperty.getJoinedType() != subFilter.getStorableType()) {
throw new IllegalArgumentException
("Filter not compatible with join property type: " +
@@ -71,20 +57,61 @@ public class ExistsFilter<S extends Storable> extends Filter<S> {
", but filter is for a " + subFilter.getStorableType().getName());
}
+ if (subFilter.isClosed()) {
+ // Exists filter reduces to a closed (or open) filter.
+ Filter<S> f = Filter.getClosedFilter(property.getPrimeProperty().getEnclosingType());
+ return not ? f.not() : f;
+ } else if (joinProperty.isQuery() || subFilter.isOpen()) {
+ return getCanonical(property, subFilter, not);
+ } else {
+ // Convert to normal join filter.
+ return subFilter.asJoinedFrom(property);
+ }
+ }
+
+ /**
+ * Returns a canonical instance, creating a new one if there isn't one
+ * already in the cache.
+ */
+ @SuppressWarnings("unchecked")
+ private static <S extends Storable> ExistsFilter<S> getCanonical(ChainedProperty<S> property,
+ Filter<?> subFilter,
+ boolean not)
+ {
+ return (ExistsFilter<S>) cCanonical.put(new ExistsFilter<S>(property, subFilter, not));
+ }
+
+ private final ChainedProperty<S> mProperty;
+ private final Filter<?> mSubFilter;
+ private final boolean mNot;
+
+ private transient volatile Filter<S> mJoinedSubFilter;
+ private transient volatile boolean mNoParameters;
+
+ private ExistsFilter(ChainedProperty<S> property, Filter<?> subFilter, boolean not) {
+ super(property.getPrimeProperty().getEnclosingType());
mProperty = property;
mSubFilter = subFilter;
mNot = not;
}
/**
- * @return chained property whose last property is a one-to-many join
+ * Returns the join property that is being checked for existence or
+ * non-existence. The last property in the chain is a one-to-many or
+ * many-to-one join, but it is a many-to-one join only if the sub-filter is
+ * also open.
+ *
+ * @return chained property whose last property is a join
*/
public ChainedProperty<S> getChainedProperty() {
return mProperty;
}
/**
- * @return filter which is applied to last property of chain, which might be open
+ * Returns the filter applied to the join, which might be open. For a
+ * many-to-one join, the sub-filter is always open.
+ *
+ * @return filter which is applied to last property of chain
*/
public Filter<?> getSubFilter() {
return mSubFilter;
diff --git a/src/main/java/com/amazon/carbonado/filter/Filter.java b/src/main/java/com/amazon/carbonado/filter/Filter.java
index 11f36d9..4f32179 100644
--- a/src/main/java/com/amazon/carbonado/filter/Filter.java
+++ b/src/main/java/com/amazon/carbonado/filter/Filter.java
@@ -282,34 +282,34 @@ public abstract class Filter<S extends Storable> implements Appender {
/**
* Returns a combined filter instance that accepts records which are only
- * accepted by this filter and the "exists" test applied to a one-to-many join.
+ * accepted by this filter and the "exists" test applied to a join.
*
- * @param propertyName one-to-many join property name, which may be a chained property
- * @param subFilter sub-filter to apply to one-to-many join, which may be
- * null to test for any existing
+ * @param propertyName join property name, which may be a chained property
+ * @param subFilter sub-filter to apply to join, which may be null to test
+ * for any existing
* @return canonical Filter instance
* @throws IllegalArgumentException if property is not found
* @since 1.2
*/
public final Filter<S> andExists(String propertyName, Filter<?> subFilter) {
ChainedProperty<S> prop = new FilterParser<S>(mType, propertyName).parseChainedProperty();
- return and(ExistsFilter.getCanonical(prop, subFilter, false));
+ return and(ExistsFilter.build(prop, subFilter, false));
}
/**
* Returns a combined filter instance that accepts records which are only
- * accepted by this filter and the "not exists" test applied to a one-to-many join.
+ * accepted by this filter and the "not exists" test applied to a join.
*
- * @param propertyName one-to-many join property name, which may be a chained property
- * @param subFilter sub-filter to apply to one-to-many join, which may be
- * null to test for any not existing
+ * @param propertyName join property name, which may be a chained property
+ * @param subFilter sub-filter to apply to join, which may be null to test
+ * for any not existing
* @return canonical Filter instance
* @throws IllegalArgumentException if property is not found
* @since 1.2
*/
public final Filter<S> andNotExists(String propertyName, Filter<?> subFilter) {
ChainedProperty<S> prop = new FilterParser<S>(mType, propertyName).parseChainedProperty();
- return and(ExistsFilter.getCanonical(prop, subFilter, true));
+ return and(ExistsFilter.build(prop, subFilter, true));
}
/**
@@ -372,36 +372,35 @@ public abstract class Filter<S extends Storable> implements Appender {
/**
* Returns a combined filter instance that accepts records which are
- * accepted either by this filter or the "exists" test applied to a
- * one-to-many join.
+ * accepted either by this filter or the "exists" test applied to a join.
*
* @param propertyName one-to-many join property name, which may be a chained property
- * @param subFilter sub-filter to apply to one-to-many join, which may be
- * null to test for any existing
+ * @param subFilter sub-filter to apply to join, which may be null to test
+ * for any existing
* @return canonical Filter instance
* @throws IllegalArgumentException if property is not found
* @since 1.2
*/
public final Filter<S> orExists(String propertyName, Filter<?> subFilter) {
ChainedProperty<S> prop = new FilterParser<S>(mType, propertyName).parseChainedProperty();
- return or(ExistsFilter.getCanonical(prop, subFilter, false));
+ return or(ExistsFilter.build(prop, subFilter, false));
}
/**
* Returns a combined filter instance that accepts records which are
* accepted either by this filter or the "not exists" test applied to a
- * one-to-many join.
+ * join.
*
* @param propertyName one-to-many join property name, which may be a chained property
- * @param subFilter sub-filter to apply to one-to-many join, which may be
- * null to test for any not existing
+ * @param subFilter sub-filter to apply to join, which may be null to test
+ * for any not existing
* @return canonical Filter instance
* @throws IllegalArgumentException if property is not found
* @since 1.2
*/
public final Filter<S> orNotExists(String propertyName, Filter<?> subFilter) {
ChainedProperty<S> prop = new FilterParser<S>(mType, propertyName).parseChainedProperty();
- return or(ExistsFilter.getCanonical(prop, subFilter, true));
+ return or(ExistsFilter.build(prop, subFilter, true));
}
/**
diff --git a/src/main/java/com/amazon/carbonado/filter/FilterParser.java b/src/main/java/com/amazon/carbonado/filter/FilterParser.java
index bb7dac8..5d3762e 100644
--- a/src/main/java/com/amazon/carbonado/filter/FilterParser.java
+++ b/src/main/java/com/amazon/carbonado/filter/FilterParser.java
@@ -132,27 +132,13 @@ class FilterParser<S extends Storable> {
return parsePropertyFilter(chained);
}
- boolean isExistsFilter = chained.getLastProperty().isQuery();
-
- Filter<S> chainedFilter;
+ Filter<?> subFilter;
c = nextCharIgnoreWhitespace();
if (c == ')') {
- if (isExistsFilter) {
- chainedFilter = ExistsFilter.getCanonical(chained, null, false);
- } else {
- // FIXME: support exists filter for this case
- mPos--;
- throw error("Property \"" + chained +
- "\" is a many-to-one join and requires property filters");
- }
+ subFilter = null;
} else {
mPos--;
- Filter<?> cf = parseChainedFilter(chained);
- if (isExistsFilter) {
- chainedFilter = ExistsFilter.getCanonical(chained, cf, false);
- } else {
- chainedFilter = cf.asJoinedFrom(chained);
- }
+ subFilter = parseChainedFilter(chained);
c = nextCharIgnoreWhitespace();
if (c != ')') {
mPos--;
@@ -160,7 +146,7 @@ class FilterParser<S extends Storable> {
}
}
- return chainedFilter;
+ return ExistsFilter.build(chained, subFilter, false);
}
}
diff --git a/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java b/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java
index 97d508a..c4a585d 100644
--- a/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java
+++ b/src/main/java/com/amazon/carbonado/filter/PropertyFilter.java
@@ -97,7 +97,7 @@ public class PropertyFilter<S extends Storable> extends Filter<S> {
*/
private PropertyFilter(ChainedProperty<S> property, RelOp op, int bindID, Object constant) {
super(property == null ? null : property.getPrimeProperty().getEnclosingType());
- if (op == null) {
+ if (property == null || op == null) {
throw new IllegalArgumentException();
}
mProperty = property;