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