--- /dev/null
+/*
+ * 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.dex.file;
+
+import com.android.dexgen.util.AnnotatedOutput;
+import com.android.dexgen.util.Hex;
+
+import java.util.HashMap;
+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();
+ }
+}