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.dex.file;
  
  import com.taobao.android.dx.util.AnnotatedOutput;
  import com.taobao.android.dx.util.Hex;
  import java.util.List;
  
  /**
   * Class that represents a contiguous list of uniform items. Each
   * item in the list, in particular, must have the same write size and
   * alignment.
   *
   * <p>This class inherits its alignment from its items, bumped up to
   * {@code 4} if the items have a looser alignment requirement. If
   * it is more than {@code 4}, then there will be a gap after the
   * output list size (which is four bytes) and before the first item.</p>
   *
   * @param <T> type of element contained in an instance
   */
  public final class UniformListItem<T extends OffsettedItem>
          extends OffsettedItem {
      /** the size of the list header */
      private static final int HEADER_SIZE = 4;
  
      /** {@code non-null;} the item type */
      private final ItemType itemType;
  
      /** {@code non-null;} the contents */
      private final List<T> items;
  
      /**
       * Constructs an instance. It is illegal to modify the given list once
       * it is used to construct an instance of this class.
       *
       * @param itemType {@code non-null;} the type of the item
       * @param items {@code non-null and non-empty;} list of items to represent
       */
      public UniformListItem(ItemType itemType, List<T> items) {
          super(getAlignment(items), writeSize(items));
  
          if (itemType == null) {
              throw new NullPointerException("itemType == null");
          }
  
          this.items = items;
          this.itemType = itemType;
      }
  
      /**
       * Helper for {@link #UniformListItem}, which returns the alignment
       * requirement implied by the given list. See the header comment for
       * more details.
       *
       * @param items {@code non-null;} list of items being represented
       * @return {@code >= 4;} the alignment requirement
       */
      private static int getAlignment(List<? extends OffsettedItem> items) {
          try {
              // Since they all must have the same alignment, any one will do.
              return Math.max(HEADER_SIZE, items.get(0).getAlignment());
          } catch (IndexOutOfBoundsException ex) {
              // Translate the exception.
              throw new IllegalArgumentException("items.size() == 0");
          } catch (NullPointerException ex) {
              // Translate the exception.
              throw new NullPointerException("items == null");
          }
      }
  
      /**
       * Calculates the write size for the given list.
       *
       * @param items {@code non-null;} the list in question
       * @return {@code >= 0;} the write size
       */
      private static int writeSize(List<? extends OffsettedItem> items) {
          /*
           * This class assumes all included items are the same size,
           * an assumption which is verified in place0().
           */
          OffsettedItem first = items.get(0);
          return (items.size() * first.writeSize()) + getAlignment(items);
      }
  
      /** {@inheritDoc} */
      @Override
      public ItemType itemType() {
          return itemType;
      }
  
      /** {@inheritDoc} */
      @Override
      public String toString() {
          StringBuffer sb = new StringBuffer(100);
  
          sb.append(getClass().getName());
          sb.append(items);
  
          return sb.toString();
      }
  
      /** {@inheritDoc} */
      @Override
      public void addContents(DexFile file) {
          for (OffsettedItem i : items) {
              i.addContents(file);
          }
      }
  
      /** {@inheritDoc} */
      @Override
      public final String toHuman() {
          StringBuffer sb = new StringBuffer(100);
          boolean first = true;
  
          sb.append("{");
  
          for (OffsettedItem i : items) {
              if (first) {
                  first = false;
              } else {
                  sb.append(", ");
              }
              sb.append(i.toHuman());
          }
  
          sb.append("}");
          return sb.toString();
      }
  
      /**
       * Gets the underlying list of items.
       *
       * @return {@code non-null;} the list
       */
      public final List<T> getItems() {
          return items;
      }
  
      /** {@inheritDoc} */
      @Override
      protected void place0(Section addedTo, int offset) {
          offset += headerSize();
  
          boolean first = true;
          int theSize = -1;
          int theAlignment = -1;
  
          for (OffsettedItem i : items) {
              int size = i.writeSize();
              if (first) {
                  theSize = size;
                  theAlignment = i.getAlignment();
                  first = false;
              } else {
                  if (size != theSize) {
                      throw new UnsupportedOperationException(
                              "item size mismatch");
                  }
                  if (i.getAlignment() != theAlignment) {
                      throw new UnsupportedOperationException(
                              "item alignment mismatch");
                  }
              }
  
              offset = i.place(addedTo, offset) + size;
          }
      }
  
      /** {@inheritDoc} */
      @Override
      protected void writeTo0(DexFile file, AnnotatedOutput out) {
          int size = items.size();
  
          if (out.annotates()) {
              out.annotate(0, offsetString() + " " + typeName());
              out.annotate(4, "  size: " + Hex.u4(size));
          }
  
          out.writeInt(size);
  
          for (OffsettedItem i : items) {
              i.writeTo(file, out);
          }
      }
  
      /**
       * Get the size of the header of this list.
       *
       * @return {@code >= 0;} the header size
       */
      private int headerSize() {
          /*
           * Because of how this instance was set up, this is the same
           * as the alignment.
           */
          return getAlignment();
      }
  }
  