1579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/* 2579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Copyright (C) 2007 The Android Open Source Project 3579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 4579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 5579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * you may not use this file except in compliance with the License. 6579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * You may obtain a copy of the License at 7579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 8579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * http://www.apache.org/licenses/LICENSE-2.0 9579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 10579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Unless required by applicable law or agreed to in writing, software 11579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * distributed under the License is distributed on an "AS IS" BASIS, 12579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * See the License for the specific language governing permissions and 14579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * limitations under the License. 15579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 16579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 17579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpackage com.android.dx.dex.file; 18579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 19579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.util.AnnotatedOutput; 20579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.util.ExceptionWithContext; 21579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport com.android.dx.util.Hex; 22579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 23579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.ArrayList; 24579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Arrays; 25579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Collection; 26579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Collections; 27579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Comparator; 28579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.HashMap; 29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.Map; 30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.NoSuchElementException; 31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.TreeMap; 32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/** 34579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * A section of a {@code .dex} file which consists of a sequence of 35579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@link OffsettedItem} objects, which may each be of a different concrete 36579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * class and/or size. 37579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 38579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <b>Note:</b> It is invalid for an item in an instance of this class to 39579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * have a larger alignment requirement than the alignment of this instance. 40579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 41579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpublic final class MixedItemSection extends Section { 42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson static enum SortType { 43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** no sorting */ 44579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson NONE, 45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 46579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** sort by type only */ 47579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson TYPE, 48579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 49579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** sort in class-major order, with instances sorted per-class */ 50579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson INSTANCE; 51579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson }; 52579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 53579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code non-null;} sorter which sorts instances by type */ 54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private static final Comparator<OffsettedItem> TYPE_SORTER = 55579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson new Comparator<OffsettedItem>() { 56579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int compare(OffsettedItem item1, OffsettedItem item2) { 57579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ItemType type1 = item1.itemType(); 58579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson ItemType type2 = item2.itemType(); 59579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return type1.compareTo(type2); 60579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 61579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson }; 62579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 63579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code non-null;} the items in this part */ 64579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final ArrayList<OffsettedItem> items; 65579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 66579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code non-null;} items that have been explicitly interned */ 67579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final HashMap<OffsettedItem, OffsettedItem> interns; 68579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 69579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@code non-null;} how to sort the items */ 70579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private final SortType sort; 71579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 72579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 73579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@code >= -1;} the current size of this part, in bytes, or {@code -1} 74579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * if not yet calculated 75579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 76579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson private int writeSize; 77579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 78579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 79579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Constructs an instance. The file offset is initially unknown. 80579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 81579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param name {@code null-ok;} the name of this instance, for annotation 82579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * purposes 83579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param file {@code non-null;} file that this instance is part of 84579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param alignment {@code > 0;} alignment requirement for the final output; 85579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * must be a power of 2 86579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param sort how the items should be sorted in the final output 87579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 88579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public MixedItemSection(String name, DexFile file, int alignment, 89579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson SortType sort) { 90579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson super(name, file, alignment); 91579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 92579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.items = new ArrayList<OffsettedItem>(100); 93579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.interns = new HashMap<OffsettedItem, OffsettedItem>(100); 94579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.sort = sort; 95579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson this.writeSize = -1; 96579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 97579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 98579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 99579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson @Override 100579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public Collection<? extends Item> items() { 101579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return items; 102579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 103579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 104579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 105579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson @Override 106579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int writeSize() { 107579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwIfNotPrepared(); 108579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return writeSize; 109579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 110579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 111579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 112579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson @Override 113579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int getAbsoluteItemOffset(Item item) { 114579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson OffsettedItem oi = (OffsettedItem) item; 115579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return oi.getAbsoluteOffset(); 116579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 117579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 118579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 119579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Gets the size of this instance, in items. 120579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 121579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return {@code >= 0;} the size 122579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 123579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public int size() { 124579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return items.size(); 125579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 126579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 127579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 128579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Writes the portion of the file header that refers to this instance. 129579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 130579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param out {@code non-null;} where to write 131579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 132579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeHeaderPart(AnnotatedOutput out) { 133579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwIfNotPrepared(); 134579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 135579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (writeSize == -1) { 136579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new RuntimeException("write size not yet set"); 137579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 138579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 139579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int sz = writeSize; 140579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int offset = (sz == 0) ? 0 : getFileOffset(); 141579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson String name = getName(); 142579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 143579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (name == null) { 144579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson name = "<unnamed>"; 145579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 146579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 147579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int spaceCount = 15 - name.length(); 148579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson char[] spaceArr = new char[spaceCount]; 149579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Arrays.fill(spaceArr, ' '); 150579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson String spaces = new String(spaceArr); 151579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 152579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (out.annotates()) { 153579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.annotate(4, name + "_size:" + spaces + Hex.u4(sz)); 154579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.annotate(4, name + "_off: " + spaces + Hex.u4(offset)); 155579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 156579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 157579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.writeInt(sz); 158579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.writeInt(offset); 159579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 160579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 161579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 162579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Adds an item to this instance. This will in turn tell the given item 163579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * that it has been added to this instance. It is invalid to add the 164579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * same item to more than one instance, nor to add the same items 165579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * multiple times to a single instance. 166579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 167579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param item {@code non-null;} the item to add 168579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 169579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void add(OffsettedItem item) { 170579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwIfPrepared(); 171579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 172579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson try { 173579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (item.getAlignment() > getAlignment()) { 174579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new IllegalArgumentException( 175579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson "incompatible item alignment"); 176579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 177579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } catch (NullPointerException ex) { 178579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson // Elucidate the exception. 179579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new NullPointerException("item == null"); 180579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 181579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 182579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson items.add(item); 183579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 184579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 185579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 186579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Interns an item in this instance, returning the interned instance 187579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * (which may not be the one passed in). This will add the item if no 188579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * equal item has been added. 189579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 190579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param item {@code non-null;} the item to intern 191579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return {@code non-null;} the equivalent interned instance 192579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 193579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public <T extends OffsettedItem> T intern(T item) { 194579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwIfPrepared(); 195579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 196579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson OffsettedItem result = interns.get(item); 197579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 198579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (result != null) { 199579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return (T) result; 200579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 201579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 202579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson add(item); 203579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson interns.put(item, item); 204579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return item; 205579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 206579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 207579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 208579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Gets an item which was previously interned. 209579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 210579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param item {@code non-null;} the item to look for 211579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @return {@code non-null;} the equivalent already-interned instance 212579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 213579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public <T extends OffsettedItem> T get(T item) { 214579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwIfNotPrepared(); 215579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 216579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson OffsettedItem result = interns.get(item); 217579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 218579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (result != null) { 219579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return (T) result; 220579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 221579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 222579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new NoSuchElementException(item.toString()); 223579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 224579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 225579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 226579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Writes an index of contents of the items in this instance of the 227579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * given type. If there are none, this writes nothing. If there are any, 228579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * then the index is preceded by the given intro string. 229579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * 230579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param out {@code non-null;} where to write to 231579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param itemType {@code non-null;} the item type of interest 232579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param intro {@code non-null;} the introductory string for non-empty indices 233579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 234579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType, 235579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson String intro) { 236579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwIfNotPrepared(); 237579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 238579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson TreeMap<String, OffsettedItem> index = 239579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson new TreeMap<String, OffsettedItem>(); 240579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 241579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson for (OffsettedItem item : items) { 242579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (item.itemType() == itemType) { 243579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson String label = item.toHuman(); 244579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson index.put(label, item); 245579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 246579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 247579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 248579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (index.size() == 0) { 249579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson return; 250579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 251579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 252579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.annotate(0, intro); 253579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 254579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) { 255579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson String label = entry.getKey(); 256579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson OffsettedItem item = entry.getValue(); 257579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.annotate(0, item.offsetString() + ' ' + label + '\n'); 258579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 259579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 260579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 261579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 262579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson @Override 263579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson protected void prepare0() { 264579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson DexFile file = getFile(); 265579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 266579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /* 267579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * It's okay for new items to be added as a result of an 268579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * addContents() call; we just have to deal with the possibility. 269579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 270579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 271579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int i = 0; 272579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson for (;;) { 273579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int sz = items.size(); 274579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (i >= sz) { 275579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson break; 276579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 277579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 278579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson for (/*i*/; i < sz; i++) { 279579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson OffsettedItem one = items.get(i); 280579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson one.addContents(file); 281579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 282579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 283579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 284579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 285579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** 286579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Places all the items in this instance at particular offsets. This 287579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * will call {@link OffsettedItem#place} on each item. If an item 288579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * does not know its write size before the call to {@code place}, 289579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * it is that call which is responsible for setting the write size. 290579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * This method may only be called once per instance; subsequent calls 291579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * will throw an exception. 292579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */ 293579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson public void placeItems() { 294579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throwIfNotPrepared(); 295579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 296579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson switch (sort) { 297579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson case INSTANCE: { 298579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Collections.sort(items); 299579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson break; 300579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 301579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson case TYPE: { 302579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson Collections.sort(items, TYPE_SORTER); 303579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson break; 304579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 305579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 306579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 307579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int sz = items.size(); 308579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int outAt = 0; 309579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson for (int i = 0; i < sz; i++) { 310579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson OffsettedItem one = items.get(i); 311579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson try { 312579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int placedAt = one.place(this, outAt); 313579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 314579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (placedAt < outAt) { 315579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new RuntimeException("bogus place() result for " + 316579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson one); 317579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 318579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 319579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson outAt = placedAt + one.writeSize(); 320579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } catch (RuntimeException ex) { 321579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw ExceptionWithContext.withContext(ex, 322579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson "...while placing " + one); 323579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 324579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 325579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 326579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson writeSize = outAt; 327579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 328579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 329579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson /** {@inheritDoc} */ 330579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson @Override 331579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson protected void writeTo0(AnnotatedOutput out) { 332579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson boolean annotates = out.annotates(); 333579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson boolean first = true; 334579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson DexFile file = getFile(); 335579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int at = 0; 336579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 337579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson for (OffsettedItem one : items) { 338579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (annotates) { 339579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (first) { 340579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson first = false; 341579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } else { 342579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.annotate(0, "\n"); 343579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 344579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 345579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 346579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int alignMask = one.getAlignment() - 1; 347579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson int writeAt = (at + alignMask) & ~alignMask; 348579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 349579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (at != writeAt) { 350579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson out.writeZeroes(writeAt - at); 351579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson at = writeAt; 352579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 353579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 354579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson one.writeTo(file, out); 355579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson at += one.writeSize(); 356579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 357579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson 358579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson if (at != writeSize) { 359579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson throw new RuntimeException("output size mismatch"); 360579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 361579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson } 362579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson} 363