1 /*
   2  * $Id$
   3  *
   4  * Copyright (c) 1996, 2009, Oracle and/or its affiliates. All rights reserved.
   5  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   6  *
   7  * This code is free software; you can redistribute it and/or modify it
   8  * under the terms of the GNU General Public License version 2 only, as
   9  * published by the Free Software Foundation.  Oracle designates this
  10  * particular file as subject to the "Classpath" exception as provided
  11  * by Oracle in the LICENSE file that accompanied this code.
  12  *
  13  * This code is distributed in the hope that it will be useful, but WITHOUT
  14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  16  * version 2 for more details (a copy is included in the LICENSE file that
  17  * accompanied this code).
  18  *
  19  * You should have received a copy of the GNU General Public License version
  20  * 2 along with this work; if not, write to the Free Software Foundation,
  21  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  22  *
  23  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  24  * or visit www.oracle.com if you need additional information or have any
  25  * questions.
  26  */
  27 package com.sun.javatest.util;
  28 
  29 import java.lang.reflect.Array;
  30 
  31 /**
  32  * A class that manipulates arrays of Objects (no primitives).
  33  * Currently, you can remove any element and have the array shrink itself,
  34  * and you can append an element and have it expand.
  35  * <em>Note: You should either initialize array variables to a zero length array
  36  * or use the append method that accepts the Class argument to prevent array
  37  * storage exceptions.  This is an issue when this class is forced to create a
  38  * new array of unknown type and determine the type from the item being appended.</em>
  39  */
  40 
  41 public final class DynamicArray {
  42     /**
  43      * Append an object to the end of the array.
  44      *
  45      * @param oldArr The original array which we are appending to.  May be zero length or null.
  46      * @param newObj The new value to append to the oldArr.  Null is not a legal value.
  47      * @return A new array with the object appended to it.
  48      * @throws IllegalArgumentException If newObj is null.
  49      * @throws ArrayStoreException If there is a type mismatch between oldArr and newObj.
  50      */
  51     public static <T> T[] append(T[] oldArr, T newObj) {
  52         T[] newArr;
  53 
  54         if (oldArr == null) {
  55             if (newObj != null) {
  56                 newArr = (T[])(Array.newInstance(newObj.getClass(), 1));
  57                 newArr[0] = newObj;
  58             }
  59             else {
  60                 throw new IllegalArgumentException("Cannot add null item to null array.");
  61             }
  62         }
  63         else {
  64             newArr = (T[])(Array.newInstance(getArrayClass(oldArr), oldArr.length+1));
  65             System.arraycopy(oldArr, 0, newArr, 0, oldArr.length);
  66             newArr[newArr.length-1] = newObj;
  67         }
  68 
  69         return newArr;
  70     }
  71 
  72     /**
  73      * Append an object to the end of the array.
  74      *
  75      * @param oldArr The original array which we are appending to.  May be zero length or null.
  76      * @param newObj The new value to append to the oldArr.
  77      * @param arrayClass If the oldArr is null, a new array of arrayClass will be created.
  78      *        If arrayClass is null, the type of newObj will be used to create the array.
  79      * @return A new array with the object appended to it.
  80      * @throws IllegalArgumentException If newObj is null.
  81      * @throws ArrayStoreException If there is a type mismatch between oldArr and newObj.
  82      */
  83     public static <T> T[] append(T[] oldArr, T newObj, Class<? extends T> arrayClass) {
  84         T[] localArr;
  85 
  86         if (oldArr == null && arrayClass != null)
  87             localArr = (T[])(Array.newInstance(arrayClass, 0));
  88         else
  89             localArr = oldArr;
  90 
  91         return append(localArr, newObj);
  92     }
  93 
  94     /**
  95      * Join two arrays.
  96      * @param array1 The first array to be joined.
  97      * @param array2 The second array to be joined.
  98      * @return If either argument is null, the result is the other argument;
  99      * otherwise, the result is a new array, whose element type is taken from
 100      * the first array, containing the elements of the first array, followed
 101      * by the elements of the second array.
 102      */
 103     public static <T> T[] join(T[] array1, T[] array2) {
 104         if (array1 == null)
 105             return array2;
 106 
 107         if (array2 == null)
 108             return array1;
 109 
 110         Class<?> type = array1.getClass().getComponentType();
 111         int size = array1.length + array2.length;
 112         T[] newArray = (T[]) Array.newInstance(type, size);
 113         System.arraycopy(array1, 0, newArray, 0, array1.length);
 114         System.arraycopy(array2, 0, newArray, array1.length, array2.length);
 115         return newArray;
 116     }
 117 
 118     /**
 119      * Insert an object into the array at the specified index.
 120      *
 121      * @param oldArr The array to insert into.  May be null, in which case a new array
 122      *        will be created using the type of <code>newObj</code>.
 123      * @param newObj The object to insert.
 124      * @param location The index at which to insert the item.  An exception will occur
 125      *        if the location in out of range.  This location can be equal to
 126      *        <code>oldArr.length</code>, in which case this becomes an append
 127      *        operation.
 128      * @return A new array with the object inserted into it at the specified location.
 129      */
 130     public static <T> T[] insert(T[] oldArr, T newObj, int location) {
 131         T[] newArr;
 132 
 133         if (oldArr == null) {
 134             if (newObj != null) {
 135                 newArr = (T[])(Array.newInstance(newObj.getClass(), 1));
 136                 newArr[0] = newObj;
 137             }
 138             else {
 139                 throw new IllegalArgumentException("Cannot add null item to null array.");
 140             }
 141         }
 142         else {
 143             if (location > oldArr.length)
 144                 throw new IllegalArgumentException("Index location too large (" + location +
 145                                                     ").");
 146             newArr = (T[])(Array.newInstance(getArrayClass(oldArr), oldArr.length+1));
 147 
 148             if (location == 0) {
 149                 newArr[0] = newObj;
 150                 System.arraycopy(oldArr, 0, newArr, 1, oldArr.length);
 151             } else {
 152                 System.arraycopy(oldArr, 0, newArr, 0, location);
 153                 newArr[location] = newObj;
 154                 System.arraycopy(oldArr, location, newArr, location + 1,
 155                                  oldArr.length - location);
 156             }
 157         }
 158 
 159         return newArr;
 160     }
 161 
 162     /**
 163      * Remove the object at the specified index.
 164      * The new array type is determined by the type of element 0 in the
 165      * original array.
 166      *
 167      * @param oldArr The original array which we are removing from.  May not be
 168      *        null or zero length.
 169      * @param index The index to remove from <code>oldArr</code>.  Exception
 170      *        will be thrown if it is out of range.
 171      * @return An array of the same type as the original array (element 0), without the
 172      *         given index.  Zero length array if the last element is removed.
 173      * @exception IllegalArgumentException Will occur if the given index is out of range,
 174      *            or the given array is null.
 175      * @exception NullPointerException May occur if the source array has null
 176      *          values, particularly in the first array position.
 177      * @exception ArrayStoreException May occur if all the objects in the
 178      *          array do not match.
 179      */
 180     public static <T> T[] remove(T[] oldArr, int index) {
 181         T[] newArr;
 182 
 183         if (oldArr == null)
 184             throw new IllegalArgumentException("Cannot remove from null array.");
 185         else if (index > oldArr.length-1 || index < 0) {
 186             // invalid index
 187             throw new IllegalArgumentException("Index to remove from array is invalid (too small/large).");
 188         }
 189         else if (index == 0) {
 190             // chop the head
 191             newArr = (T[])(Array.newInstance(getArrayClass(oldArr), oldArr.length-1));
 192             System.arraycopy(oldArr, 1, newArr, 0, oldArr.length-1);
 193         }
 194         else if (index == oldArr.length-1) {
 195             // chop the tail
 196             newArr = (T[])(Array.newInstance(getArrayClass(oldArr), oldArr.length-1));
 197             System.arraycopy(oldArr, 0, newArr, 0, oldArr.length-1);
 198         }
 199         else {
 200             // chop the middle
 201             newArr = (T[])(Array.newInstance(getArrayClass(oldArr), oldArr.length-1));
 202             System.arraycopy(oldArr, 0, newArr, 0, index);
 203             System.arraycopy(oldArr, index+1, newArr, index,
 204                              oldArr.length-index-1);
 205         }
 206 
 207         return newArr;
 208     }
 209 
 210     /**
 211      * Remove the object from the array.
 212      * If the object is not found, the original array is returned unchanged.
 213      * The first instance of the target is removed.
 214      *
 215      * @param oldArr The array to remove from.
 216      * @param victim The object to remove from the array.  May be null.
 217      * @return Zero length array if the last element is removed.
 218      */
 219     public static <T> T[] remove(T[] oldArr, T victim) {
 220         T[] newArr;
 221 
 222         if (oldArr == null) {
 223             newArr = oldArr;
 224         } else {
 225             int location = find(oldArr, victim);
 226             if(location != -1) {
 227                 newArr = remove(oldArr, location);
 228             } else {
 229                 // not found, return the original array
 230                 newArr = oldArr;
 231             }
 232         }
 233 
 234         return newArr;
 235     }
 236 
 237     /**
 238      * Find the first index of the object that is equal (reference) to the
 239      * target.
 240      *
 241      * @param arr The data to search; should not be null.
 242      * @param target The reference to search for; can be null.
 243      * @return The array index of the target.
 244      */
 245     public static <T> int find(T[] arr, T target) {
 246         int index = -1;
 247 
 248         for(int i = 0; i < arr.length; i++) {
 249             if(arr[i] == target) {
 250                 index = i;
 251                 break;
 252             }
 253         }
 254 
 255         return index;
 256     }
 257 
 258     /**
 259      * Get the type of objects that the array is declared to hold.
 260      *
 261      * @param arr The array to examine.
 262      * @return The class of objects that the given array can hold.
 263      */
 264     protected static Class<?> getArrayClass(Object[] arr) {
 265         if(arr != null) {
 266             return arr.getClass().getComponentType();
 267         } else {
 268             return null;
 269         }
 270     }
 271 
 272 }
 273