ParallelSorter.java revision 674060f01e9090cd21b3c5656cc3204912ad17a6
1/* 2 * Copyright 2003 The Apache Software Foundation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16package org.mockito.cglib.util; 17 18import java.lang.reflect.*; 19import java.util.Comparator; 20 21import org.mockito.asm.ClassVisitor; 22import org.mockito.cglib.core.*; 23 24/** 25 * For the efficient sorting of multiple arrays in parallel. 26 * <p> 27 * Given two arrays of equal length and varying types, the standard 28 * technique for sorting them in parallel is to create a new temporary 29 * object for each row, store the objects in a temporary array, sort the 30 * array using a custom comparator, and the extract the original values 31 * back into their respective arrays. This is wasteful in both time and 32 * memory. 33 * <p> 34 * This class generates bytecode customized to the particular set of 35 * arrays you need to sort, in such a way that both arrays are sorted 36 * in-place, simultaneously. 37 * <p> 38 * Two sorting algorithms are provided. 39 * Quicksort is best when you only need to sort by a single column, as 40 * it requires very few comparisons and swaps. Mergesort is best used 41 * when sorting multiple columns, as it is a "stable" sort--that is, it 42 * does not affect the relative order of equal objects from previous sorts. 43 * <p> 44 * The mergesort algorithm here is an "in-place" variant, which while 45 * slower, does not require a temporary array. 46 * 47 * @author Chris Nokleberg 48 */ 49abstract public class ParallelSorter extends SorterTemplate { 50 protected Object[] a; 51 private Comparer comparer; 52 53 protected ParallelSorter() { 54 } 55 56 abstract public ParallelSorter newInstance(Object[] arrays); 57 58 /** 59 * Create a new ParallelSorter object for a set of arrays. You may 60 * sort the arrays multiple times via the same ParallelSorter object. 61 * @param arrays An array of arrays to sort. The arrays may be a mix 62 * of primitive and non-primitive types, but should all be the same 63 * length. 64 * @param loader ClassLoader for generated class, uses "current" if null 65 */ 66 public static ParallelSorter create(Object[] arrays) { 67 Generator gen = new Generator(); 68 gen.setArrays(arrays); 69 return gen.create(); 70 } 71 72 private int len() { 73 return ((Object[])a[0]).length; 74 } 75 76 /** 77 * Sort the arrays using the quicksort algorithm. 78 * @param index array (column) to sort by 79 */ 80 public void quickSort(int index) { 81 quickSort(index, 0, len(), null); 82 } 83 84 /** 85 * Sort the arrays using the quicksort algorithm. 86 * @param index array (column) to sort by 87 * @param lo starting array index (row), inclusive 88 * @param hi ending array index (row), exclusive 89 */ 90 public void quickSort(int index, int lo, int hi) { 91 quickSort(index, lo, hi, null); 92 } 93 94 /** 95 * Sort the arrays using the quicksort algorithm. 96 * @param index array (column) to sort by 97 * @param cmp Comparator to use if the specified column is non-primitive 98 */ 99 public void quickSort(int index, Comparator cmp) { 100 quickSort(index, 0, len(), cmp); 101 } 102 103 /** 104 * Sort the arrays using the quicksort algorithm. 105 * @param index array (column) to sort by 106 * @param lo starting array index (row), inclusive 107 * @param hi ending array index (row), exclusive 108 * @param cmp Comparator to use if the specified column is non-primitive 109 */ 110 public void quickSort(int index, int lo, int hi, Comparator cmp) { 111 chooseComparer(index, cmp); 112 super.quickSort(lo, hi - 1); 113 } 114 115 /** 116 * @param index array (column) to sort by 117 */ 118 public void mergeSort(int index) { 119 mergeSort(index, 0, len(), null); 120 } 121 122 /** 123 * Sort the arrays using an in-place merge sort. 124 * @param index array (column) to sort by 125 * @param lo starting array index (row), inclusive 126 * @param hi ending array index (row), exclusive 127 */ 128 public void mergeSort(int index, int lo, int hi) { 129 mergeSort(index, lo, hi, null); 130 } 131 132 /** 133 * Sort the arrays using an in-place merge sort. 134 * @param index array (column) to sort by 135 * @param lo starting array index (row), inclusive 136 * @param hi ending array index (row), exclusive 137 */ 138 public void mergeSort(int index, Comparator cmp) { 139 mergeSort(index, 0, len(), cmp); 140 } 141 142 /** 143 * Sort the arrays using an in-place merge sort. 144 * @param index array (column) to sort by 145 * @param lo starting array index (row), inclusive 146 * @param hi ending array index (row), exclusive 147 * @param cmp Comparator to use if the specified column is non-primitive 148 */ 149 public void mergeSort(int index, int lo, int hi, Comparator cmp) { 150 chooseComparer(index, cmp); 151 super.mergeSort(lo, hi - 1); 152 } 153 154 private void chooseComparer(int index, Comparator cmp) { 155 Object array = a[index]; 156 Class type = array.getClass().getComponentType(); 157 if (type.equals(Integer.TYPE)) { 158 comparer = new IntComparer((int[])array); 159 } else if (type.equals(Long.TYPE)) { 160 comparer = new LongComparer((long[])array); 161 } else if (type.equals(Double.TYPE)) { 162 comparer = new DoubleComparer((double[])array); 163 } else if (type.equals(Float.TYPE)) { 164 comparer = new FloatComparer((float[])array); 165 } else if (type.equals(Short.TYPE)) { 166 comparer = new ShortComparer((short[])array); 167 } else if (type.equals(Byte.TYPE)) { 168 comparer = new ByteComparer((byte[])array); 169 } else if (cmp != null) { 170 comparer = new ComparatorComparer((Object[])array, cmp); 171 } else { 172 comparer = new ObjectComparer((Object[])array); 173 } 174 } 175 176 protected int compare(int i, int j) { 177 return comparer.compare(i, j); 178 } 179 180 interface Comparer { 181 int compare(int i, int j); 182 } 183 184 static class ComparatorComparer implements Comparer { 185 private Object[] a; 186 private Comparator cmp; 187 188 public ComparatorComparer(Object[] a, Comparator cmp) { 189 this.a = a; 190 this.cmp = cmp; 191 } 192 193 public int compare(int i, int j) { 194 return cmp.compare(a[i], a[j]); 195 } 196 } 197 198 static class ObjectComparer implements Comparer { 199 private Object[] a; 200 public ObjectComparer(Object[] a) { this.a = a; } 201 public int compare(int i, int j) { 202 return ((Comparable)a[i]).compareTo(a[j]); 203 } 204 } 205 206 static class IntComparer implements Comparer { 207 private int[] a; 208 public IntComparer(int[] a) { this.a = a; } 209 public int compare(int i, int j) { return a[i] - a[j]; } 210 } 211 212 static class LongComparer implements Comparer { 213 private long[] a; 214 public LongComparer(long[] a) { this.a = a; } 215 public int compare(int i, int j) { 216 long vi = a[i]; 217 long vj = a[j]; 218 return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; 219 } 220 } 221 222 static class FloatComparer implements Comparer { 223 private float[] a; 224 public FloatComparer(float[] a) { this.a = a; } 225 public int compare(int i, int j) { 226 float vi = a[i]; 227 float vj = a[j]; 228 return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; 229 } 230 } 231 232 static class DoubleComparer implements Comparer { 233 private double[] a; 234 public DoubleComparer(double[] a) { this.a = a; } 235 public int compare(int i, int j) { 236 double vi = a[i]; 237 double vj = a[j]; 238 return (vi == vj) ? 0 : (vi > vj) ? 1 : -1; 239 } 240 } 241 242 static class ShortComparer implements Comparer { 243 private short[] a; 244 public ShortComparer(short[] a) { this.a = a; } 245 public int compare(int i, int j) { return a[i] - a[j]; } 246 } 247 248 static class ByteComparer implements Comparer { 249 private byte[] a; 250 public ByteComparer(byte[] a) { this.a = a; } 251 public int compare(int i, int j) { return a[i] - a[j]; } 252 } 253 254 public static class Generator extends AbstractClassGenerator { 255 private static final Source SOURCE = new Source(ParallelSorter.class.getName()); 256 257 private Object[] arrays; 258 259 public Generator() { 260 super(SOURCE); 261 } 262 263 protected ClassLoader getDefaultClassLoader() { 264 return null; // TODO 265 } 266 267 public void setArrays(Object[] arrays) { 268 this.arrays = arrays; 269 } 270 271 public ParallelSorter create() { 272 return (ParallelSorter)super.create(ClassesKey.create(arrays)); 273 } 274 275 public void generateClass(ClassVisitor v) throws Exception { 276 if (arrays.length == 0) { 277 throw new IllegalArgumentException("No arrays specified to sort"); 278 } 279 for (int i = 0; i < arrays.length; i++) { 280 if (!arrays[i].getClass().isArray()) { 281 throw new IllegalArgumentException(arrays[i].getClass() + " is not an array"); 282 } 283 } 284 new ParallelSorterEmitter(v, getClassName(), arrays); 285 } 286 287 protected Object firstInstance(Class type) { 288 return ((ParallelSorter)ReflectUtils.newInstance(type)).newInstance(arrays); 289 } 290 291 protected Object nextInstance(Object instance) { 292 return ((ParallelSorter)instance).newInstance(arrays); 293 } 294 } 295} 296