001 package com.mockrunner.util.common; 002 003 import java.lang.reflect.Array; 004 import java.util.ArrayList; 005 import java.util.HashMap; 006 import java.util.Iterator; 007 import java.util.List; 008 import java.util.Map; 009 010 /** 011 * Util class for arrays 012 */ 013 public class ArrayUtil 014 { 015 /** 016 * Returns a <code>List</code> containing the bytes from the 017 * specified array as <code>Byte</code> objects. 018 * @param data the byte data 019 * @return the <code>List</code> with the <code>Byte</code> objects 020 */ 021 public static List getListFromByteArray(byte[] data) 022 { 023 ArrayList list = new ArrayList(data.length); 024 for(int ii = 0; ii < data.length; ii++) 025 { 026 list.add(new Byte(data[ii])); 027 } 028 return list; 029 } 030 031 /** 032 * Returns a <code>List</code> containing the objects from the 033 * specified array. 034 * @param data the data 035 * @return the <code>List</code> with the objects 036 */ 037 public static List getListFromObjectArray(Object[] data) 038 { 039 ArrayList list = new ArrayList(data.length); 040 for(int ii = 0; ii < data.length; ii++) 041 { 042 list.add(data[ii]); 043 } 044 return list; 045 } 046 047 /** 048 * Returns a byte array containing the bytes from the <code>List</code>. 049 * The <code>List</code> must contain <code>Byte</code> objects. 050 * <code>null</code> entries in the <code>List</code> are 051 * allowed, the resulting byte will be 0. 052 * @param data the <code>List</code> 053 * @return the resulting byte array 054 */ 055 public static byte[] getByteArrayFromList(List data) 056 { 057 return getByteArrayFromList(data, 0); 058 } 059 060 /** 061 * Returns a byte array containing the bytes from the <code>List</code>. 062 * The <code>List</code> must contain <code>Byte</code> objects. 063 * <code>null</code> entries in the <code>List</code> are 064 * allowed, the resulting byte will be 0. 065 * @param data the <code>List</code> 066 * @param index the index at which to start 067 * @return the resulting byte array 068 */ 069 public static byte[] getByteArrayFromList(List data, int index) 070 { 071 return getByteArrayFromList(data, index, data.size() - index); 072 } 073 074 /** 075 * Returns a byte array containing the bytes from the <code>List</code>. 076 * The <code>List</code> must contain <code>Byte</code> objects. 077 * <code>null</code> entries in the <code>List</code> are 078 * allowed, the resulting byte will be 0. 079 * @param data the <code>List</code> 080 * @param index the index at which to start 081 * @param len the number of bytes 082 * @return the resulting byte array 083 */ 084 public static byte[] getByteArrayFromList(List data, int index, int len) 085 { 086 if(data.size() == 0) return new byte[0]; 087 if(index >= data.size()) 088 { 089 throw new IndexOutOfBoundsException("Position " + index + " invalid in List of size " + data.size()); 090 } 091 byte[] byteData = new byte[len]; 092 for(int ii = index; ii < data.size() && ii < index + len; ii++) 093 { 094 Byte nextValue = (Byte)data.get(ii); 095 if(null != nextValue) 096 { 097 byteData[ii - index] = nextValue.byteValue(); 098 } 099 } 100 return byteData; 101 } 102 103 /** 104 * Copies the bytes from the specified array to the specified 105 * <code>List</code> as <code>Byte</code> objects starting 106 * at the specified index. Grows the list if necessary. 107 * <i>index</i> must be a valid index in the list. 108 * @param data the byte data 109 * @param list the <code>List</code> 110 * @param index the index at which to start copying 111 */ 112 public static void addBytesToList(byte[] data, List list, int index) 113 { 114 addBytesToList(data, 0, data.length, list, index); 115 } 116 117 /** 118 * Copies the bytes from the specified array to the specified 119 * <code>List</code> as <code>Byte</code> objects starting 120 * at the specified index. Grows the list if necessary. 121 * <i>index</i> must be a valid index in the list. 122 * @param data the byte data 123 * @param offset the offset into the byte array at which to start 124 * @param len the number of bytes to copy 125 * @param list the <code>List</code> 126 * @param index the index at which to start copying 127 */ 128 public static void addBytesToList(byte[] data, int offset, int len, List list, int index) 129 { 130 int bytesToIncrease = index + len - list.size(); 131 if(bytesToIncrease > 0) 132 { 133 for(int ii = 0; ii < bytesToIncrease; ii++) 134 { 135 list.add(null); 136 } 137 } 138 for(int ii = index; ii < index + len; ii++) 139 { 140 list.set(ii, new Byte(data[offset + ii - index])); 141 } 142 } 143 144 /** 145 * Returns a truncated copy of <i>sourceArray</i>. <i>len</i> 146 * entries are copied. 147 * @param sourceArray the source array 148 * @param len the truncate length 149 * @return the truncated array 150 * @throws IllegalArgumentException if the specified object 151 * is not an array (either of reference or primitive 152 * component type) 153 */ 154 public static Object truncateArray(Object sourceArray, int len) 155 { 156 return truncateArray(sourceArray, 0, len); 157 } 158 159 /** 160 * Returns a truncated copy of <i>sourceArray</i>. <i>len</i> 161 * entries are copied starting at the specified index. 162 * @param sourceArray the source array 163 * @param index the start index 164 * @param len the truncate length 165 * @return the truncated array 166 * @throws IllegalArgumentException if the specified object 167 * is not an array (either of reference or primitive 168 * component type) 169 */ 170 public static Object truncateArray(Object sourceArray, int index, int len) 171 { 172 if(!sourceArray.getClass().isArray()) 173 { 174 throw new IllegalArgumentException("sourceArray must be an array"); 175 } 176 Object targetArray = Array.newInstance(sourceArray.getClass().getComponentType(), len); 177 System.arraycopy(sourceArray, index, targetArray, 0, len); 178 return targetArray; 179 } 180 181 /** 182 * Returns a copy of the specified array. If <i>array</i> 183 * is not an array, the object itself will be returned. 184 * Otherwise a copy of the array will be returned. The components 185 * themselves are not cloned. 186 * @param array the array 187 * @return the copy of the array 188 */ 189 public static Object copyArray(Object array) 190 { 191 if(!array.getClass().isArray()) return array; 192 Class componentType = array.getClass().getComponentType(); 193 int length = Array.getLength(array); 194 Object copy = Array.newInstance(componentType, Array.getLength(array)); 195 for(int ii = 0; ii < length; ii++) 196 { 197 Array.set(copy, ii, Array.get(array, ii)); 198 } 199 return copy; 200 } 201 202 /** 203 * Returns an object array by wrapping primitive types. If the 204 * specified array is of primitive component type, an <code>Object[]</code> 205 * with the corresponding wrapper component type is returned. 206 * If the specified array is already an object array, the instance is 207 * returned unchanged. 208 * @param sourceArray the array 209 * @return the corresponding object array 210 * @throws IllegalArgumentException if the specified object 211 * is not an array (either of reference or primitive 212 * component type) 213 */ 214 public static Object[] convertToObjectArray(Object sourceArray) 215 { 216 if(!sourceArray.getClass().isArray()) 217 { 218 throw new IllegalArgumentException("sourceArray must be an array"); 219 } 220 Class componentType = sourceArray.getClass().getComponentType(); 221 if(!componentType.isPrimitive()) 222 { 223 return (Object[])sourceArray; 224 } 225 if(componentType.equals(Boolean.TYPE)) 226 { 227 componentType = Boolean.class; 228 } 229 else if(componentType.equals(Byte.TYPE)) 230 { 231 componentType = Byte.class; 232 } 233 else if(componentType.equals(Character.TYPE)) 234 { 235 componentType = Character.class; 236 } 237 else if(componentType.equals(Short.TYPE)) 238 { 239 componentType = Short.class; 240 } 241 else if(componentType.equals(Integer.TYPE)) 242 { 243 componentType = Integer.class; 244 } 245 else if(componentType.equals(Long.TYPE)) 246 { 247 componentType = Long.class; 248 } 249 else if(componentType.equals(Float.TYPE)) 250 { 251 componentType = Float.class; 252 } 253 else if(componentType.equals(Double.TYPE)) 254 { 255 componentType = Double.class; 256 } 257 int length = Array.getLength(sourceArray); 258 Object[] targetArray = (Object[])Array.newInstance(componentType, length); 259 for(int ii = 0; ii < length; ii++) 260 { 261 targetArray[ii] = Array.get(sourceArray, ii); 262 } 263 return targetArray; 264 } 265 266 /** 267 * Creates an array with a single object as component. 268 * If the specified object is an array, it will be returned 269 * unchanged. Otherwise an array with the specified object 270 * as the single element will be returned. 271 * @param object the object 272 * @return the corresponding array 273 */ 274 public static Object convertToArray(Object object) 275 { 276 if(object.getClass().isArray()) return object; 277 Object array = Array.newInstance(object.getClass(), 1); 278 Array.set(array, 0, object); 279 return array; 280 } 281 282 /** 283 * Returns the index of the first occurence of the 284 * array <i>bytes</i> in the array <i>source</i>. 285 * @param source the array in which to search 286 * @param bytes the array to search 287 * @return the index of the first occurence, resp. 288 * -1, if <i>source</i> does not contain <i>bytes</i> 289 */ 290 public static int indexOf(byte[] source, byte[] bytes) 291 { 292 return indexOf(source, bytes, 0); 293 } 294 295 /** 296 * Returns the index of the first occurence of the 297 * array <i>bytes</i> in the array <i>source</i>. 298 * @param source the array in which to search 299 * @param bytes the array to search 300 * @param index the index where to begin the search 301 * @return the index of the first occurence, resp. 302 * -1, if <i>source</i> does not contain <i>bytes</i> 303 */ 304 public static int indexOf(byte[] source, byte[] bytes, int index) 305 { 306 if(index + bytes.length > source.length) return -1; 307 for(int ii = index; ii <= source.length - bytes.length; ii++) 308 { 309 int yy = 0; 310 while(yy < bytes.length && bytes[yy] == source[ii + yy]) yy++; 311 if(yy == bytes.length) return ii; 312 } 313 return -1; 314 } 315 316 /** 317 * Ensures that each entry in the specified string array 318 * is unique by adding a number to duplicate entries. 319 * I.e. if the string <code>"entry"</code> occurs three 320 * times, the three entries will be renamed to <code>"entry1"</code>, 321 * <code>"entry2"</code> and <code>"entry3"</code>. 322 * @param values the array of strings 323 */ 324 public static void ensureUnique(String[] values) 325 { 326 Map nameMap = collectOccurences(values); 327 renameDuplicates(values, nameMap); 328 } 329 330 private static void renameDuplicates(String[] names, Map nameMap) 331 { 332 Iterator iterator = nameMap.keySet().iterator(); 333 while(iterator.hasNext()) 334 { 335 String nextName = (String)iterator.next(); 336 Integer nextValue = (Integer)nameMap.get(nextName); 337 if(nextValue.intValue() > 1) 338 { 339 int number = 1; 340 for(int ii = 0; ii < names.length; ii++) 341 { 342 if(names[ii].equals(nextName)) 343 { 344 names[ii] = nextName + number; 345 number++; 346 } 347 } 348 } 349 } 350 } 351 352 private static Map collectOccurences(String[] names) 353 { 354 Map nameMap = new HashMap(); 355 for(int ii = 0; ii < names.length; ii++) 356 { 357 Integer currentValue = (Integer)nameMap.get(names[ii]); 358 if(null == currentValue) 359 { 360 nameMap.put(names[ii], new Integer(1)); 361 } 362 else 363 { 364 nameMap.put(names[ii], new Integer(currentValue.intValue() + 1)); 365 } 366 } 367 return nameMap; 368 } 369 }