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    }