public void testAllEqual() { Ordering<Object> comparator = Ordering.allEqual(); assertSame(comparator, comparator.reverse()); assertEquals(comparator.compare(null, null), 0); assertEquals(comparator.compare(new Object(), new Object()), 0); assertEquals(comparator.compare("apples", "oranges"), 0); assertSame(comparator, reserialize(comparator)); assertEquals("Ordering.allEqual()", comparator.toString()); List<String> strings = ImmutableList.of("b", "a", "d", "c"); assertEquals(strings, comparator.sortedCopy(strings)); assertEquals(strings, comparator.immutableSortedCopy(strings)); }
/** * Subclass-aware comparator for {@link ProductDimension} objects. Performs a <em>logical</em> * comparison between instances. This comparator is <em>not</em> consistent with {@link * #equals(Object)}. * * <p>The {@link #compare(ProductDimension, ProductDimension)} method handles nulls, ordering nulls * last. * * @author Josh Radcliff */ class ProductDimensionComparator implements Comparator<ProductDimension> { private final ImmutableMap< Class<? extends ProductDimension>, Comparator<? extends ProductDimension>> comparatorMap; /** * Ordering for Comparable objects that places nulls last, then defers to the Comparable's natural * ordering. */ private static final Ordering<Comparable<?>> NULLS_LAST_NATURAL_ORDERING = Ordering.natural().nullsLast(); /** Ordering for any type that simply compares null-ness, placing nulls last. */ private static final Ordering<Object> NULLS_LAST_OBJECT_ORDERING = Ordering.allEqual().nullsLast(); /** * Ordering for ProductDimensionType that compares by {@link ProductDimensionType#getValue()}, * placing nulls last. */ private static final Ordering<ProductDimensionType> NULLS_LAST_DIMENSION_TYPE_ORDERING = new Ordering<ProductDimensionType>() { @Override public int compare(ProductDimensionType left, ProductDimensionType right) { return NULLS_LAST_CASE_INSENSITIVE_ORDERING.compare(left.getValue(), right.getValue()); } }.nullsLast(); /** * Ordering for String that places nulls last, then defers to {@link * String#CASE_INSENSITIVE_ORDER}. */ private static final Ordering<String> NULLS_LAST_CASE_INSENSITIVE_ORDERING = Ordering.from(String.CASE_INSENSITIVE_ORDER).nullsLast(); ProductDimensionComparator() { Builder<Class<? extends ProductDimension>, Comparator<? extends ProductDimension>> mapBuilder = ImmutableMap.builder(); mapBuilder.put(ProductBiddingCategory.class, new ProductBiddingCategoryComparator()); mapBuilder.put(ProductBrand.class, new ProductBrandComparator()); mapBuilder.put(ProductCanonicalCondition.class, new ProductCanonicalConditionComparator()); mapBuilder.put(ProductCustomAttribute.class, new ProductCustomAttributeComparator()); mapBuilder.put(ProductOfferId.class, new ProductOfferIdComparator()); mapBuilder.put(ProductType.class, new ProductTypeComparator()); comparatorMap = mapBuilder.build(); } @Override public int compare(@Nullable ProductDimension d1, @Nullable ProductDimension d2) { // Confirm that each of d1 and d2 is either null or is a supported subclass of ProductDimension. Preconditions.checkArgument( d1 == null || comparatorMap.containsKey(d1.getClass()), "Unsupported dimension type %s", d1); Preconditions.checkArgument( d2 == null || comparatorMap.containsKey(d2.getClass()), "Unsupported dimension type %s", d2); if (d1 == d2) { return 0; } // Order nulls last. int result = NULLS_LAST_OBJECT_ORDERING.compare(d1, d2); if (result != 0) { return result; } // java.lang.Class does not implement Comparable, so compare by fully qualified name instead. result = d1.getClass().getName().compareTo(d2.getClass().getName()); if (result != 0) { return result; } Preconditions.checkArgument( d1.getClass().equals(d2.getClass()), "Assumption failed - class of %s and %s are not equal", d1, d2); // Both dimensions are of the same type, so now compare by the appropriate values // for the type. return getDeepComparator(d1).compare(d1, d2); } /** * Returns the deep comparator that will compare the appropriate values between two instances of * type D. Throws an IllegalArgumentException if {@code D} is a subclass of ProductDimension not * supported by Shopping campaigns. As of v201409, the types not supported by Shopping campaigns * are: * * <ul> * <li>ProductAdWordsGrouping * <li>ProductAdWordsLabels * <li>ProductLegacyCondition * <li>ProductTypeFull * </ul> * * @throws IllegalArgumentException if {@code D} is not a dimension type supported by this * comparator */ private <D extends ProductDimension> Comparator<D> getDeepComparator(D dimension) { @SuppressWarnings("unchecked") Comparator<D> comparator = (Comparator<D>) comparatorMap.get(dimension.getClass()); Preconditions.checkArgument( comparator != null, "No comparator exists for %s. This comparator only supports comparisons of " + "ProductDimension subclasses supported by Shopping campaigns.", dimension); return comparator; } // Below are the ProductDimension subclass-specific implementations of Comparator. Each of these // assumes that it will only be passed non-null arguments since ProductDimensionComparator.compare // will only defer to one of these Comparators if neither object is null. /** * Comparator for {@link ProductBiddingCategory} objects that orders by type and then value. * Assumes that neither object is null. */ private static class ProductBiddingCategoryComparator implements Comparator<ProductBiddingCategory> { @Override public int compare(ProductBiddingCategory o1, ProductBiddingCategory o2) { int result = NULLS_LAST_DIMENSION_TYPE_ORDERING.compare(o1.getType(), o2.getType()); if (result != 0) { return result; } // Value is optional, so handle nulls. return NULLS_LAST_NATURAL_ORDERING.compare(o1.getValue(), o2.getValue()); } } /** * Comparator for {@link ProductBrand} objects that orders by value. Assumes that neither object * is null. */ private static class ProductBrandComparator implements Comparator<ProductBrand> { @Override public int compare(ProductBrand o1, ProductBrand o2) { return NULLS_LAST_CASE_INSENSITIVE_ORDERING.compare(o1.getValue(), o2.getValue()); } } /** * Comparator for {@link ProductCanonicalCondition} objects that orders by condition. Assumes that * neither object is null. */ private static class ProductCanonicalConditionComparator implements Comparator<ProductCanonicalCondition> { @Override public int compare(ProductCanonicalCondition o1, ProductCanonicalCondition o2) { ProductCanonicalConditionCondition condition1 = o1.getCondition(); ProductCanonicalConditionCondition condition2 = o2.getCondition(); if (condition1 == condition2) { return 0; } // If one condition is null but not the other, order so that the null object will be last. int result = NULLS_LAST_OBJECT_ORDERING.compare(condition1, condition2); if (result != 0) { return result; } return NULLS_LAST_NATURAL_ORDERING.compare(condition1.getValue(), condition2.getValue()); } } /** * Comparator for {@link ProductCustomAttribute} objects that orders by type then value. Assumes * that neither object is null. */ private static class ProductCustomAttributeComparator implements Comparator<ProductCustomAttribute> { @Override public int compare(ProductCustomAttribute o1, ProductCustomAttribute o2) { int result = NULLS_LAST_DIMENSION_TYPE_ORDERING.compare(o1.getType(), o2.getType()); if (result != 0) { return result; } return NULLS_LAST_CASE_INSENSITIVE_ORDERING.compare(o1.getValue(), o2.getValue()); } } /** * Comparator for {@link ProductOfferId} objects that orders by value. Assumes that neither object * is null. */ private static class ProductOfferIdComparator implements Comparator<ProductOfferId> { @Override public int compare(ProductOfferId o1, ProductOfferId o2) { return NULLS_LAST_CASE_INSENSITIVE_ORDERING.compare(o1.getValue(), o2.getValue()); } } /** * Comparator for {@link ProductType} objects that orders by type then value. Assumes that neither * object is null. */ private static class ProductTypeComparator implements Comparator<ProductType> { @Override public int compare(ProductType o1, ProductType o2) { int result = NULLS_LAST_DIMENSION_TYPE_ORDERING.compare(o1.getType(), o2.getType()); if (result != 0) { return result; } return NULLS_LAST_CASE_INSENSITIVE_ORDERING.compare(o1.getValue(), o2.getValue()); } } }