| /* |
| * 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.dex.file; |
| |
| import com.android.dx.util.AnnotatedOutput; |
| import com.android.dx.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(); |
| } |
| } |