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: * Returns a <code>List</code> containing the bytes from the
016: * specified array as <code>Byte</code> objects.
017: * @param data the byte data
018: * @return the <code>List</code> with the <code>Byte</code> objects
019: */
020: public static List getListFromByteArray(byte[] data) {
021: ArrayList list = new ArrayList(data.length);
022: for (int ii = 0; ii < data.length; ii++) {
023: list.add(new Byte(data[ii]));
024: }
025: return list;
026: }
027:
028: /**
029: * Returns a byte array containing the bytes from the <code>List</code>.
030: * The <code>List</code> must contain <code>Byte</code> objects.
031: * <code>null</code> entries in the <code>List</code> are
032: * allowed, the resulting byte will be 0.
033: * @param data the <code>List</code>
034: * @return the resulting byte array
035: */
036: public static byte[] getByteArrayFromList(List data) {
037: return getByteArrayFromList(data, 0);
038: }
039:
040: /**
041: * Returns a byte array containing the bytes from the <code>List</code>.
042: * The <code>List</code> must contain <code>Byte</code> objects.
043: * <code>null</code> entries in the <code>List</code> are
044: * allowed, the resulting byte will be 0.
045: * @param data the <code>List</code>
046: * @param index the index at which to start
047: * @return the resulting byte array
048: */
049: public static byte[] getByteArrayFromList(List data, int index) {
050: return getByteArrayFromList(data, index, data.size() - index);
051: }
052:
053: /**
054: * Returns a byte array containing the bytes from the <code>List</code>.
055: * The <code>List</code> must contain <code>Byte</code> objects.
056: * <code>null</code> entries in the <code>List</code> are
057: * allowed, the resulting byte will be 0.
058: * @param data the <code>List</code>
059: * @param index the index at which to start
060: * @param len the number of bytes
061: * @return the resulting byte array
062: */
063: public static byte[] getByteArrayFromList(List data, int index,
064: int len) {
065: if (data.size() == 0)
066: return new byte[0];
067: if (index >= data.size()) {
068: throw new IndexOutOfBoundsException("Position " + index
069: + " invalid in List of size " + data.size());
070: }
071: byte[] byteData = new byte[len];
072: for (int ii = index; ii < data.size() && ii < index + len; ii++) {
073: Byte nextValue = (Byte) data.get(ii);
074: if (null != nextValue) {
075: byteData[ii - index] = nextValue.byteValue();
076: }
077: }
078: return byteData;
079: }
080:
081: /**
082: * Copies the bytes from the specified array to the specified
083: * <code>List</code> as <code>Byte</code> objects starting
084: * at the specified index. Grows the list if necessary.
085: * <i>index</i> must be a valid index in the list.
086: * @param data the byte data
087: * @param list the <code>List</code>
088: * @param index the index at which to start copying
089: */
090: public static void addBytesToList(byte[] data, List list, int index) {
091: addBytesToList(data, 0, data.length, list, index);
092: }
093:
094: /**
095: * Copies the bytes from the specified array to the specified
096: * <code>List</code> as <code>Byte</code> objects starting
097: * at the specified index. Grows the list if necessary.
098: * <i>index</i> must be a valid index in the list.
099: * @param data the byte data
100: * @param offset the offset into the byte array at which to start
101: * @param len the number of bytes to copy
102: * @param list the <code>List</code>
103: * @param index the index at which to start copying
104: */
105: public static void addBytesToList(byte[] data, int offset, int len,
106: List list, int index) {
107: int bytesToIncrease = index + len - list.size();
108: if (bytesToIncrease > 0) {
109: for (int ii = 0; ii < bytesToIncrease; ii++) {
110: list.add(null);
111: }
112: }
113: for (int ii = index; ii < index + len; ii++) {
114: list.set(ii, new Byte(data[offset + ii - index]));
115: }
116: }
117:
118: /**
119: * Returns a truncated copy of <i>sourceArray</i>. <i>len</i>
120: * entries are copied.
121: * @param sourceArray the source array
122: * @param len the truncate length
123: * @return the truncated array
124: * @throws IllegalArgumentException if the specified object
125: * is not an array (either of reference or primitive
126: * component type)
127: */
128: public static Object truncateArray(Object sourceArray, int len) {
129: return truncateArray(sourceArray, 0, len);
130: }
131:
132: /**
133: * Returns a truncated copy of <i>sourceArray</i>. <i>len</i>
134: * entries are copied starting at the specified index.
135: * @param sourceArray the source array
136: * @param index the start index
137: * @param len the truncate length
138: * @return the truncated array
139: * @throws IllegalArgumentException if the specified object
140: * is not an array (either of reference or primitive
141: * component type)
142: */
143: public static Object truncateArray(Object sourceArray, int index,
144: int len) {
145: if (!sourceArray.getClass().isArray()) {
146: throw new IllegalArgumentException(
147: "sourceArray must be an array");
148: }
149: Object targetArray = Array.newInstance(sourceArray.getClass()
150: .getComponentType(), len);
151: System.arraycopy(sourceArray, index, targetArray, 0, len);
152: return targetArray;
153: }
154:
155: /**
156: * Returns a copy of the specified array. If <i>array</i>
157: * is not an array, the object itself will be returned.
158: * Otherwise a copy of the array will be returned. The components
159: * themselves are not cloned.
160: * @param array the array
161: * @return the copy of the array
162: */
163: public static Object copyArray(Object array) {
164: if (!array.getClass().isArray())
165: return array;
166: Class componentType = array.getClass().getComponentType();
167: int length = Array.getLength(array);
168: Object copy = Array.newInstance(componentType, Array
169: .getLength(array));
170: for (int ii = 0; ii < length; ii++) {
171: Array.set(copy, ii, Array.get(array, ii));
172: }
173: return copy;
174: }
175:
176: /**
177: * Returns an object array by wrapping primitive types. If the
178: * specified array is of primitive component type, an <code>Object[]</code>
179: * with the corresponding wrapper component type is returned.
180: * If the specified array is already an object array, the instance is
181: * returned unchanged.
182: * @param sourceArray the array
183: * @return the corresponding object array
184: * @throws IllegalArgumentException if the specified object
185: * is not an array (either of reference or primitive
186: * component type)
187: */
188: public static Object[] convertToObjectArray(Object sourceArray) {
189: if (!sourceArray.getClass().isArray()) {
190: throw new IllegalArgumentException(
191: "sourceArray must be an array");
192: }
193: Class componentType = sourceArray.getClass().getComponentType();
194: if (!componentType.isPrimitive()) {
195: return (Object[]) sourceArray;
196: }
197: if (componentType.equals(Boolean.TYPE)) {
198: componentType = Boolean.class;
199: } else if (componentType.equals(Byte.TYPE)) {
200: componentType = Byte.class;
201: } else if (componentType.equals(Character.TYPE)) {
202: componentType = Character.class;
203: } else if (componentType.equals(Short.TYPE)) {
204: componentType = Short.class;
205: } else if (componentType.equals(Integer.TYPE)) {
206: componentType = Integer.class;
207: } else if (componentType.equals(Long.TYPE)) {
208: componentType = Long.class;
209: } else if (componentType.equals(Float.TYPE)) {
210: componentType = Float.class;
211: } else if (componentType.equals(Double.TYPE)) {
212: componentType = Double.class;
213: }
214: int length = Array.getLength(sourceArray);
215: Object[] targetArray = (Object[]) Array.newInstance(
216: componentType, length);
217: for (int ii = 0; ii < length; ii++) {
218: targetArray[ii] = Array.get(sourceArray, ii);
219: }
220: return targetArray;
221: }
222:
223: /**
224: * Returns a primitive array by unwrapping the corresponding types. If the
225: * specified array is not an array of primitive wrapper types (e.g. <code>Integer[]</code>),
226: * an <code>IllegalArgumentException</code> will be thrown.
227: * If an array element is <code>null</code>, an <code>IllegalArgumentException</code>
228: * will be thrown.
229: * @param sourceArray the array
230: * @return the corresponding primitive array
231: * @throws IllegalArgumentException if the specified array
232: * is not an array of primitive wrapper types or if an
233: * array element is <code>null</code>
234: */
235: public static Object convertToPrimitiveArray(Object[] sourceArray) {
236: Class componentType = sourceArray.getClass().getComponentType();
237: if (componentType.equals(Boolean.class)) {
238: componentType = Boolean.TYPE;
239: } else if (componentType.equals(Byte.class)) {
240: componentType = Byte.TYPE;
241: } else if (componentType.equals(Character.class)) {
242: componentType = Character.TYPE;
243: } else if (componentType.equals(Short.class)) {
244: componentType = Short.TYPE;
245: } else if (componentType.equals(Integer.class)) {
246: componentType = Integer.TYPE;
247: } else if (componentType.equals(Long.class)) {
248: componentType = Long.TYPE;
249: } else if (componentType.equals(Float.class)) {
250: componentType = Float.TYPE;
251: } else if (componentType.equals(Double.class)) {
252: componentType = Double.TYPE;
253: } else {
254: throw new IllegalArgumentException(
255: "sourceArray is of type " + componentType
256: + " which is not allowed");
257: }
258: int length = Array.getLength(sourceArray);
259: Object targetArray = Array.newInstance(componentType, length);
260: for (int ii = 0; ii < length; ii++) {
261: Array.set(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: if (object.getClass().isArray())
276: return object;
277: Object array = Array.newInstance(object.getClass(), 1);
278: Array.set(array, 0, object);
279: return array;
280: }
281:
282: /**
283: * Compares the two specified arrays. If both passed objects are
284: * <code>null</code>, <code>true</code> is returned. If both passed
285: * objects are not arrays, they are compared using <code>equals</code>.
286: * Otherwise all array elements are compared using <code>equals</code>.
287: * This method does not handle multidimensional arrays, i.e. if an
288: * array contains another array, comparison is based on identity.
289: * @param array1 the first array
290: * @param array2 the second array
291: * @return <code>true</code> if the arrays are equal, <code>false</code>
292: * otherwise
293: */
294: public static boolean areArraysEqual(Object array1, Object array2) {
295: if (null == array1 && null == array2)
296: return true;
297: if (null == array1 || null == array2)
298: return false;
299: if (!array1.getClass().isArray()
300: && !array2.getClass().isArray())
301: return array1.equals(array2);
302: if (!array1.getClass().isArray()
303: || !array2.getClass().isArray())
304: return false;
305: int length1 = Array.getLength(array1);
306: int length2 = Array.getLength(array2);
307: if (length1 != length2)
308: return false;
309: for (int ii = 0; ii < length1; ii++) {
310: Object value1 = Array.get(array1, ii);
311: Object value2 = Array.get(array2, ii);
312: if (null != value1 && !value1.equals(value2))
313: return false;
314: if (null == value1 && null != value2)
315: return false;
316: }
317: return true;
318: }
319:
320: /**
321: * Returns a suitable hash code for the specified array. If the passed
322: * object is <code>null</code>, <code>0</code> is returned.
323: * It is allowed to pass an object that is not an array, in this case,
324: * the hash code of the object will be returned. Otherwise the hash code
325: * will be based on the array elements. <code>null</code> elements are
326: * allowed.
327: * This method does not handle multidimensional arrays, i.e. if an
328: * array contains another array, the hash code is based on identity.
329: * @param array the array
330: * @return a suitable hash code
331: */
332: public static int computeHashCode(Object array) {
333: if (null == array)
334: return 0;
335: if (!array.getClass().isArray())
336: return array.hashCode();
337: int length = Array.getLength(array);
338: int hashCode = 17;
339: for (int ii = 0; ii < length; ii++) {
340: Object value = Array.get(array, ii);
341: if (null != value)
342: hashCode = (31 * hashCode) + value.hashCode();
343: }
344: return hashCode;
345: }
346:
347: /**
348: * Returns the index of the first occurence of the
349: * array <i>bytes</i> in the array <i>source</i>.
350: * @param source the array in which to search
351: * @param bytes the array to search
352: * @return the index of the first occurence or
353: * -1, if <i>source</i> does not contain <i>bytes</i>
354: */
355: public static int indexOf(byte[] source, byte[] bytes) {
356: return indexOf(source, bytes, 0);
357: }
358:
359: /**
360: * Returns the index of the first occurence of the
361: * array <i>bytes</i> in the array <i>source</i>.
362: * @param source the array in which to search
363: * @param bytes the array to search
364: * @param index the index where to begin the search
365: * @return the index of the first occurence or
366: * -1, if <i>source</i> does not contain <i>bytes</i>
367: */
368: public static int indexOf(byte[] source, byte[] bytes, int index) {
369: if (index + bytes.length > source.length)
370: return -1;
371: for (int ii = index; ii <= source.length - bytes.length; ii++) {
372: int yy = 0;
373: while (yy < bytes.length && bytes[yy] == source[ii + yy])
374: yy++;
375: if (yy == bytes.length)
376: return ii;
377: }
378: return -1;
379: }
380:
381: /**
382: * Ensures that each entry in the specified string array
383: * is unique by adding a number to duplicate entries.
384: * I.e. if the string <code>"entry"</code> occurs three
385: * times, the three entries will be renamed to <code>"entry1"</code>,
386: * <code>"entry2"</code> and <code>"entry3"</code>.
387: * @param values the array of strings
388: */
389: public static void ensureUnique(String[] values) {
390: Map nameMap = collectOccurences(values);
391: renameDuplicates(values, nameMap);
392: }
393:
394: private static void renameDuplicates(String[] names, Map nameMap) {
395: Iterator iterator = nameMap.keySet().iterator();
396: while (iterator.hasNext()) {
397: String nextName = (String) iterator.next();
398: Integer nextValue = (Integer) nameMap.get(nextName);
399: if (nextValue.intValue() > 1) {
400: int number = 1;
401: for (int ii = 0; ii < names.length; ii++) {
402: if (names[ii].equals(nextName)) {
403: names[ii] = nextName + number;
404: number++;
405: }
406: }
407: }
408: }
409: }
410:
411: private static Map collectOccurences(String[] names) {
412: Map nameMap = new HashMap();
413: for (int ii = 0; ii < names.length; ii++) {
414: Integer currentValue = (Integer) nameMap.get(names[ii]);
415: if (null == currentValue) {
416: nameMap.put(names[ii], new Integer(1));
417: } else {
418: nameMap.put(names[ii], new Integer(currentValue
419: .intValue() + 1));
420: }
421: }
422: return nameMap;
423: }
424: }
|