diff options
| author | Brian S. O'Neill <bronee@gmail.com> | 2006-09-03 21:48:14 +0000 | 
|---|---|---|
| committer | Brian S. O'Neill <bronee@gmail.com> | 2006-09-03 21:48:14 +0000 | 
| commit | 1e947afa4b660a23a2dcb57463dd810fb73e6030 (patch) | |
| tree | f8fdb7e2530bc07820f51d30c8e08cf737d6c5ac /src | |
| parent | 39fce59a840b723eb013bc79285687986592b2da (diff) | |
Manage ordering properties with lists.
Diffstat (limited to 'src')
14 files changed, 534 insertions, 107 deletions
| diff --git a/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java b/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java index eb2e977..74d6e8d 100644 --- a/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/AbstractQuery.java @@ -44,22 +44,6 @@ import com.amazon.carbonado.util.Appender;   * @author Brian S O'Neill
   */
  public abstract class AbstractQuery<S extends Storable> implements Query<S>, Appender {
 -    // FIXME: remove this
 -    protected static final String[] EMPTY_ORDERINGS = {};
 -
 -    // FIXME: remove this
 -    protected static String[] extractOrderingNames(OrderedProperty<?>[] orderings) {
 -        String[] orderingStrings;
 -        if (orderings == null || orderings.length == 0) {
 -            return EMPTY_ORDERINGS;
 -        }
 -        orderingStrings = new String[orderings.length];
 -        for (int i=0; i<orderingStrings.length; i++) {
 -            orderingStrings[i] = orderings[i].toString().intern();
 -        }
 -        return orderingStrings;
 -    }
 -
      protected AbstractQuery() {
      }
 diff --git a/src/main/java/com/amazon/carbonado/qe/CompositeScore.java b/src/main/java/com/amazon/carbonado/qe/CompositeScore.java index 7028c54..be4ceb7 100644 --- a/src/main/java/com/amazon/carbonado/qe/CompositeScore.java +++ b/src/main/java/com/amazon/carbonado/qe/CompositeScore.java @@ -19,6 +19,7 @@  package com.amazon.carbonado.qe;
  import java.util.Comparator;
 +import java.util.Collections;
  import java.util.List;
  import com.amazon.carbonado.Storable;
 @@ -44,12 +45,28 @@ public class CompositeScore<S extends Storable> {       *
       * @param index index to evaluate
       * @param filter optional filter which cannot contain any logical 'or' operations.
 +     * @throws IllegalArgumentException if index is null or filter is not supported
 +     */
 +    public static <S extends Storable> CompositeScore<S> evaluate
 +        (StorableIndex<S> index,
 +         Filter<S> filter)
 +    {
 +        return evaluate(index, filter, null);
 +    }
 +
 +    /**
 +     * Evaluates the given index for its filtering and ordering capabilities
 +     * against the given filter and order-by properties.
 +     *
 +     * @param index index to evaluate
 +     * @param filter optional filter which cannot contain any logical 'or' operations.
       * @param orderings properties which define desired ordering
       * @throws IllegalArgumentException if index is null or filter is not supported
       */
 -    public static <S extends Storable> CompositeScore<S> evaluate(StorableIndex<S> index,
 -                                                                  Filter<S> filter,
 -                                                                  OrderedProperty<S>... orderings)
 +    public static <S extends Storable> CompositeScore<S> evaluate
 +        (StorableIndex<S> index,
 +         Filter<S> filter,
 +         List<OrderedProperty<S>> orderings)
      {
          if (index == null) {
              throw new IllegalArgumentException("Index required");
 @@ -70,6 +87,25 @@ public class CompositeScore<S extends Storable> {       * @param unique true if index is unique
       * @param clustered true if index is clustered
       * @param filter optional filter which cannot contain any logical 'or' operations.
 +     * @throws IllegalArgumentException if index is null or filter is not supported
 +     */
 +    public static <S extends Storable> CompositeScore<S> evaluate
 +        (OrderedProperty<S>[] indexProperties,
 +         boolean unique,
 +         boolean clustered,
 +         Filter<S> filter)
 +    {
 +        return evaluate(indexProperties, unique, clustered, filter, null);
 +    }
 +
 +    /**
 +     * Evaluates the given index properties for its filtering and ordering
 +     * capabilities against the given filter and order-by properties.
 +     *
 +     * @param indexProperties index properties to evaluate
 +     * @param unique true if index is unique
 +     * @param clustered true if index is clustered
 +     * @param filter optional filter which cannot contain any logical 'or' operations.
       * @param orderings properties which define desired ordering
       * @throws IllegalArgumentException if index is null or filter is not supported
       */
 @@ -78,7 +114,7 @@ public class CompositeScore<S extends Storable> {           boolean unique,
           boolean clustered,
           Filter<S> filter,
 -         OrderedProperty<S>... orderings)
 +         List<OrderedProperty<S>> orderings)
      {
          FilteringScore<S> filteringScore = FilteringScore
              .evaluate(indexProperties, unique, clustered, filter);
 diff --git a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java index ba18531..4918f08 100644 --- a/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/EmptyQuery.java @@ -44,31 +44,29 @@ public final class EmptyQuery<S extends Storable> extends AbstractQuery<S> {      private final Storage<S> mStorage;
      // Properties that this query is ordered by.
 -    private final String[] mOrderings;
 +    private final OrderingList<S> mOrderings;
      /**
       * @param storage required storage object
       * @param orderings optional order-by properties
       */
 -    // FIXME: remove this
 -    public EmptyQuery(Storage<S> storage, OrderedProperty[] orderings) {
 +    public EmptyQuery(Storage<S> storage, OrderingList<S> orderings) {
          if (storage == null) {
              throw new IllegalArgumentException();
          }
          mStorage = storage;
 -        mOrderings = extractOrderingNames(orderings);
 +        if (orderings == null) {
 +            orderings = OrderingList.emptyList();
 +        }
 +        mOrderings = orderings;
      }
      /**
       * @param storage required storage object
       * @param orderings optional order-by properties
       */
 -    public EmptyQuery(Storage<S> storage, String[] orderings) {
 -        if (storage == null) {
 -            throw new IllegalArgumentException();
 -        }
 -        mStorage = storage;
 -        mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings;
 +    public EmptyQuery(Storage<S> storage, String... orderings) {
 +        this(storage, OrderingList.get(storage.getStorableType(), orderings));
      }
      public Class<S> getStorableType() {
 @@ -182,9 +180,8 @@ public final class EmptyQuery<S extends Storable> extends AbstractQuery<S> {      public Query<S> not() throws FetchException {
          Query<S> query = mStorage.query();
 -        String[] orderings = mOrderings;
 -        if (orderings.length > 0) {
 -            query = query.orderBy(orderings);
 +        if (mOrderings.size() > 0) {
 +            query = query.orderBy(mOrderings.asStringArray());
          }
          return query;
      }
 @@ -246,13 +243,13 @@ public final class EmptyQuery<S extends Storable> extends AbstractQuery<S> {          app.append(", filter=");
          getFilter().appendTo(app);
 -        if (mOrderings != null && mOrderings.length > 0) {
 +        if (mOrderings != null && mOrderings.size() > 0) {
              app.append(", orderBy=[");
 -            for (int i=0; i<mOrderings.length; i++) {
 +            for (int i=0; i<mOrderings.size(); i++) {
                  if (i > 0) {
                      app.append(", ");
                  }
 -                app.append(mOrderings[i]);
 +                app.append(mOrderings.get(i).toString());
              }
              app.append(']');
          }
 diff --git a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java index dfe13cc..92c21dd 100644 --- a/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java +++ b/src/main/java/com/amazon/carbonado/qe/IndexedQueryAnalyzer.java @@ -76,10 +76,19 @@ public class IndexedQueryAnalyzer<S extends Storable> {      /**
       * @param filter optional filter which which must be {@link Filter#isBound
       * bound} and cannot contain any logical 'or' operations.
 -     * @param orderings properties which define desired ordering
       * @throws IllegalArgumentException if filter is not supported
       */
 -    public Result analyze(Filter<S> filter, OrderedProperty<S>... orderings) {
 +    public Result analyze(Filter<S> filter) {
 +        return analyze(filter, null);
 +    }
 +
 +    /**
 +     * @param filter optional filter which which must be {@link Filter#isBound
 +     * bound} and cannot contain any logical 'or' operations.
 +     * @param orderings optional properties which define desired ordering
 +     * @throws IllegalArgumentException if filter is not supported
 +     */
 +    public Result analyze(Filter<S> filter, List<OrderedProperty<S>> orderings) {
          if (!filter.isBound()) {
              // Strictly speaking, this is not required, but it detects the
              // mistake of not properly calling initialFilterValues.
 diff --git a/src/main/java/com/amazon/carbonado/qe/OrderingList.java b/src/main/java/com/amazon/carbonado/qe/OrderingList.java new file mode 100644 index 0000000..907453d --- /dev/null +++ b/src/main/java/com/amazon/carbonado/qe/OrderingList.java @@ -0,0 +1,227 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +package com.amazon.carbonado.qe;
 +
 +import java.util.AbstractList;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
 +
 +import org.cojen.util.SoftValuedHashMap;
 +
 +import com.amazon.carbonado.Storable;
 +
 +import com.amazon.carbonado.info.OrderedProperty;
 +import com.amazon.carbonado.info.StorableIntrospector;
 +
 +/**
 + * Produces unmodifiable lists of {@link OrderedProperty orderings}. Instances
 + * are immutable, canonical and cached. Calls to "equals" and "hashCode" are
 + * fast.
 + *
 + * @author Brian S O'Neill
 + */
 +public class OrderingList<S extends Storable> extends AbstractList<OrderedProperty<S>> {
 +    private static final OrderingList EMPTY_LIST = new OrderingList();
 +
 +    private static final Map<Class, OrderingList> cCache;
 +
 +    static {
 +	cCache = new SoftValuedHashMap();
 +    }
 +
 +    /**
 +     * Returns a canonical empty instance.
 +     */
 +    public static <S extends Storable> OrderingList<S> emptyList() {
 +	return EMPTY_LIST;
 +    }
 +
 +    /**
 +     * Returns a canonical instance composed of the given ordering.
 +     *
 +     * @throws IllegalArgumentException if ordering property is not in S
 +     */
 +    public static <S extends Storable> OrderingList<S> get(Class<S> type, String property) {
 +	if (property == null) {
 +	    return EMPTY_LIST;
 +	}
 +	return getListNode(type).nextNode(type, property);
 +    }
 +
 +    /**
 +     * Returns a canonical instance composed of the given orderings.
 +     *
 +     * @throws IllegalArgumentException if any ordering property is not in S
 +     */
 +    public static <S extends Storable> OrderingList<S> get(Class<S> type, String... orderings) {
 +	if (orderings == null || orderings.length == 0) {
 +	    return EMPTY_LIST;
 +	}
 +
 +	OrderingList<S> node = getListNode(type);
 +	for (String property : orderings) {
 +	    node = node.nextNode(type, property);
 +	}
 +
 +        return node;
 +    }
 +
 +    /**
 +     * Returns a canonical instance composed of the given orderings.
 +     */
 +    public static <S extends Storable> OrderingList<S> get(OrderedProperty<S>... orderings) {
 +	if (orderings == null || orderings.length == 0) {
 +	    return EMPTY_LIST;
 +	}
 +
 +	Class<S> type = orderings[0].getChainedProperty().getPrimeProperty().getEnclosingType();
 +
 +	OrderingList<S> node = getListNode(type);
 +	for (OrderedProperty<S> property : orderings) {
 +	    node = node.nextNode(property);
 +	}
 +
 +        return node;
 +    }
 +
 +    private static <S extends Storable> OrderingList<S> getListNode(Class<S> type) {
 +	OrderingList<S> node;
 +	synchronized (cCache) {
 +	    node = (OrderingList<S>) cCache.get(type);
 +	    if (node == null) {
 +		node = new OrderingList<S>();
 +		cCache.put(type, node);
 +	    }
 +	}
 +	return node;
 +    }
 +
 +    private final OrderingList<S> mParent;
 +    private final OrderedProperty<S> mProperty;
 +    private final int mSize;
 +
 +    private Map<Object, OrderingList<S>> mNextNode;
 +
 +    private OrderedProperty<S>[] mOrderings;
 +    private String[] mOrderingStrings;
 +
 +    private OrderingList() {
 +	mParent = null;
 +	mProperty = null;
 +	mSize = 0;
 +    }
 +
 +    private OrderingList(OrderingList<S> parent, OrderedProperty<S> property) {
 +	if (property == null) {
 +	    throw new IllegalArgumentException("Ordering property is null");
 +	}
 +	mParent = parent;
 +	mProperty = property;
 +	mSize = parent.mSize + 1;
 +    }
 +
 +    public int size() {
 +	return mSize;
 +    }
 +
 +    public OrderedProperty<S> get(int index) {
 +	return asArray()[index];
 +    }
 +
 +    private OrderedProperty<S>[] asArray() {
 +	if (mOrderings == null) {
 +	    OrderedProperty<S>[] orderings = new OrderedProperty[mSize];
 +	    OrderingList<S> node = this;
 +	    for (int i=mSize; --i>=0; ) {
 +		orderings[i] = node.mProperty;
 +		node = node.mParent;
 +	    }
 +	    mOrderings = orderings;
 +	}
 +	return mOrderings;
 +    }
 +
 +    /**
 +     * Returns the orderings as qualified string property names. Each is
 +     * prefixed with a '+' or '-'.
 +     */
 +    String[] asStringArray() {
 +	if (mOrderingStrings == null) {
 +	    String[] orderings = new String[mSize];
 +	    OrderingList<S> node = this;
 +	    for (int i=mSize; --i>=0; ) {
 +		orderings[i] = node.mProperty.toString();
 +		node = node.mParent;
 +	    }
 +	    mOrderingStrings = orderings;
 +	}
 +	return mOrderingStrings;
 +    }
 +
 +    @Override
 +    public int hashCode() {
 +	return System.identityHashCode(this);
 +    }
 +
 +    @Override
 +    public boolean equals(Object other) {
 +	if (this == other) {
 +	    return true;
 +	}
 +	return super.equals(other);
 +    }
 +
 +    private synchronized OrderingList<S> nextNode(Class<S> type, String property) {
 +	OrderingList<S> node;
 +	if (mNextNode == null) {
 +	    mNextNode = new HashMap<Object, OrderingList<S>>();
 +	    node = null;
 +	} else {
 +	    node = mNextNode.get(property);
 +	}
 +
 +	if (node == null) {
 +	    OrderedProperty<S> op = OrderedProperty
 +		.parse(StorableIntrospector.examine(type), property);
 +
 +	    node = nextNode(op);
 +	    mNextNode.put(property, node);
 +	}
 +
 +	return node;
 +    }
 +
 +    private synchronized OrderingList<S> nextNode(OrderedProperty<S> property) {
 +	OrderingList<S> node;
 +	if (mNextNode == null) {
 +	    mNextNode = new HashMap<Object, OrderingList<S>>();
 +	    node = null;
 +	} else {
 +	    node = mNextNode.get(property);
 +	}
 +
 +	if (node == null) {
 +	    node = new OrderingList<S>(this, property);
 +	    mNextNode.put(property, node);
 +	}
 +
 +	return node;
 +    }
 +}
 diff --git a/src/main/java/com/amazon/carbonado/qe/OrderingScore.java b/src/main/java/com/amazon/carbonado/qe/OrderingScore.java index 76b273f..67edcdd 100644 --- a/src/main/java/com/amazon/carbonado/qe/OrderingScore.java +++ b/src/main/java/com/amazon/carbonado/qe/OrderingScore.java @@ -68,12 +68,28 @@ public class OrderingScore<S extends Storable> {       *
       * @param index index to evaluate
       * @param filter optional filter which cannot contain any logical 'or' operations.
 +     * @throws IllegalArgumentException if index is null or filter is not supported
 +     */
 +    public static <S extends Storable> OrderingScore<S> evaluate
 +        (StorableIndex<S> index,
 +         Filter<S> filter)
 +    {
 +        return evaluate(index, filter, null);
 +    }
 +
 +    /**
 +     * Evaluates the given index for its ordering capabilities against the
 +     * given filter and order-by properties.
 +     *
 +     * @param index index to evaluate
 +     * @param filter optional filter which cannot contain any logical 'or' operations.
       * @param orderings properties which define desired ordering
       * @throws IllegalArgumentException if index is null or filter is not supported
       */
 -    public static <S extends Storable> OrderingScore<S> evaluate(StorableIndex<S> index,
 -                                                                 Filter<S> filter,
 -                                                                 OrderedProperty<S>... orderings)
 +    public static <S extends Storable> OrderingScore<S> evaluate
 +        (StorableIndex<S> index,
 +         Filter<S> filter,
 +         List<OrderedProperty<S>> orderings)
      {
          if (index == null) {
              throw new IllegalArgumentException("Index required");
 @@ -94,6 +110,25 @@ public class OrderingScore<S extends Storable> {       * @param unique true if index is unique
       * @param clustered true if index is clustered
       * @param filter optional filter which cannot contain any logical 'or' operations.
 +     * @throws IllegalArgumentException if index is null or filter is not supported
 +     */
 +    public static <S extends Storable> OrderingScore<S> evaluate
 +        (OrderedProperty<S>[] indexProperties,
 +         boolean unique,
 +         boolean clustered,
 +         Filter<S> filter)
 +    {
 +        return evaluate(indexProperties, unique, clustered, filter, null);
 +    }
 +
 +    /**
 +     * Evaluates the given index properties for its ordering capabilities
 +     * against the given filter and order-by properties.
 +     *
 +     * @param indexProperties index properties to evaluate
 +     * @param unique true if index is unique
 +     * @param clustered true if index is clustered
 +     * @param filter optional filter which cannot contain any logical 'or' operations.
       * @param orderings properties which define desired ordering
       * @throws IllegalArgumentException if index is null or filter is not supported
       */
 @@ -102,7 +137,7 @@ public class OrderingScore<S extends Storable> {           boolean unique,
           boolean clustered,
           Filter<S> filter,
 -         OrderedProperty<S>... orderings)
 +         List<OrderedProperty<S>> orderings)
      {
          if (indexProperties == null) {
              throw new IllegalArgumentException("Index properties required");
 @@ -111,7 +146,7 @@ public class OrderingScore<S extends Storable> {          // Get filter list early to detect errors.
          List<PropertyFilter<S>> filterList = PropertyFilterList.get(filter);
 -        if (orderings == null || orderings.length == 0) {
 +        if (orderings == null || orderings.size() == 0) {
              return new OrderingScore<S>(clustered, indexProperties.length, null, null, false);
          }
 @@ -154,8 +189,8 @@ public class OrderingScore<S extends Storable> {          int indexPos = 0;
          calcScore:
 -        for (int i=0; i<orderings.length; i++) {
 -            OrderedProperty<S> property = orderings[i];
 +        for (int i=0; i<orderings.size(); i++) {
 +            OrderedProperty<S> property = orderings.get(i);
              ChainedProperty<S> chained = property.getChainedProperty();
              if (seen.contains(chained)) {
 diff --git a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java index 822cde8..48defa7 100644 --- a/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/SortedQueryExecutor.java @@ -54,6 +54,8 @@ public abstract class SortedQueryExecutor<S extends Storable> extends AbstractQu      /**
       * @param executor executor to wrap
 +     * @param handledOrderings optional handled orderings
 +     * @param remainderOrderings required remainder orderings
       * @throws IllegalArgumentException if executor is null or if remainder
       * orderings is empty
       */
 diff --git a/src/main/java/com/amazon/carbonado/qe/StandardQuery.java b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java index 7152e55..8c423e6 100644 --- a/src/main/java/com/amazon/carbonado/qe/StandardQuery.java +++ b/src/main/java/com/amazon/carbonado/qe/StandardQuery.java @@ -38,6 +38,7 @@ import com.amazon.carbonado.filter.FilterValues;  import com.amazon.carbonado.filter.OpenFilter;
  import com.amazon.carbonado.filter.RelOp;
 +import com.amazon.carbonado.info.Direction;
  import com.amazon.carbonado.info.OrderedProperty;
  import com.amazon.carbonado.util.Appender;
 @@ -53,27 +54,27 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>      // Values for this query, which may be null.
      private final FilterValues<S> mValues;
      // Properties that this query is ordered by.
 -    private final String[] mOrderings;
 +    private final OrderingList<S> mOrderings;
      private volatile QueryExecutor<S> mExecutor;
      /**
       * @param values optional values object, defaults to open filter if null
 -     * @param orderings optional order-by properties
       */
 -    // FIXME: remove this
 -    protected StandardQuery(FilterValues<S> values, OrderedProperty<S>... orderings) {
 -        this(values, extractOrderingNames(orderings));
 +    protected StandardQuery(FilterValues<S> values) {
 +        this(values, null);
      }
      /**
       * @param values optional values object, defaults to open filter if null
 -     * @param orderings optional order-by properties, not cloned, which may be
 -     * prefixed with '+' or '-'
 +     * @param orderings optional order-by properties
       */
 -    protected StandardQuery(FilterValues<S> values, String... orderings) {
 +    protected StandardQuery(FilterValues<S> values, OrderingList<S> orderings) {
          mValues = values;
 -        mOrderings = orderings == null ? EMPTY_ORDERINGS : orderings;
 +        if (orderings == null) {
 +            orderings = OrderingList.emptyList();
 +        }
 +        mOrderings = orderings;
      }
      public Class<S> getStorableType() {
 @@ -148,7 +149,7 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>              newQuery = getStorage().query(values.getFilter().and(filter));
              newQuery = newQuery.withValues(values.getValues());
          }
 -        return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings);
 +        return mOrderings.size() == 0 ? newQuery : newQuery.orderBy(mOrderings.asStringArray());
      }
      public Query<S> or(Filter<S> filter) throws FetchException {
 @@ -158,7 +159,7 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>          }
          Query<S> newQuery = getStorage().query(values.getFilter().or(filter));
          newQuery = newQuery.withValues(values.getValues());
 -        return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings);
 +        return mOrderings.size() == 0 ? newQuery : newQuery.orderBy(mOrderings.asStringArray());
      }
      public Query<S> not() throws FetchException {
 @@ -168,21 +169,15 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>          }
          Query<S> newQuery = getStorage().query(values.getFilter().not());
          newQuery = newQuery.withValues(values.getSuppliedValues());
 -        return mOrderings.length == 0 ? newQuery : newQuery.orderBy(mOrderings);
 +        return mOrderings.size() == 0 ? newQuery : newQuery.orderBy(mOrderings.asStringArray());
      }
      public Query<S> orderBy(String property) throws FetchException {
 -        StandardQuery<S> query = newInstance(mValues, property);
 -        // Get executor to ensure order property is correct.
 -        query.executor();
 -        return query;
 +        return newInstance(mValues, OrderingList.get(getStorableType(), property));
      }
      public Query<S> orderBy(String... properties) throws FetchException {
 -        StandardQuery<S> query = newInstance(mValues, properties);
 -        // Get executor to ensure order properties are correct.
 -        query.executor();
 -        return query;
 +        return newInstance(mValues, OrderingList.get(getStorableType(), properties));
      }
      public Cursor<S> fetch() throws FetchException {
 @@ -190,8 +185,8 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>      }
      public Cursor<S> fetchAfter(S start) throws FetchException {
 -        String[] orderings;
 -        if (start == null || (orderings = mOrderings).length == 0) {
 +        OrderingList<S> orderings;
 +        if (start == null || (orderings = mOrderings).size() == 0) {
              return fetch();
          }
 @@ -200,24 +195,21 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>          Filter<S> lastSubFilter = Filter.getOpenFilter(storableType);
          BeanPropertyAccessor accessor = BeanPropertyAccessor.forClass(storableType);
 -        Object[] values = new Object[orderings.length];
 +        Object[] values = new Object[orderings.size()];
          for (int i=0;;) {
 -            String propertyName = orderings[i];
 +            OrderedProperty<S> property = orderings.get(i);
              RelOp operator = RelOp.GT;
 -            char c = propertyName.charAt(0);
 -            if (c == '-') {
 -                propertyName = propertyName.substring(1);
 +            if (property.getDirection() == Direction.DESCENDING) {
                  operator = RelOp.LT;
 -            } else if (c == '+') {
 -                propertyName = propertyName.substring(1);
              }
 +            String propertyName = property.getChainedProperty().toString();
              values[i] = accessor.getPropertyValue(start, propertyName);
              orderFilter = orderFilter.or(lastSubFilter.and(propertyName, operator));
 -            if (++i >= orderings.length) {
 +            if (++i >= orderings.size()) {
                  break;
              }
 @@ -336,13 +328,13 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>              app.append('"');
          }
 -        if (mOrderings != null && mOrderings.length > 0) {
 +        if (mOrderings != null && mOrderings.size() > 0) {
              app.append(", orderBy=[");
 -            for (int i=0; i<mOrderings.length; i++) {
 +            for (int i=0; i<mOrderings.size(); i++) {
                  if (i > 0) {
                      app.append(", ");
                  }
 -                app.append(mOrderings[i]);
 +                app.append(mOrderings.get(i).toString());
              }
              app.append(']');
          }
 @@ -375,10 +367,10 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>       * Return a new or cached executor.
       *
       * @param values optional values object, defaults to open filter if null
 -     * @param orderings optional order-by properties, which may be prefixed
 -     * with '+' or '-'
 +     * @param orderings order-by properties, never null
       */
 -    protected abstract QueryExecutor<S> getExecutor(FilterValues<S> values, String... orderings);
 +    protected abstract QueryExecutor<S> getExecutor(FilterValues<S> values,
 +                                                    OrderingList<S> orderings);
      /**
       * Return a new or cached instance of StandardQuery implementation, using
 @@ -388,7 +380,8 @@ public abstract class StandardQuery<S extends Storable> extends AbstractQuery<S>       * @param values optional values object, defaults to open filter if null
       * @param orderings order-by properties, never null
       */
 -    protected abstract StandardQuery<S> newInstance(FilterValues<S> values, String... orderings);
 +    protected abstract StandardQuery<S> newInstance(FilterValues<S> values,
 +                                                    OrderingList<S> orderings);
      private StandardQuery<S> newInstance(FilterValues<S> values) {
          return newInstance(values, mOrderings);
 diff --git a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java index 9563097..b4e45fa 100644 --- a/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java +++ b/src/main/java/com/amazon/carbonado/qe/UnionQueryAnalyzer.java @@ -19,6 +19,7 @@  package com.amazon.carbonado.qe;
  import java.util.ArrayList;
 +import java.util.Collections;
  import java.util.List;
  import com.amazon.carbonado.Storable;
 @@ -58,9 +59,16 @@ public class UnionQueryAnalyzer<S extends Storable> {      /**
       * @param filter optional filter which must be {@link Filter#isBound bound}
 -     * @param orderings properties which define desired ordering
       */
 -    public Result analyze(Filter<S> filter, OrderedProperty<S>... orderings) {
 +    public Result analyze(Filter<S> filter) {
 +        return analyze(filter, null);
 +    }
 +
 +    /**
 +     * @param filter optional filter which must be {@link Filter#isBound bound}
 +     * @param orderings optional properties which define desired ordering
 +     */
 +    public Result analyze(Filter<S> filter, List<OrderedProperty<S>> orderings) {
          if (!filter.isBound()) {
              // Strictly speaking, this is not required, but it detects the
              // mistake of not properly calling initialFilterValues.
 @@ -87,7 +95,7 @@ public class UnionQueryAnalyzer<S extends Storable> {      }
      private List<IndexedQueryAnalyzer<S>.Result>
 -        splitIntoSubResults(Filter<S> filter, OrderedProperty<S>... orderings)
 +        splitIntoSubResults(Filter<S> filter, List<OrderedProperty<S>> orderings)
      {
          Splitter splitter = new Splitter(orderings);
          filter.accept(splitter, null);
 @@ -174,11 +182,11 @@ public class UnionQueryAnalyzer<S extends Storable> {       * only contain 'and' operations.
       */
      private class Splitter extends Visitor<S, Object, Object> {
 -        private final OrderedProperty<S>[] mOrderings;
 +        private final List<OrderedProperty<S>> mOrderings;
          final List<IndexedQueryAnalyzer<S>.Result> mSubResults;
 -        Splitter(OrderedProperty<S>... orderings) {
 +        Splitter(List<OrderedProperty<S>> orderings) {
              mOrderings = orderings;
              mSubResults = new ArrayList<IndexedQueryAnalyzer<S>.Result>();
          }
 diff --git a/src/main/java/com/amazon/carbonado/spi/BaseQuery.java b/src/main/java/com/amazon/carbonado/spi/BaseQuery.java index e1117cd..b1a1d45 100644 --- a/src/main/java/com/amazon/carbonado/spi/BaseQuery.java +++ b/src/main/java/com/amazon/carbonado/spi/BaseQuery.java @@ -49,6 +49,7 @@ import com.amazon.carbonado.qe.EmptyQuery;   * BaseQuery supports binding filters to values.
   *
   * @author Brian S O'Neill
 + * @deprecated Use {@link com.amazon.carbonado.qe.StandardQuery}
   */
  public abstract class BaseQuery<S extends Storable> extends AbstractQuery<S> implements Appender {
      /**
 @@ -61,6 +62,20 @@ public abstract class BaseQuery<S extends Storable> extends AbstractQuery<S> imp          }
      }
 +    protected static final String[] EMPTY_ORDERINGS = {};
 +
 +    protected static String[] extractOrderingNames(OrderedProperty<?>[] orderings) {
 +        String[] orderingStrings;
 +        if (orderings == null || orderings.length == 0) {
 +            return EMPTY_ORDERINGS;
 +        }
 +        orderingStrings = new String[orderings.length];
 +        for (int i=0; i<orderingStrings.length; i++) {
 +            orderingStrings[i] = orderings[i].toString().intern();
 +        }
 +        return orderingStrings;
 +    }
 +
      private final Repository mRepository;
      private final Storage<S> mStorage;
      // Values for this query.
 diff --git a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java index 3f865e6..b3eef91 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java +++ b/src/test/java/com/amazon/carbonado/qe/TestIndexedQueryExecutor.java @@ -21,6 +21,7 @@ package com.amazon.carbonado.qe;  import java.util.Arrays;
  import java.util.Collection;
  import java.util.Collections;
 +import java.util.List;
  import junit.framework.TestCase;
  import junit.framework.TestSuite;
 @@ -56,7 +57,8 @@ public class TestIndexedQueryExecutor extends TestCase {          return TestOrderingScore.makeIndex(type, props);
      }
 -    static <S extends Storable> OrderedProperty<S>[] makeOrderings(Class<S> type, String... props)
 +    static <S extends Storable> List<OrderedProperty<S>> makeOrderings(Class<S> type,
 +                                                                       String... props)
      {
          return TestOrderingScore.makeOrderings(type, props);
      }
 @@ -679,9 +681,9 @@ public class TestIndexedQueryExecutor extends TestCase {          assertTrue(executor.mReverseOrder);
          assertEquals(values.getFilter(), executor.getFilter());
 -        OrderedProperty<StorableTestBasic>[] expectedOrdering =
 +        List<OrderedProperty<StorableTestBasic>> expectedOrdering =
              makeOrderings(StorableTestBasic.class, "intProp", "-doubleProp", "stringProp");
 -        assertEquals(Arrays.asList(expectedOrdering), executor.getOrdering());
 +        assertEquals(expectedOrdering, executor.getOrdering());
      }
      /**
 diff --git a/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java new file mode 100644 index 0000000..b696984 --- /dev/null +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingList.java @@ -0,0 +1,110 @@ +/*
 + * Copyright 2006 Amazon Technologies, Inc. or its affiliates.
 + * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
 + * of Amazon Technologies, Inc. or its affiliates.  All rights reserved.
 + *
 + * Licensed under the Apache License, Version 2.0 (the "License");
 + * you may not use this file except in compliance with the License.
 + * You may obtain a copy of the License at
 + *
 + *     http://www.apache.org/licenses/LICENSE-2.0
 + *
 + * Unless required by applicable law or agreed to in writing, software
 + * distributed under the License is distributed on an "AS IS" BASIS,
 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 + * See the License for the specific language governing permissions and
 + * limitations under the License.
 + */
 +
 +package com.amazon.carbonado.qe;
 +
 +import java.util.List;
 +
 +import junit.framework.TestCase;
 +import junit.framework.TestSuite;
 +
 +import com.amazon.carbonado.info.OrderedProperty;
 +
 +import com.amazon.carbonado.stored.StorableTestBasic;
 +
 +/**
 + * 
 + *
 + * @author Brian S O'Neill
 + */
 +public class TestOrderingList extends TestCase {
 +    public static void main(String[] args) {
 +        junit.textui.TestRunner.run(suite());
 +    }
 +
 +    public static TestSuite suite() {
 +        return new TestSuite(TestOrderingList.class);
 +    }
 +
 +    public TestOrderingList(String name) {
 +        super(name);
 +    }
 +
 +    public void testEmpty() throws Exception {
 +        assertEquals(0, OrderingList.get(StorableTestBasic.class).size());
 +    }
 +
 +    public void testSingle() throws Exception {
 +        List<OrderedProperty<StorableTestBasic>> list_1 =
 +            OrderingList.get(StorableTestBasic.class, "date");
 +
 +        assertEquals(1, list_1.size());
 +        assertEquals("+date", list_1.get(0).toString());
 +
 +        List<OrderedProperty<StorableTestBasic>> list_2 =
 +            OrderingList.get(StorableTestBasic.class, "+date");
 +
 +        assertEquals(1, list_2.size());
 +        assertEquals("+date", list_2.get(0).toString());
 +        assertEquals(list_1, list_2);
 +        assertTrue(list_1 == list_2);
 +
 +        List<OrderedProperty<StorableTestBasic>> list_3 =
 +            OrderingList.get(StorableTestBasic.class, "-date");
 +
 +        assertEquals(1, list_3.size());
 +        assertEquals("-date", list_3.get(0).toString());
 +        assertFalse(list_2.equals(list_3));
 +        assertFalse(list_2 == list_3);
 +    }
 +
 +    public void testDouble() throws Exception {
 +        List<OrderedProperty<StorableTestBasic>> list_1 =
 +            OrderingList.get(StorableTestBasic.class, "date", "intProp");
 +
 +        assertEquals(2, list_1.size());
 +        assertEquals("+date", list_1.get(0).toString());
 +        assertEquals("+intProp", list_1.get(1).toString());
 +
 +        List<OrderedProperty<StorableTestBasic>> list_2 =
 +            OrderingList.get(StorableTestBasic.class, "+date", "+intProp");
 +
 +        assertEquals(2, list_2.size());
 +        assertEquals("+date", list_2.get(0).toString());
 +        assertEquals("+intProp", list_2.get(1).toString());
 +        assertEquals(list_1, list_2);
 +        assertTrue(list_1 == list_2);
 +
 +        List<OrderedProperty<StorableTestBasic>> list_3 =
 +            OrderingList.get(StorableTestBasic.class, "-date", "-intProp");
 +
 +        assertEquals(2, list_3.size());
 +        assertEquals("-date", list_3.get(0).toString());
 +        assertEquals("-intProp", list_3.get(1).toString());
 +        assertFalse(list_2.equals(list_3));
 +        assertFalse(list_2 == list_3);
 +    }
 +
 +    public void testIllegal() throws Exception {
 +        try {
 +            OrderingList.get(StorableTestBasic.class, "foo");
 +            fail();
 +        } catch (IllegalArgumentException e) {
 +        }
 +    }
 +}
 diff --git a/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java index 3929f59..1545b70 100644 --- a/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java +++ b/src/test/java/com/amazon/carbonado/qe/TestOrderingScore.java @@ -18,6 +18,8 @@  package com.amazon.carbonado.qe;
 +import java.util.ArrayList;
 +import java.util.Collections;
  import java.util.Comparator;
  import java.util.List;
 @@ -53,15 +55,19 @@ public class TestOrderingScore extends TestCase {      }
      static <S extends Storable> StorableIndex<S> makeIndex(Class<S> type, String... props) {
 -        return new StorableIndex<S>(makeOrderings(type, props), UNSPECIFIED);
 +        List<OrderedProperty<S>> list = makeOrderings(type, props);
 +        OrderedProperty<S>[] array = list.toArray(new OrderedProperty[0]);
 +
 +        return new StorableIndex<S>(array, UNSPECIFIED);
      }
 -    static <S extends Storable> OrderedProperty<S>[] makeOrderings(Class<S> type, String... props)
 +    static <S extends Storable> List<OrderedProperty<S>> makeOrderings(Class<S> type,
 +                                                                       String... props)
      {
          StorableInfo<S> info = StorableIntrospector.examine(type);
 -        OrderedProperty<S>[] ops = new OrderedProperty[props.length];
 +        List<OrderedProperty<S>> ops = new ArrayList<OrderedProperty<S>>(props.length);
          for (int i=0; i<props.length; i++) {
 -            ops[i] = OrderedProperty.parse(info, props[i]);
 +            ops.add(OrderedProperty.parse(info, props[i]));
          }
          return ops;
      }
 @@ -82,7 +88,7 @@ public class TestOrderingScore extends TestCase {      public void testOneProp() throws Exception {
          StorableIndex<StorableTestBasic> ix;
 -        OrderedProperty<StorableTestBasic>[] ops;
 +        List<OrderedProperty<StorableTestBasic>> ops;
          OrderingScore<StorableTestBasic> score;
          /////////////
 @@ -180,7 +186,7 @@ public class TestOrderingScore extends TestCase {      public void testMultipleProps() throws Exception {
          final StorableIndex<StorableTestBasic> ix;
 -        OrderedProperty<StorableTestBasic>[] ops;
 +        List<OrderedProperty<StorableTestBasic>> ops;
          OrderingScore<StorableTestBasic> score;
          ix = makeIndex(StorableTestBasic.class, "id", "intProp");
 @@ -317,7 +323,7 @@ public class TestOrderingScore extends TestCase {      public void testMidGap() throws Exception {
          final StorableIndex<StorableTestBasic> ix;
 -        OrderedProperty<StorableTestBasic>[] ops;
 +        List<OrderedProperty<StorableTestBasic>> ops;
          OrderingScore score;
          Filter<StorableTestBasic> filter;
 @@ -401,7 +407,7 @@ public class TestOrderingScore extends TestCase {      public void testComparator() throws Exception {
          StorableIndex<StorableTestBasic> ix_1, ix_2;
 -        OrderedProperty<StorableTestBasic>[] ops;
 +        List<OrderedProperty<StorableTestBasic>> ops;
          OrderingScore score_1, score_2;
          Filter<StorableTestBasic> filter;
          Comparator<OrderingScore<?>> comp = OrderingScore.fullComparator();
 @@ -466,7 +472,7 @@ public class TestOrderingScore extends TestCase {          // properties are filtered out. Thus the index is not needed.
          final StorableIndex<StorableTestBasic> ix;
 -        OrderedProperty<StorableTestBasic>[] ops;
 +        List<OrderedProperty<StorableTestBasic>> ops;
          OrderingScore score;
          Filter<StorableTestBasic> filter;
 @@ -501,7 +507,7 @@ public class TestOrderingScore extends TestCase {          // Test a unique index which has been fully specified. Ordering is not
          // needed at all.
          final StorableIndex<StorableTestBasic> ix;
 -        OrderedProperty<StorableTestBasic>[] ops;
 +        List<OrderedProperty<StorableTestBasic>> ops;
          OrderingScore score;
          Filter<StorableTestBasic> filter;
 diff --git a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java index dd29e4b..01f9fbf 100644 --- a/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java +++ b/src/test/java/com/amazon/carbonado/repo/toy/ToyStorage.java @@ -48,10 +48,11 @@ import com.amazon.carbonado.filter.FilterValues;  import com.amazon.carbonado.info.OrderedProperty;
  import com.amazon.carbonado.info.StorableIntrospector;
 +import com.amazon.carbonado.qe.ArraySortedQueryExecutor;
  import com.amazon.carbonado.qe.FilteredQueryExecutor;
  import com.amazon.carbonado.qe.IterableQueryExecutor;
 +import com.amazon.carbonado.qe.OrderingList;
  import com.amazon.carbonado.qe.QueryExecutor;
 -import com.amazon.carbonado.qe.SortedQueryExecutor;
  import com.amazon.carbonado.qe.StandardQuery;
  /**
 @@ -89,7 +90,7 @@ public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport      }
      public Query<S> query() throws FetchException {
 -        return new ToyQuery(null);
 +        return new ToyQuery(null, null);
      }
      public Query<S> query(String filter) throws FetchException {
 @@ -97,7 +98,7 @@ public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport      }
      public Query<S> query(Filter<S> filter) throws FetchException {
 -        return new ToyQuery(filter.initialFilterValues());
 +        return new ToyQuery(filter.initialFilterValues(), null);
      }
      public boolean addTrigger(Trigger<? super S> trigger) {
 @@ -211,7 +212,7 @@ public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport      }
      private class ToyQuery extends StandardQuery<S> {
 -        ToyQuery(FilterValues<S> values, String... orderings) {
 +        ToyQuery(FilterValues<S> values, OrderingList<S> orderings) {
              super(values, orderings);
          }
 @@ -223,19 +224,21 @@ public class ToyStorage<S extends Storable> implements Storage<S>, MasterSupport              return mRepo.enterTransaction(level);
          }
 -        protected QueryExecutor<S> getExecutor(FilterValues<S> values, String... orderings) {
 +        protected QueryExecutor<S> getExecutor(FilterValues<S> values, OrderingList<S> orderings) {
              QueryExecutor<S> executor = new IterableQueryExecutor<S>(mType, mData, mDataLock);
              if (values != null) {
                  executor = new FilteredQueryExecutor<S>(executor, values.getFilter());
              }
 -            // FIXME: sorting
 +            if (orderings.size() > 0) {
 +                executor = new ArraySortedQueryExecutor<S>(executor, null, orderings);
 +            }
              return executor;
          }
 -        protected StandardQuery<S> newInstance(FilterValues<S> values, String... orderings) {
 +        protected StandardQuery<S> newInstance(FilterValues<S> values, OrderingList<S> orderings) {
              return new ToyQuery(values, orderings);
          }
      }
 | 
