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