diff options
Diffstat (limited to 'src/main/java/com/amazon')
3 files changed, 76 insertions, 15 deletions
| diff --git a/src/main/java/com/amazon/carbonado/cursor/FilteredCursor.java b/src/main/java/com/amazon/carbonado/cursor/FilteredCursor.java index 264f7a9..51d69e7 100644 --- a/src/main/java/com/amazon/carbonado/cursor/FilteredCursor.java +++ b/src/main/java/com/amazon/carbonado/cursor/FilteredCursor.java @@ -104,7 +104,7 @@ public abstract class FilteredCursor<S> extends AbstractCursor<S> {      /**
       * @return false if object should not be in results
       */
 -    protected abstract boolean isAllowed(S storable);
 +    protected abstract boolean isAllowed(S storable) throws FetchException;
      public void close() throws FetchException {
          mCursor.close();
 diff --git a/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java b/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java index 4514865..9b2a945 100644 --- a/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java +++ b/src/main/java/com/amazon/carbonado/cursor/FilteredCursorGenerator.java @@ -38,6 +38,7 @@ import org.cojen.util.ClassInjector;  import org.cojen.util.WeakIdentityMap;
  import com.amazon.carbonado.Cursor;
 +import com.amazon.carbonado.Query;
  import com.amazon.carbonado.Storable;
  import com.amazon.carbonado.filter.AndFilter;
 @@ -154,7 +155,11 @@ class FilteredCursorGenerator {              isAllowedBuilder.storeLocal(storableVar);
          }
 -        filter.accept(new CodeGen<S>(cf, ctorBuilder, isAllowedBuilder, storableVar), null);
 +        CodeGen<S> cg = new CodeGen<S>(cf, ctorBuilder, isAllowedBuilder, storableVar);
 +        filter.accept(cg, null);
 +
 +        // Finish static initializer. (if any)
 +        cg.finish();
          // Finish constructor.
          ctorBuilder.returnVoid();
 @@ -223,6 +228,7 @@ class FilteredCursorGenerator {      private static class CodeGen<S extends Storable> extends Visitor<S, Object, Object> {
          private static String FIELD_PREFIX = "value$";
 +        private static String FILTER_FIELD_PREFIX = "filter$";
          private final ClassFile mClassFile;
          private final CodeBuilder mCtorBuilder;
 @@ -233,6 +239,8 @@ class FilteredCursorGenerator {          private int mPropertyOrdinal;
 +        private CodeBuilder mInitBuilder;
 +
          CodeGen(ClassFile cf,
                  CodeBuilder ctorBuilder,
                  CodeBuilder isAllowedBuilder, LocalVariable storableVar) {
 @@ -244,6 +252,12 @@ class FilteredCursorGenerator {              mScopeStack.push(new Scope(null, null));
          }
 +        public void finish() {
 +            if (mInitBuilder != null) {
 +                mInitBuilder.returnVoid();
 +            }
 +        }
 +
          public Object visit(OrFilter<S> filter, Object param) {
              Label failLocation = mIsAllowedBuilder.createLabel();
              // Inherit success location to short-circuit if 'or' test succeeds.
 @@ -289,25 +303,72 @@ class FilteredCursorGenerator {              b = mIsAllowedBuilder;
              b.loadLocal(mStorableVar);
              loadProperty(b, chained.getPrimeProperty());
 -            for (int i=0; i<chained.getChainCount(); i++) {
 -                // Check if last loaded property was null, and fail if so.
 -                b.dup();
 -                Label notNull = b.createLabel();
 -                b.ifNullBranch(notNull, false);
 -                b.pop();
 -                getScope().fail(b);
 -                notNull.setLocation();
 -                // Now load next property in chain.
 -                loadProperty(b, chained.getChainedProperty(i));
 -            }
 +            if (chained.getPrimeProperty().isQuery()) {
 +                Filter tail = Filter.getOpenFilter(chained.getPrimeProperty().getJoinedType())
 +                    .and(chained.tail().toString(), filter.getOperator());
 +
 +                String filterFieldName = addStaticFilterField(tail);
 +
 +                TypeDesc filterType = TypeDesc.forClass(Filter.class);
 +                TypeDesc queryType = TypeDesc.forClass(Query.class);
 +
 +                b.loadStaticField(filterFieldName, filterType);
 +                b.invokeInterface(queryType, "and", queryType, new TypeDesc[] {filterType});
 +                b.loadThis();
 +                b.loadField(fieldName, fieldType);
 +                TypeDesc withType = fieldType;
 +                if (!withType.isPrimitive()) {
 +                    withType = TypeDesc.OBJECT;
 +                }
 +                b.invokeInterface(queryType, "with", queryType, new TypeDesc[] {withType});
 +                b.invokeInterface(queryType, "exists", TypeDesc.BOOLEAN, null);
 +
 +                // Success if boolean value is true (non-zero).
 +                getScope().successIfZeroComparisonElseFail(b, RelOp.NE);
 +            } else {
 +                for (int i=0; i<chained.getChainCount(); i++) {
 +                    // Check if last loaded property was null, and fail if so.
 +                    b.dup();
 +                    Label notNull = b.createLabel();
 +                    b.ifNullBranch(notNull, false);
 +                    b.pop();
 +                    getScope().fail(b);
 +                    notNull.setLocation();
 +
 +                    // Now load next property in chain.
 +                    loadProperty(b, chained.getChainedProperty(i));
 +                }
 -            addPropertyFilter(b, type, filter.getOperator());
 +                addPropertyFilter(b, type, filter.getOperator());
 +            }
              mPropertyOrdinal++;
              return null;
          }
 +        private String addStaticFilterField(Filter filter) {
 +            String fieldName = FILTER_FIELD_PREFIX + mPropertyOrdinal;
 +
 +            TypeDesc filterType = TypeDesc.forClass(Filter.class);
 +            TypeDesc classType = TypeDesc.forClass(Class.class);
 +
 +            mClassFile.addField(Modifiers.PRIVATE.toStatic(true).toFinal(true),
 +                                fieldName, filterType);
 +
 +            if (mInitBuilder == null) {
 +                mInitBuilder = new CodeBuilder(mClassFile.addInitializer());
 +            }
 +
 +            mInitBuilder.loadConstant(TypeDesc.forClass(filter.getStorableType()));
 +            mInitBuilder.loadConstant(filter.toString());
 +            mInitBuilder.invokeStatic(Filter.class.getName(), "filterFor", filterType,
 +                                      new TypeDesc[] {classType, TypeDesc.STRING});
 +            mInitBuilder.storeStaticField(fieldName, filterType);
 +
 +            return fieldName;
 +        }
 +
          private Scope getScope() {
              return mScopeStack.peek();
          }
 diff --git a/src/main/java/com/amazon/carbonado/filter/FilterParser.java b/src/main/java/com/amazon/carbonado/filter/FilterParser.java index a0a178d..aa01ba9 100644 --- a/src/main/java/com/amazon/carbonado/filter/FilterParser.java +++ b/src/main/java/com/amazon/carbonado/filter/FilterParser.java @@ -220,7 +220,7 @@ class FilterParser<S extends Storable> {          }
          List<StorableProperty<?>> chain = new ArrayList<StorableProperty<?>>(4);
 -        Class<?> type = prime.getType();
 +        Class<?> type = prime.isJoin() ? prime.getJoinedType() : prime.getType();
          while (true) {
              ident = parseIdentifier();
 | 
