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