| /* |
| * 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.dexgen.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(); |
| } |
| |
| } |