Package com.google.appengine.api.datastore

Source Code of com.google.appengine.api.datastore.BaseEntityComparator

// Copyright 2009 Google Inc. All Rights Reserved.
package com.google.appengine.api.datastore;

import com.google.apphosting.datastore.DatastoreV3Pb;
import com.google.apphosting.datastore.DatastoreV3Pb.Query.Filter;
import com.google.apphosting.datastore.DatastoreV3Pb.Query.Order;
import com.google.apphosting.datastore.DatastoreV3Pb.Query.Order.Direction;
import com.google.common.collect.Lists;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
* Base class for Entity comparators.
*/
abstract class BaseEntityComparator<EntityT> implements Comparator<EntityT> {

  static final Comparator<Comparable<Object>> MULTI_TYPE_COMPARATOR = new MultiTypeComparator();

  static final Comparator<Order> ORDER_PROPERTY_COMPARATOR = new Comparator<Order>() {
    @Override
    public int compare(Order o1, Order o2) {
      return o1.getProperty().compareTo(o2.getProperty());
    }
  };

  static final Order KEY_ASC_ORDER = new Order().setProperty(Entity.KEY_RESERVED_PROPERTY)
      .setDirection(Order.Direction.ASCENDING);

  final List<Order> orders;
  final Map<String, FilterMatcher> filters;

  BaseEntityComparator(List<Order> orders, List<Filter> filters) {
    this.orders = makeAdjustedOrders(orders, filters);
    this.filters = makeFilterMatchers(orders, filters);
  }

  /**
   * Get a {@link List} with comparable representations of the Entity's
   * values with the given property name.
   *
   * @return A {@link List} of comparable property values, or {@code null} if
   * the entity has no property with the given name.
   */
  abstract List<Comparable<Object>> getComparablePropertyValues(EntityT entity, String property);

  private static List<Order> makeAdjustedOrders(List<Order> orders, List<Filter> filters) {
    List<Order> existsOrders = Lists.newArrayList();
    for (Filter filter : filters) {
      if (filter.getOpEnum() == Filter.Operator.EXISTS) {
        existsOrders.add(new Order()
            .setProperty(filter.getProperty(0).getName())
            .setDirection(Direction.ASCENDING));
      }
    }
    Collections.sort(existsOrders, ORDER_PROPERTY_COMPARATOR);

    List<Order> adjusted = new ArrayList<Order>(orders.size() + existsOrders.size() + 1);
    adjusted.addAll(orders);

    if (adjusted.isEmpty()) {
      for (Filter filter : filters) {
        if (ValidatedQuery.INEQUALITY_OPERATORS.contains(filter.getOpEnum())) {
          adjusted.add(new Order()
            .setProperty(filter.getProperty(0).getName())
            .setDirection(Direction.ASCENDING));
          break;
        }
      }
    }

    adjusted.addAll(existsOrders);

    if (adjusted.isEmpty() || !adjusted.get(adjusted.size() - 1).equals(KEY_ASC_ORDER)) {
      adjusted.add(KEY_ASC_ORDER);
    }
    return adjusted;
  }

  private static Map<String, FilterMatcher> makeFilterMatchers(List<Order> orders,
                                                               List<Filter> filters) {
    Map<String, FilterMatcher> filterMatchers = new HashMap<String, FilterMatcher>();
    for (Filter filter : filters) {
      String name = filter.getProperty(0).getName();
      FilterMatcher filterMatcher = filterMatchers.get(name);
      if (filterMatcher == null) {
        filterMatcher = new FilterMatcher();
        filterMatchers.put(name, filterMatcher);
      }
      filterMatcher.addFilter(filter);
    }

    for (Order order : orders) {
      if (!filterMatchers.containsKey(order.getProperty())) {
        filterMatchers.put(order.getProperty(), FilterMatcher.MATCH_ALL);
      }
      if (order.getProperty().equals(KEY_ASC_ORDER.getProperty())) {
        break;
      }
    }

    return filterMatchers;
  }

  @Override
  public int compare(EntityT entityA, EntityT entityB) {
    int result;

    for (Order order : orders) {
      String property = order.getProperty();

      Collection<Comparable<Object>> aValues = getComparablePropertyValues(entityA, property);
      Collection<Comparable<Object>> bValues = getComparablePropertyValues(entityB, property);

      if (aValues == null || bValues == null) {
        return 0;
      }
      boolean findMin = order.getDirectionEnum() == DatastoreV3Pb.Query.Order.Direction.ASCENDING;
      FilterMatcher matcher = filters.get(property);
      if (matcher == null) {
        matcher = FilterMatcher.MATCH_ALL;
      }
      Comparable<Object> extremeA = multiTypeExtreme(aValues, findMin, matcher);
      Comparable<Object> extremeB = multiTypeExtreme(bValues, findMin, matcher);

      result = MULTI_TYPE_COMPARATOR.compare(extremeA, extremeB);

      if (result != 0) {
        if (order.getDirectionEnum() == DatastoreV3Pb.Query.Order.Direction.DESCENDING) {
          result = -result;
        }
        return result;
      }
    }

    return 0;
  }

  /**
   * Find either the smallest or largest element in a potentially
   * heterogenous collection, depending on the value of {@code findMin}.
   */
  static Comparable<Object> multiTypeExtreme(
      Collection<Comparable<Object>> comparables, boolean findMin, FilterMatcher matcher) {
    boolean findMax = !findMin;
    Comparable<Object> extreme = FilterMatcher.NoValue.INSTANCE;
    for (Comparable<Object> value : comparables) {
      if (!matcher.considerValueForOrder(value)) {
        continue;
      }

      if (extreme == FilterMatcher.NoValue.INSTANCE) {
        extreme = value;
      } else if (findMin && MULTI_TYPE_COMPARATOR.compare(value, extreme) < 0) {
        extreme = value;
      } else if (findMax && MULTI_TYPE_COMPARATOR.compare(value, extreme) > 0) {
        extreme = value;
      }
    }
    if (extreme == FilterMatcher.NoValue.INSTANCE) {
      throw new IllegalArgumentException("Entity contains no relevant values.");
    }
    return extreme;
  }

  private static final class MultiTypeComparator implements Comparator<Comparable<Object>> {
    @Override
    public int compare(Comparable<Object> o1, Comparable<Object> o2) {
      if (o1 == null) {
        if (o2 == null) {
          return 0;
        }
        return -1;
      } else if (o2 == null) {
        return 1;
      }
      int comp1TypeRank = DataTypeTranslator.getTypeRank(o1.getClass());
      int comp2TypeRank = DataTypeTranslator.getTypeRank(o2.getClass());
      if (comp1TypeRank == comp2TypeRank) {
        return o1.compareTo(o2);
      }
      return Integer.compare(comp1TypeRank, comp2TypeRank);
    }
  }
}
TOP

Related Classes of com.google.appengine.api.datastore.BaseEntityComparator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.