content: /*
   * 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.taobao.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();
      }
  
  }
  