/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.dx.util; import java.util.Arrays; /** * Simple (mostly) fixed-size list of objects, which may be made immutable. */ public class FixedSizeList extends MutabilityControl implements ToHuman { /** {@code non-null;} array of elements */ private Object[] arr; /** * Constructs an instance. All indices initially contain {@code null}. * * @param size the size of the list */ public FixedSizeList(int size) { super(size != 0); try { arr = new Object[size]; } catch (NegativeArraySizeException ex) { // Translate the exception. throw new IllegalArgumentException("size < 0"); } } /** {@inheritDoc} */ @Override public boolean equals(Object other) { if (this == other) { // Easy out. return true; } if ((other == null) || (getClass() != other.getClass())) { // Another easy out. return false; } FixedSizeList list = (FixedSizeList) other; return Arrays.equals(arr, list.arr); } /** {@inheritDoc} */ @Override public int hashCode() { return Arrays.hashCode(arr); } /** {@inheritDoc} */ @Override public String toString() { String name = getClass().getName(); return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', ", ", "}", false); } /** * {@inheritDoc} * * This method will only work if every element of the list * implements {@link ToHuman}. */ public String toHuman() { String name = getClass().getName(); return toString0(name.substring(name.lastIndexOf('.') + 1) + '{', ", ", "}", true); } /** * Gets a customized string form for this instance. * * @param prefix {@code null-ok;} prefix for the start of the result * @param separator {@code null-ok;} separator to insert between each item * @param suffix {@code null-ok;} suffix for the end of the result * @return {@code non-null;} the custom string */ public String toString(String prefix, String separator, String suffix) { return toString0(prefix, separator, suffix, false); } /** * Gets a customized human string for this instance. This method will * only work if every element of the list implements {@link * ToHuman}. * * @param prefix {@code null-ok;} prefix for the start of the result * @param separator {@code null-ok;} separator to insert between each item * @param suffix {@code null-ok;} suffix for the end of the result * @return {@code non-null;} the custom string */ public String toHuman(String prefix, String separator, String suffix) { return toString0(prefix, separator, suffix, true); } /** * Gets the number of elements in this list. */ public final int size() { return arr.length; } /** * Shrinks this instance to fit, by removing any unset * ({@code null}) elements, leaving the remaining elements in * their original order. */ public void shrinkToFit() { int sz = arr.length; int newSz = 0; for (int i = 0; i < sz; i++) { if (arr[i] != null) { newSz++; } } if (sz == newSz) { return; } throwIfImmutable(); Object[] newa = new Object[newSz]; int at = 0; for (int i = 0; i < sz; i++) { Object one = arr[i]; if (one != null) { newa[at] = one; at++; } } arr = newa; if (newSz == 0) { setImmutable(); } } /** * Gets the indicated element. It is an error to call this with the * index for an element which was never set; if you do that, this * will throw {@code NullPointerException}. This method is * protected so that subclasses may offer a safe type-checked * public interface to their clients. * * @param n {@code >= 0, < size();} which element * @return {@code non-null;} the indicated element */ protected final Object get0(int n) { try { Object result = arr[n]; if (result == null) { throw new NullPointerException("unset: " + n); } return result; } catch (ArrayIndexOutOfBoundsException ex) { // Translate the exception. return throwIndex(n); } } /** * Gets the indicated element, allowing {@code null}s to be * returned. This method is protected so that subclasses may * (optionally) offer a safe type-checked public interface to * their clients. * * @param n {@code >= 0, < size();} which element * @return {@code null-ok;} the indicated element */ protected final Object getOrNull0(int n) { return arr[n]; } /** * Sets the element at the given index, but without doing any type * checks on the element. This method is protected so that * subclasses may offer a safe type-checked public interface to * their clients. * * @param n {@code >= 0, < size();} which element * @param obj {@code null-ok;} the value to store */ protected final void set0(int n, Object obj) { throwIfImmutable(); try { arr[n] = obj; } catch (ArrayIndexOutOfBoundsException ex) { // Translate the exception. throwIndex(n); } } /** * Throws the appropriate exception for the given index value. * * @param n the index value * @return never * @throws IndexOutOfBoundsException always thrown */ private Object throwIndex(int n) { if (n < 0) { throw new IndexOutOfBoundsException("n < 0"); } throw new IndexOutOfBoundsException("n >= size()"); } /** * Helper for {@link #toString} and {@link #toHuman}, which both of * those call to pretty much do everything. * * @param prefix {@code null-ok;} prefix for the start of the result * @param separator {@code null-ok;} separator to insert between each item * @param suffix {@code null-ok;} suffix for the end of the result * @param human whether the output is to be human * @return {@code non-null;} the custom string */ private String toString0(String prefix, String separator, String suffix, boolean human) { int len = arr.length; StringBuffer sb = new StringBuffer(len * 10 + 10); if (prefix != null) { sb.append(prefix); } for (int i = 0; i < len; i++) { if ((i != 0) && (separator != null)) { sb.append(separator); } if (human) { sb.append(((ToHuman) arr[i]).toHuman()); } else { sb.append(arr[i]); } } if (suffix != null) { sb.append(suffix); } return sb.toString(); } }