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