From 90bd381e91c873af5956da9e5f0cfa2b29675633 Mon Sep 17 00:00:00 2001 From: "Brian S. O'Neill" Date: Thu, 11 Oct 2007 21:00:12 +0000 Subject: Support sorting of arrays. --- .../com/amazon/carbonado/cursor/Comparators.java | 315 +++++++++++++++++++++ .../com/amazon/carbonado/cursor/SortedCursor.java | 75 ++++- .../carbonado/repo/indexed/ManagedIndex.java | 2 +- .../repo/replicated/ReplicatedRepository.java | 12 +- .../SyntheticStorableReferenceBuilder.java | 19 +- 5 files changed, 392 insertions(+), 31 deletions(-) create mode 100644 src/main/java/com/amazon/carbonado/cursor/Comparators.java (limited to 'src/main') diff --git a/src/main/java/com/amazon/carbonado/cursor/Comparators.java b/src/main/java/com/amazon/carbonado/cursor/Comparators.java new file mode 100644 index 0000000..bddd883 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/cursor/Comparators.java @@ -0,0 +1,315 @@ +/* + * Copyright 2007 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.cursor; + +import java.util.Comparator; + +import org.cojen.classfile.TypeDesc; + +/** + * Collection of utility comparators. + * + * @author Brian S O'Neill + */ +class Comparators { + /** + * Returns a comparator which can sort single or multi-dimensional arrays + * of primitves or Comparables. + * + * @param unsigned applicable only to arrays of bytes, shorts, ints, or longs + * @return null if unsupported + */ + static Comparator arrayComparator(Class arrayType, boolean unsigned) { + if (!arrayType.isArray()) { + throw new IllegalArgumentException(); + } + + Comparator c; + + TypeDesc componentType = TypeDesc.forClass(arrayType.getComponentType()); + switch (componentType.getTypeCode()) { + case TypeDesc.BYTE_CODE: + c = unsigned ? UnsignedByteArray : SignedByteArray; + break; + case TypeDesc.SHORT_CODE: + c = unsigned ? UnsignedShortArray : SignedShortArray; + break; + case TypeDesc.INT_CODE: + c = unsigned ? UnsignedIntArray : SignedIntArray; + break; + case TypeDesc.LONG_CODE: + c = unsigned ? UnsignedLongArray : SignedLongArray; + break; + case TypeDesc.BOOLEAN_CODE: + c = BooleanArray; + break; + case TypeDesc.CHAR_CODE: + c = CharArray; + break; + case TypeDesc.FLOAT_CODE: + c = FloatArray; + break; + case TypeDesc.DOUBLE_CODE: + c = DoubleArray; + break; + case TypeDesc.OBJECT_CODE: default: + if (componentType.isArray()) { + c = new ComparatorArray(arrayComparator(componentType.toClass(), unsigned)); + } else if (Comparable.class.isAssignableFrom(componentType.toClass())) { + c = ComparableArray; + } else { + c = null; + } + break; + } + + return (Comparator) c; + } + + private static final Comparator SignedByteArray = new Comparator() { + public int compare(byte[] a, byte[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i UnsignedByteArray = new Comparator() { + public int compare(byte[] a, byte[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i SignedShortArray = new Comparator() { + public int compare(short[] a, short[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i UnsignedShortArray = new Comparator() { + public int compare(short[] a, short[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i SignedIntArray = new Comparator() { + public int compare(int[] a, int[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i UnsignedIntArray = new Comparator() { + public int compare(int[] a, int[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i SignedLongArray = new Comparator() { + public int compare(long[] a, long[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i UnsignedLongArray = new Comparator() { + public int compare(long[] a, long[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i>> 1; + long sbi = bi >>> 1; + if (sai < sbi) { + return -1; + } + if (sai > sbi) { + return 1; + } + return (ai & 1) == 0 ? -1 : 1; + } + } + return a.length - b.length; + } + }; + + private static final Comparator BooleanArray = new Comparator() { + public int compare(boolean[] a, boolean[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i CharArray = new Comparator() { + public int compare(char[] a, char[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i FloatArray = new Comparator() { + public int compare(float[] a, float[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i DoubleArray = new Comparator() { + public int compare(double[] a, double[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i ComparableArray = new Comparator() + { + public int compare(Comparable[] a, Comparable[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i { + private final Comparator mComparator; + + ComparatorArray(Comparator comparator) { + mComparator = comparator; + } + + public int compare(Object[] a, Object[] b) { + int len = Math.min(a.length, b.length); + for (int i=0; i extends AbstractCursor { public static Comparator createComparator(Class type, String... orderProperties) { BeanComparator bc = BeanComparator.forClass(type); for (String property : orderProperties) { - bc = bc.orderBy(property); - bc = bc.caseSensitive(); + Class propertyType; + { + String name = property; + if (name.startsWith("+") || name.startsWith("-")) { + name = name.substring(1); + } + propertyType = propertyType(type, name); + } + bc = orderBy(bc, property, propertyType, Direction.ASCENDING); } return bc; } + /** + * @return null if unknown + */ + private static Class propertyType(Class enclosingType, String propertyName) { + Map properties = BeanIntrospector.getAllProperties(enclosingType); + int dotIndex = propertyName.indexOf('.'); + if (dotIndex < 0) { + BeanProperty bp = properties.get(propertyName); + return bp == null ? null : bp.getType(); + } else { + String parentName = propertyName.substring(0, dotIndex); + BeanProperty bp = properties.get(parentName); + if (bp == null) { + return null; + } + return propertyType(bp.getType(), propertyName.substring(dotIndex + 1)); + } + } + /** * Convenience method to create a comparator which orders storables by the * given properties. @@ -86,16 +117,14 @@ public class SortedCursor extends AbstractCursor { if (property == null) { throw new IllegalArgumentException(); } - bc = bc.orderBy(property.getChainedProperty().toString()); - bc = bc.caseSensitive(); - if (property.getDirection() == Direction.DESCENDING) { - bc = bc.reverse(); - } + bc = orderBy(bc, property.getChainedProperty().toString(), + property.getChainedProperty().getType(), property.getDirection()); } return bc; } + /** * Convenience method to create a comparator which orders storables by the * given properties. @@ -119,11 +148,35 @@ public class SortedCursor extends AbstractCursor { if (property == null) { throw new IllegalArgumentException(); } - bc = bc.orderBy(property.getChainedProperty().toString()); - bc = bc.caseSensitive(); - if (property.getDirection() == Direction.DESCENDING) { - bc = bc.reverse(); + bc = orderBy(bc, property.getChainedProperty().toString(), + property.getChainedProperty().getType(), property.getDirection()); + } + + return bc; + } + + private static BeanComparator orderBy(BeanComparator bc, String property, + Class type, Direction direction) + { + bc = bc.orderBy(property); + + if (type != null && type.isArray()) { + TypeDesc td = TypeDesc.forClass(type); + if (td.getRootComponentType() == TypeDesc.BYTE) { + bc = bc.using(Comparators.arrayComparator(type, true)); + } else { + bc = bc.using(Comparators.arrayComparator(type, false)); + if (bc == null) { + throw new IllegalArgumentException("Cannot sort by property type of " + + type.getName() + " for " + property); + } } + } else { + bc = bc.caseSensitive(); + } + + if (direction == Direction.DESCENDING) { + bc = bc.reverse(); } return bc; diff --git a/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java b/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java index 73469db..2d7f983 100644 --- a/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java +++ b/src/main/java/com/amazon/carbonado/repo/indexed/ManagedIndex.java @@ -287,7 +287,7 @@ class ManagedIndex implements IndexEntryAccessor { buffer.close(); throw new UniqueConstraintException ("Cannot build unique index because duplicates exist: " - + this); + + this + ", " + last + " == " + obj); } } last = obj; diff --git a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedRepository.java b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedRepository.java index 2c73200..b28fab5 100644 --- a/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedRepository.java +++ b/src/main/java/com/amazon/carbonado/repo/replicated/ReplicatedRepository.java @@ -24,8 +24,6 @@ import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.cojen.util.BeanComparator; - import com.amazon.carbonado.CorruptEncodingException; import com.amazon.carbonado.Cursor; import com.amazon.carbonado.FetchException; @@ -49,6 +47,8 @@ import com.amazon.carbonado.capability.ResyncCapability; import com.amazon.carbonado.capability.ShutdownCapability; import com.amazon.carbonado.capability.StorableInfoCapability; +import com.amazon.carbonado.cursor.SortedCursor; + import com.amazon.carbonado.info.Direction; import com.amazon.carbonado.info.StorableInfo; import com.amazon.carbonado.info.StorableIntrospector; @@ -389,11 +389,7 @@ class ReplicatedRepository } } - BeanComparator bc = BeanComparator.forClass(type); - for (String order : orderBy) { - bc = bc.orderBy(order); - bc = bc.caseSensitive(); - } + Comparator comparator = SortedCursor.createComparator(type, orderBy); replicaQuery = replicaQuery.orderBy(orderBy); masterQuery = masterQuery.orderBy(orderBy); @@ -417,7 +413,7 @@ class ReplicatedRepository replicaStorage, replicaQuery, masterStorage, masterQuery, throttle, desiredSpeed, - bc, replicaTxn); + comparator, replicaTxn); replicaTxn.commit(); } finally { diff --git a/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableReferenceBuilder.java b/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableReferenceBuilder.java index f9db620..4fddcd5 100644 --- a/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableReferenceBuilder.java +++ b/src/main/java/com/amazon/carbonado/synthetic/SyntheticStorableReferenceBuilder.java @@ -29,6 +29,7 @@ import java.util.Set; import com.amazon.carbonado.Storable; import com.amazon.carbonado.SupportException; +import com.amazon.carbonado.cursor.SortedCursor; import com.amazon.carbonado.info.Direction; import com.amazon.carbonado.info.StorableInfo; import com.amazon.carbonado.info.StorableIntrospector; @@ -219,18 +220,14 @@ public class SyntheticStorableReferenceBuilder mSyntheticClass = mBuilder.getStorableClass(); // We need a comparator which follows the same order as the generated - // storable. We can't construct it until we get here - { - BeanComparator bc = BeanComparator.forClass(mSyntheticClass); - Iterator props = mPrimaryKey.getProperties(); - while (props.hasNext()) { - String prop = props.next(); - // BeanComparator knows how to handle the '+' or '-' prefix. - bc = bc.orderBy(prop); - bc = bc.caseSensitive(); - } - mComparator = bc; + // storable. We can't construct it until we get here. + String[] orderBy = new String[mPrimaryKey.getPropertyCount()]; + int i=0; + Iterator it = mPrimaryKey.getProperties(); + while (it.hasNext()) { + orderBy[i++] = it.next(); } + mComparator = SortedCursor.createComparator(mSyntheticClass, orderBy); } return mSyntheticClass; } -- cgit v1.2.3