001/*
002 * Copyright (C) 2009-2011 Mathias Doenitz
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.parboiled.common;
018
019import org.parboiled.errors.GrammarException;
020
021import java.lang.reflect.Array;
022import java.lang.reflect.Constructor;
023import java.lang.reflect.GenericArrayType;
024import java.lang.reflect.ParameterizedType;
025import java.lang.reflect.Type;
026import java.lang.reflect.TypeVariable;
027import java.util.ArrayList;
028import java.util.Arrays;
029import java.util.HashMap;
030import java.util.LinkedList;
031import java.util.List;
032import java.util.Map;
033import java.util.Queue;
034
035/**
036 * General utility methods.
037 */
038public final class Utils {
039
040    public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0];
041    public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0];
042    public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0];
043    public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0];
044    public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0];
045    public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0];
046    public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0];
047    public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0];
048
049
050    private Utils() {}
051
052    public static Character[] toObjectArray(char[] array) {
053        if (array == null) return null;
054        if (array.length == 0) return EMPTY_CHARACTER_OBJECT_ARRAY;
055        Character[] result = new Character[array.length];
056        for (int i = 0; i < array.length; i++) result[i] = array[i];
057        return result;
058    }
059
060    public static Integer[] toObjectArray(int[] array) {
061        if (array == null) return null;
062        if (array.length == 0) return EMPTY_INTEGER_OBJECT_ARRAY;
063        Integer[] result = new Integer[array.length];
064        for (int i = 0; i < array.length; i++) result[i] = array[i];
065        return result;
066    }
067
068    public static Long[] toObjectArray(long[] array) {
069        if (array == null) return null;
070        if (array.length == 0) return EMPTY_LONG_OBJECT_ARRAY;
071        Long[] result = new Long[array.length];
072        for (int i = 0; i < array.length; i++) result[i] = array[i];
073        return result;
074    }
075
076    public static Short[] toObjectArray(short[] array) {
077        if (array == null) return null;
078        if (array.length == 0) return EMPTY_SHORT_OBJECT_ARRAY;
079        Short[] result = new Short[array.length];
080        for (int i = 0; i < array.length; i++) result[i] = array[i];
081        return result;
082    }
083
084    public static Byte[] toObjectArray(byte[] array) {
085        if (array == null) return null;
086        if (array.length == 0) return EMPTY_BYTE_OBJECT_ARRAY;
087        Byte[] result = new Byte[array.length];
088        for (int i = 0; i < array.length; i++) result[i] = array[i];
089        return result;
090    }
091
092    public static Float[] toObjectArray(float[] array) {
093        if (array == null) return null;
094        if (array.length == 0) return EMPTY_FLOAT_OBJECT_ARRAY;
095        Float[] result = new Float[array.length];
096        for (int i = 0; i < array.length; i++) result[i] = array[i];
097        return result;
098    }
099
100    public static Double[] toObjectArray(double[] array) {
101        if (array == null) return null;
102        if (array.length == 0) return EMPTY_DOUBLE_OBJECT_ARRAY;
103        Double[] result = new Double[array.length];
104        for (int i = 0; i < array.length; i++) result[i] = array[i];
105        return result;
106    }
107
108    public static Boolean[] toObjectArray(boolean[] array) {
109        if (array == null) return null;
110        if (array.length == 0) return EMPTY_BOOLEAN_OBJECT_ARRAY;
111        Boolean[] result = new Boolean[array.length];
112        for (int i = 0; i < array.length; i++) result[i] = array[i];
113        return result;
114    }
115
116    /**
117     * Joins the given arguments into one array.
118     *
119     * @param firstElement the first element
120     * @param moreElements more elements (optional)
121     * @return a new array containing all arguments.
122     */
123    @SuppressWarnings({"unchecked"})
124    public static <T> T[] arrayOf(T firstElement, T... moreElements) {
125        Preconditions.checkArgNotNull(moreElements, "moreElements");
126        Class elementType = moreElements.getClass().getComponentType();
127        T[] array = (T[]) Array.newInstance(elementType, moreElements.length + 1);
128        array[0] = firstElement;
129        System.arraycopy(moreElements, 0, array, 1, moreElements.length);
130        return array;
131    }
132
133    /**
134     * Joins the given arguments into one array.
135     *
136     * @param firstElement the first element
137     * @param secondElement the second element
138     * @param moreElements more elements (optional)
139     * @return a new array containing all arguments.
140     */
141    @SuppressWarnings({"unchecked"})
142    public static <T> T[] arrayOf(T firstElement, T secondElement, T... moreElements) {
143        Preconditions.checkArgNotNull(moreElements, "moreElements");
144        Class elementType = moreElements.getClass().getComponentType();
145        T[] array = (T[]) Array.newInstance(elementType, moreElements.length + 2);
146        array[0] = firstElement;
147        array[1] = secondElement;
148        System.arraycopy(moreElements, 0, array, 2, moreElements.length);
149        return array;
150    }
151
152    /**
153     * Joins the given arguments into one array.
154     *
155     * @param firstElements the first elements
156     * @param lastElement   the element to append
157     * @return a new array containing all arguments.
158     */
159    @SuppressWarnings({"unchecked"})
160    public static <T> T[] arrayOf(T[] firstElements, T lastElement) {
161        Preconditions.checkArgNotNull(firstElements, "firstElements");
162        Class elementType = firstElements.getClass().getComponentType();
163        T[] array = (T[]) Array.newInstance(elementType, firstElements.length + 1);
164        System.arraycopy(firstElements, 0, array, 0, firstElements.length);
165        array[firstElements.length] = lastElement;
166        return array;
167    }
168
169    /**
170     * Null enabled toString().
171     *
172     * @param obj the object
173     * @return the empty string of obj is null, otherwise obj.toString()
174     */
175    public static String toString(Object obj) {
176        return obj == null ? "" : obj.toString();
177    }
178
179    /**
180     * Null enabled equals().
181     *
182     * @param a the first object
183     * @param b the second object
184     * @return true if both are null or both are equal
185     */
186    public static <T> boolean equal(T a, T b) {
187        return a != null ? a.equals(b) : b == null;
188    }
189
190    /**
191     * Gets the actual type arguments that are used in a given implementation of a given generic base class or interface.
192     * (Based on code copyright 2007 by Ian Robertson).
193     *
194     * @param base           the generic base class or interface
195     * @param implementation the type (potentially) implementing the given base class or interface
196     * @return a list of the raw classes for the actual type arguments.
197     */
198    public static List<Class<?>> getTypeArguments(Class<?> base, Class<?> implementation) {
199        Preconditions.checkArgNotNull(base, "base");
200        Preconditions.checkArgNotNull(implementation, "implementation");
201        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
202
203        // first we need to resolve all supertypes up to the required base class or interface
204        // and find the right Type for it
205        Type type;
206
207        Queue<Type> toCheck = new LinkedList<Type>();
208        toCheck.add(implementation);
209        while (true) {
210            // if we have checked everything and not found the base class we return an empty list
211            if (toCheck.isEmpty()) return ImmutableList.of();
212
213            type = toCheck.remove();
214            Class<?> clazz;
215
216            if (type instanceof Class) {
217                // there is no useful information for us in raw types, so just keep going up the inheritance chain
218                clazz = (Class) type;
219                if (base.isInterface()) {
220                    // if we are actually looking for the type parameters to an interface we also need to
221                    // look at all the ones implemented by the given current one
222                    toCheck.addAll(Arrays.asList(clazz.getGenericInterfaces()));
223                }
224            } else if (type instanceof ParameterizedType) {
225                ParameterizedType parameterizedType = (ParameterizedType) type;
226                clazz = (Class) parameterizedType.getRawType();
227
228                // for instances of ParameterizedType we extract and remember all type arguments
229                TypeVariable<?>[] typeParameters = clazz.getTypeParameters();
230                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
231                for (int i = 0; i < actualTypeArguments.length; i++) {
232                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
233                }
234            } else {
235                return ImmutableList.of();
236            }
237
238            // we can stop if we have reached the sought for base type
239            if (base.equals(getClass(type))) break;
240
241            toCheck.add(clazz.getGenericSuperclass());
242        }
243
244        // finally, for each actual type argument provided to baseClass,
245        // determine (if possible) the raw class for that type argument.
246        Type[] actualTypeArguments;
247        if (type instanceof Class) {
248            actualTypeArguments = ((Class) type).getTypeParameters();
249        } else {
250            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
251        }
252        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
253        // resolve types by chasing down type variables.
254        for (Type baseType : actualTypeArguments) {
255            while (resolvedTypes.containsKey(baseType)) {
256                baseType = resolvedTypes.get(baseType);
257            }
258            typeArgumentsAsClasses.add(getClass(baseType));
259        }
260        return typeArgumentsAsClasses;
261    }
262
263    /**
264     * Get the underlying class for a type, or null if the type is a variable type.
265     * (Copyright 2007 by Ian Robertson).
266     *
267     * @param type the type
268     * @return the underlying class
269     */
270
271    public static Class<?> getClass(Type type) {
272        if (type instanceof Class) {
273            return (Class<?>) type;
274        } else if (type instanceof ParameterizedType) {
275            return getClass(((ParameterizedType) type).getRawType());
276        } else if (type instanceof GenericArrayType) {
277            Type componentType = ((GenericArrayType) type).getGenericComponentType();
278            Class<?> componentClass = getClass(componentType);
279            if (componentClass != null) {
280                return Array.newInstance(componentClass, 0).getClass();
281            }
282        }
283        return null;
284    }
285
286    /**
287     * Determines if the primitive type is boxed as the boxed type
288     * @param primitive the primitive type to check if boxed is the boxed type
289     * @param boxed the possible boxed type of the primitive
290     * @return true if boxed is the boxed type of primitive, false otherwise.
291     */
292    public static boolean isBoxedType(Class<?> primitive, Class<?> boxed) {
293        return (primitive.equals(boolean.class) && boxed.equals(Boolean.class)) ||
294               (primitive.equals(byte.class) && boxed.equals(Byte.class)) ||
295               (primitive.equals(char.class) && boxed.equals(Character.class)) ||
296               (primitive.equals(double.class) && boxed.equals(Double.class)) ||
297               (primitive.equals(float.class) && boxed.equals(Float.class)) ||
298               (primitive.equals(int.class) && boxed.equals(Integer.class)) ||
299               (primitive.equals(long.class) && boxed.equals(Long.class)) ||
300               (primitive.equals(short.class) && boxed.equals(Short.class)) ||
301               (primitive.equals(void.class) && boxed.equals(Void.class));
302    }
303
304    /**
305     * Finds the constructor of the given class that is compatible with the given arguments.
306     *
307     * @param type the class to find the constructor of
308     * @param args the arguments
309     * @return the constructor
310     */
311    public static Constructor findConstructor(Class<?> type, Object[] args) {
312        outer:
313        for (Constructor constructor : type.getConstructors()) {
314            Class<?>[] paramTypes = constructor.getParameterTypes();
315            if (paramTypes.length != args.length) continue;
316            for (int i = 0; i < args.length; i++) {
317                Object arg = args[i];
318                if (arg != null && !paramTypes[i].isAssignableFrom(arg.getClass()) && !isBoxedType(paramTypes[i], arg.getClass())) continue outer;
319                if (arg == null && paramTypes[i].isPrimitive()) continue outer;
320            }
321            return constructor;
322        }
323        throw new GrammarException("No constructor found for %s and the given %s arguments", type, args.length);
324    }
325
326    /**
327     * Formats the given long value into a human readable notation using the Kilo, Mega, Giga, etc. abbreviations.
328     *
329     * @param value the value to format
330     * @return the string representation
331     */
332    public static String humanize(long value) {
333        if (value < 0) {
334            return '-' + humanize(-value);
335        } else if (value > 1000000000000000000L) {
336            return Double.toString(
337                    (value + 500000000000000L) / 1000000000000000L * 1000000000000000L / 1000000000000000000.0) + 'E';
338        } else if (value > 100000000000000000L) {
339            return Double.toString(
340                    (value + 50000000000000L) / 100000000000000L * 100000000000000L / 1000000000000000.0) + 'P';
341        } else if (value > 10000000000000000L) {
342            return Double
343                    .toString((value + 5000000000000L) / 10000000000000L * 10000000000000L / 1000000000000000.0) + 'P';
344        } else if (value > 1000000000000000L) {
345            return Double
346                    .toString((value + 500000000000L) / 1000000000000L * 1000000000000L / 1000000000000000.0) + 'P';
347        } else if (value > 100000000000000L) {
348            return Double.toString((value + 50000000000L) / 100000000000L * 100000000000L / 1000000000000.0) + 'T';
349        } else if (value > 10000000000000L) {
350            return Double.toString((value + 5000000000L) / 10000000000L * 10000000000L / 1000000000000.0) + 'T';
351        } else if (value > 1000000000000L) {
352            return Double.toString((value + 500000000) / 1000000000 * 1000000000 / 1000000000000.0) + 'T';
353        } else if (value > 100000000000L) {
354            return Double.toString((value + 50000000) / 100000000 * 100000000 / 1000000000.0) + 'G';
355        } else if (value > 10000000000L) {
356            return Double.toString((value + 5000000) / 10000000 * 10000000 / 1000000000.0) + 'G';
357        } else if (value > 1000000000) {
358            return Double.toString((value + 500000) / 1000000 * 1000000 / 1000000000.0) + 'G';
359        } else if (value > 100000000) {
360            return Double.toString((value + 50000) / 100000 * 100000 / 1000000.0) + 'M';
361        } else if (value > 10000000) {
362            return Double.toString((value + 5000) / 10000 * 10000 / 1000000.0) + 'M';
363        } else if (value > 1000000) {
364            return Double.toString((value + 500) / 1000 * 1000 / 1000000.0) + 'M';
365        } else if (value > 100000) {
366            return Double.toString((value + 50) / 100 * 100 / 1000.0) + 'K';
367        } else if (value > 10000) {
368            return Double.toString((value + 5) / 10 * 10 / 1000.0) + 'K';
369        } else if (value > 1000) {
370            return Double.toString(value / 1000.0) + 'K';
371        } else {
372            return Long.toString(value) + ' ';
373        }
374    }
375}
376