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 */ 16 package org.mockito.cglib.util; 17 18 import java.lang.reflect.*; 19 import java.util.Comparator; 20 21 import org.mockito.asm.ClassVisitor; 22 import 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 */ 49 abstract public class ParallelSorter extends SorterTemplate { 50 protected Object[] a; 51 private Comparer comparer; 52 ParallelSorter()53 protected ParallelSorter() { 54 } 55 newInstance(Object[] arrays)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 */ create(Object[] arrays)66 public static ParallelSorter create(Object[] arrays) { 67 Generator gen = new Generator(); 68 gen.setArrays(arrays); 69 return gen.create(); 70 } 71 len()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 */ quickSort(int index)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 */ quickSort(int index, int lo, int hi)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 */ quickSort(int index, Comparator cmp)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 */ quickSort(int index, int lo, int hi, Comparator cmp)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 */ mergeSort(int index)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 */ mergeSort(int index, int lo, int hi)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 */ mergeSort(int index, Comparator cmp)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 */ mergeSort(int index, int lo, int hi, Comparator cmp)149 public void mergeSort(int index, int lo, int hi, Comparator cmp) { 150 chooseComparer(index, cmp); 151 super.mergeSort(lo, hi - 1); 152 } 153 chooseComparer(int index, Comparator cmp)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 compare(int i, int j)176 protected int compare(int i, int j) { 177 return comparer.compare(i, j); 178 } 179 180 interface Comparer { compare(int i, int j)181 int compare(int i, int j); 182 } 183 184 static class ComparatorComparer implements Comparer { 185 private Object[] a; 186 private Comparator cmp; 187 ComparatorComparer(Object[] a, Comparator cmp)188 public ComparatorComparer(Object[] a, Comparator cmp) { 189 this.a = a; 190 this.cmp = cmp; 191 } 192 compare(int i, int j)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; ObjectComparer(Object[] a)200 public ObjectComparer(Object[] a) { this.a = a; } compare(int i, int j)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; IntComparer(int[] a)208 public IntComparer(int[] a) { this.a = a; } compare(int i, int j)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; LongComparer(long[] a)214 public LongComparer(long[] a) { this.a = a; } compare(int i, int j)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; FloatComparer(float[] a)224 public FloatComparer(float[] a) { this.a = a; } compare(int i, int j)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; DoubleComparer(double[] a)234 public DoubleComparer(double[] a) { this.a = a; } compare(int i, int j)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; ShortComparer(short[] a)244 public ShortComparer(short[] a) { this.a = a; } compare(int i, int j)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; ByteComparer(byte[] a)250 public ByteComparer(byte[] a) { this.a = a; } compare(int i, int j)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 Generator()259 public Generator() { 260 super(SOURCE); 261 } 262 getDefaultClassLoader()263 protected ClassLoader getDefaultClassLoader() { 264 return null; // TODO 265 } 266 setArrays(Object[] arrays)267 public void setArrays(Object[] arrays) { 268 this.arrays = arrays; 269 } 270 create()271 public ParallelSorter create() { 272 return (ParallelSorter)super.create(ClassesKey.create(arrays)); 273 } 274 generateClass(ClassVisitor v)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 firstInstance(Class type)287 protected Object firstInstance(Class type) { 288 return ((ParallelSorter)ReflectUtils.newInstance(type)).newInstance(arrays); 289 } 290 nextInstance(Object instance)291 protected Object nextInstance(Object instance) { 292 return ((ParallelSorter)instance).newInstance(arrays); 293 } 294 } 295 } 296