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 }