1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.dx.util; 18 19 import java.util.Arrays; 20 21 /** 22 * Simple (mostly) fixed-size list of objects, which may be made immutable. 23 */ 24 public class FixedSizeList 25 extends MutabilityControl implements ToHuman { 26 /** {@code non-null;} array of elements */ 27 private Object[] arr; 28 29 /** 30 * Constructs an instance. All indices initially contain {@code null}. 31 * 32 * @param size the size of the list 33 */ FixedSizeList(int size)34 public FixedSizeList(int size) { 35 super(size != 0); 36 37 try { 38 arr = new Object[size]; 39 } catch (NegativeArraySizeException ex) { 40 // Translate the exception. 41 throw new IllegalArgumentException("size < 0"); 42 } 43 } 44 45 /** {@inheritDoc} */ 46 @Override equals(Object other)47 public boolean equals(Object other) { 48 if (this == other) { 49 // Easy out. 50 return true; 51 } 52 53 if ((other == null) || (getClass() != other.getClass())) { 54 // Another easy out. 55 return false; 56 } 57 58 FixedSizeList list = (FixedSizeList) other; 59 return Arrays.equals(arr, list.arr); 60 } 61 62 /** {@inheritDoc} */ 63 @Override hashCode()64 public int hashCode() { 65 return Arrays.hashCode(arr); 66 } 67 68 /** {@inheritDoc} */ 69 @Override toString()70 public String toString() { 71 String name = getClass().getName(); 72 73 return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', 74 ", ", 75 "}", 76 false); 77 } 78 79 /** 80 * {@inheritDoc} 81 * 82 * This method will only work if every element of the list 83 * implements {@link ToHuman}. 84 */ toHuman()85 public String toHuman() { 86 String name = getClass().getName(); 87 88 return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', 89 ", ", 90 "}", 91 true); 92 } 93 94 /** 95 * Gets a customized string form for this instance. 96 * 97 * @param prefix {@code null-ok;} prefix for the start of the result 98 * @param separator {@code null-ok;} separator to insert between each item 99 * @param suffix {@code null-ok;} suffix for the end of the result 100 * @return {@code non-null;} the custom string 101 */ toString(String prefix, String separator, String suffix)102 public String toString(String prefix, String separator, String suffix) { 103 return toString0(prefix, separator, suffix, false); 104 } 105 106 /** 107 * Gets a customized human string for this instance. This method will 108 * only work if every element of the list implements {@link 109 * ToHuman}. 110 * 111 * @param prefix {@code null-ok;} prefix for the start of the result 112 * @param separator {@code null-ok;} separator to insert between each item 113 * @param suffix {@code null-ok;} suffix for the end of the result 114 * @return {@code non-null;} the custom string 115 */ toHuman(String prefix, String separator, String suffix)116 public String toHuman(String prefix, String separator, String suffix) { 117 return toString0(prefix, separator, suffix, true); 118 } 119 120 /** 121 * Gets the number of elements in this list. 122 */ size()123 public final int size() { 124 return arr.length; 125 } 126 127 /** 128 * Shrinks this instance to fit, by removing any unset 129 * ({@code null}) elements, leaving the remaining elements in 130 * their original order. 131 */ shrinkToFit()132 public void shrinkToFit() { 133 int sz = arr.length; 134 int newSz = 0; 135 136 for (int i = 0; i < sz; i++) { 137 if (arr[i] != null) { 138 newSz++; 139 } 140 } 141 142 if (sz == newSz) { 143 return; 144 } 145 146 throwIfImmutable(); 147 148 Object[] newa = new Object[newSz]; 149 int at = 0; 150 151 for (int i = 0; i < sz; i++) { 152 Object one = arr[i]; 153 if (one != null) { 154 newa[at] = one; 155 at++; 156 } 157 } 158 159 arr = newa; 160 if (newSz == 0) { 161 setImmutable(); 162 } 163 } 164 165 /** 166 * Gets the indicated element. It is an error to call this with the 167 * index for an element which was never set; if you do that, this 168 * will throw {@code NullPointerException}. This method is 169 * protected so that subclasses may offer a safe type-checked 170 * public interface to their clients. 171 * 172 * @param n {@code >= 0, < size();} which element 173 * @return {@code non-null;} the indicated element 174 */ get0(int n)175 protected final Object get0(int n) { 176 try { 177 Object result = arr[n]; 178 179 if (result == null) { 180 throw new NullPointerException("unset: " + n); 181 } 182 183 return result; 184 } catch (ArrayIndexOutOfBoundsException ex) { 185 // Translate the exception. 186 return throwIndex(n); 187 } 188 } 189 190 /** 191 * Gets the indicated element, allowing {@code null}s to be 192 * returned. This method is protected so that subclasses may 193 * (optionally) offer a safe type-checked public interface to 194 * their clients. 195 * 196 * @param n {@code >= 0, < size();} which element 197 * @return {@code null-ok;} the indicated element 198 */ getOrNull0(int n)199 protected final Object getOrNull0(int n) { 200 return arr[n]; 201 } 202 203 /** 204 * Sets the element at the given index, but without doing any type 205 * checks on the element. This method is protected so that 206 * subclasses may offer a safe type-checked public interface to 207 * their clients. 208 * 209 * @param n {@code >= 0, < size();} which element 210 * @param obj {@code null-ok;} the value to store 211 */ set0(int n, Object obj)212 protected final void set0(int n, Object obj) { 213 throwIfImmutable(); 214 215 try { 216 arr[n] = obj; 217 } catch (ArrayIndexOutOfBoundsException ex) { 218 // Translate the exception. 219 throwIndex(n); 220 } 221 } 222 223 /** 224 * Throws the appropriate exception for the given index value. 225 * 226 * @param n the index value 227 * @return never 228 * @throws IndexOutOfBoundsException always thrown 229 */ throwIndex(int n)230 private Object throwIndex(int n) { 231 if (n < 0) { 232 throw new IndexOutOfBoundsException("n < 0"); 233 } 234 235 throw new IndexOutOfBoundsException("n >= size()"); 236 } 237 238 /** 239 * Helper for {@link #toString} and {@link #toHuman}, which both of 240 * those call to pretty much do everything. 241 * 242 * @param prefix {@code null-ok;} prefix for the start of the result 243 * @param separator {@code null-ok;} separator to insert between each item 244 * @param suffix {@code null-ok;} suffix for the end of the result 245 * @param human whether the output is to be human 246 * @return {@code non-null;} the custom string 247 */ toString0(String prefix, String separator, String suffix, boolean human)248 private String toString0(String prefix, String separator, String suffix, 249 boolean human) { 250 int len = arr.length; 251 StringBuffer sb = new StringBuffer(len * 10 + 10); 252 253 if (prefix != null) { 254 sb.append(prefix); 255 } 256 257 for (int i = 0; i < len; i++) { 258 if ((i != 0) && (separator != null)) { 259 sb.append(separator); 260 } 261 262 if (human) { 263 sb.append(((ToHuman) arr[i]).toHuman()); 264 } else { 265 sb.append(arr[i]); 266 } 267 } 268 269 if (suffix != null) { 270 sb.append(suffix); 271 } 272 273 return sb.toString(); 274 } 275 276 } 277