1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.dex.file; 18 19import com.android.dx.util.AnnotatedOutput; 20import com.android.dx.util.Hex; 21import java.util.List; 22 23/** 24 * Class that represents a contiguous list of uniform items. Each 25 * item in the list, in particular, must have the same write size and 26 * alignment. 27 * 28 * <p>This class inherits its alignment from its items, bumped up to 29 * {@code 4} if the items have a looser alignment requirement. If 30 * it is more than {@code 4}, then there will be a gap after the 31 * output list size (which is four bytes) and before the first item.</p> 32 * 33 * @param <T> type of element contained in an instance 34 */ 35public final class UniformListItem<T extends OffsettedItem> 36 extends OffsettedItem { 37 /** the size of the list header */ 38 private static final int HEADER_SIZE = 4; 39 40 /** {@code non-null;} the item type */ 41 private final ItemType itemType; 42 43 /** {@code non-null;} the contents */ 44 private final List<T> items; 45 46 /** 47 * Constructs an instance. It is illegal to modify the given list once 48 * it is used to construct an instance of this class. 49 * 50 * @param itemType {@code non-null;} the type of the item 51 * @param items {@code non-null and non-empty;} list of items to represent 52 */ 53 public UniformListItem(ItemType itemType, List<T> items) { 54 super(getAlignment(items), writeSize(items)); 55 56 if (itemType == null) { 57 throw new NullPointerException("itemType == null"); 58 } 59 60 this.items = items; 61 this.itemType = itemType; 62 } 63 64 /** 65 * Helper for {@link #UniformListItem}, which returns the alignment 66 * requirement implied by the given list. See the header comment for 67 * more details. 68 * 69 * @param items {@code non-null;} list of items being represented 70 * @return {@code >= 4;} the alignment requirement 71 */ 72 private static int getAlignment(List<? extends OffsettedItem> items) { 73 try { 74 // Since they all must have the same alignment, any one will do. 75 return Math.max(HEADER_SIZE, items.get(0).getAlignment()); 76 } catch (IndexOutOfBoundsException ex) { 77 // Translate the exception. 78 throw new IllegalArgumentException("items.size() == 0"); 79 } catch (NullPointerException ex) { 80 // Translate the exception. 81 throw new NullPointerException("items == null"); 82 } 83 } 84 85 /** 86 * Calculates the write size for the given list. 87 * 88 * @param items {@code non-null;} the list in question 89 * @return {@code >= 0;} the write size 90 */ 91 private static int writeSize(List<? extends OffsettedItem> items) { 92 /* 93 * This class assumes all included items are the same size, 94 * an assumption which is verified in place0(). 95 */ 96 OffsettedItem first = items.get(0); 97 return (items.size() * first.writeSize()) + getAlignment(items); 98 } 99 100 /** {@inheritDoc} */ 101 @Override 102 public ItemType itemType() { 103 return itemType; 104 } 105 106 /** {@inheritDoc} */ 107 @Override 108 public String toString() { 109 StringBuilder sb = new StringBuilder(100); 110 111 sb.append(getClass().getName()); 112 sb.append(items); 113 114 return sb.toString(); 115 } 116 117 /** {@inheritDoc} */ 118 @Override 119 public void addContents(DexFile file) { 120 for (OffsettedItem i : items) { 121 i.addContents(file); 122 } 123 } 124 125 /** {@inheritDoc} */ 126 @Override 127 public final String toHuman() { 128 StringBuilder sb = new StringBuilder(100); 129 boolean first = true; 130 131 sb.append("{"); 132 133 for (OffsettedItem i : items) { 134 if (first) { 135 first = false; 136 } else { 137 sb.append(", "); 138 } 139 sb.append(i.toHuman()); 140 } 141 142 sb.append("}"); 143 return sb.toString(); 144 } 145 146 /** 147 * Gets the underlying list of items. 148 * 149 * @return {@code non-null;} the list 150 */ 151 public final List<T> getItems() { 152 return items; 153 } 154 155 /** {@inheritDoc} */ 156 @Override 157 protected void place0(Section addedTo, int offset) { 158 offset += headerSize(); 159 160 boolean first = true; 161 int theSize = -1; 162 int theAlignment = -1; 163 164 for (OffsettedItem i : items) { 165 int size = i.writeSize(); 166 if (first) { 167 theSize = size; 168 theAlignment = i.getAlignment(); 169 first = false; 170 } else { 171 if (size != theSize) { 172 throw new UnsupportedOperationException( 173 "item size mismatch"); 174 } 175 if (i.getAlignment() != theAlignment) { 176 throw new UnsupportedOperationException( 177 "item alignment mismatch"); 178 } 179 } 180 181 offset = i.place(addedTo, offset) + size; 182 } 183 } 184 185 /** {@inheritDoc} */ 186 @Override 187 protected void writeTo0(DexFile file, AnnotatedOutput out) { 188 int size = items.size(); 189 190 if (out.annotates()) { 191 out.annotate(0, offsetString() + " " + typeName()); 192 out.annotate(4, " size: " + Hex.u4(size)); 193 } 194 195 out.writeInt(size); 196 197 for (OffsettedItem i : items) { 198 i.writeTo(file, out); 199 } 200 } 201 202 /** 203 * Get the size of the header of this list. 204 * 205 * @return {@code >= 0;} the header size 206 */ 207 private int headerSize() { 208 /* 209 * Because of how this instance was set up, this is the same 210 * as the alignment. 211 */ 212 return getAlignment(); 213 } 214} 215