1917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul/*
2917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Copyright (C) 2007 The Android Open Source Project
3917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul *
4917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Licensed under the Apache License, Version 2.0 (the "License");
5917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * you may not use this file except in compliance with the License.
6917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * You may obtain a copy of the License at
7917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul *
8917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul *      http://www.apache.org/licenses/LICENSE-2.0
9917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul *
10917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * Unless required by applicable law or agreed to in writing, software
11917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * distributed under the License is distributed on an "AS IS" BASIS,
12917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * See the License for the specific language governing permissions and
14917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * limitations under the License.
15917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */
16917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
17917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulpackage com.android.dexgen.dex.file;
18917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
19917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport com.android.dexgen.util.AnnotatedOutput;
20917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport com.android.dexgen.util.ExceptionWithContext;
21917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport com.android.dexgen.util.Hex;
22917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
23917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.ArrayList;
24917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.Arrays;
25917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.Collection;
26917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.Collections;
27917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.Comparator;
28917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.HashMap;
29917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.Map;
30917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.NoSuchElementException;
31917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulimport java.util.TreeMap;
32917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
33917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul/**
34917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * A section of a {@code .dex} file which consists of a sequence of
35917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * {@link OffsettedItem} objects, which may each be of a different concrete
36917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * class and/or size.
37917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul *
38917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * <b>Note:</b> It is invalid for an item in an instance of this class to
39917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul * have a larger alignment requirement than the alignment of this instance.
40917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul */
41917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgulpublic final class MixedItemSection extends Section {
42917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    static enum SortType {
43917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        /** no sorting */
44917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        NONE,
45917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
46917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        /** sort by type only */
47917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        TYPE,
48917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
49917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        /** sort in class-major order, with instances sorted per-class */
50917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        INSTANCE;
51917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    };
52917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
53917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@code non-null;} sorter which sorts instances by type */
54917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    private static final Comparator<OffsettedItem> TYPE_SORTER =
55917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        new Comparator<OffsettedItem>() {
56917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        public int compare(OffsettedItem item1, OffsettedItem item2) {
57917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            ItemType type1 = item1.itemType();
58917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            ItemType type2 = item2.itemType();
59917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            return type1.compareTo(type2);
60917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
61917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    };
62917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
63917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@code non-null;} the items in this part */
64917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    private final ArrayList<OffsettedItem> items;
65917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
66917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@code non-null;} items that have been explicitly interned */
67917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    private final HashMap<OffsettedItem, OffsettedItem> interns;
68917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
69917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@code non-null;} how to sort the items */
70917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    private final SortType sort;
71917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
72917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
73917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * {@code >= -1;} the current size of this part, in bytes, or {@code -1}
74917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * if not yet calculated
75917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
76917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    private int writeSize;
77917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
78917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
79917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Constructs an instance. The file offset is initially unknown.
80917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     *
81917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param name {@code null-ok;} the name of this instance, for annotation
82917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * purposes
83917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param file {@code non-null;} file that this instance is part of
84917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param alignment {@code > 0;} alignment requirement for the final output;
85917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * must be a power of 2
86917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param sort how the items should be sorted in the final output
87917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
88917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public MixedItemSection(String name, DexFile file, int alignment,
89917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            SortType sort) {
90917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        super(name, file, alignment);
91917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
92917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        this.items = new ArrayList<OffsettedItem>(100);
93917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        this.interns = new HashMap<OffsettedItem, OffsettedItem>(100);
94917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        this.sort = sort;
95917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        this.writeSize = -1;
96917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
97917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
98917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@inheritDoc} */
99917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    @Override
100917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public Collection<? extends Item> items() {
101917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        return items;
102917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
103917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
104917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@inheritDoc} */
105917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    @Override
106917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public int writeSize() {
107917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throwIfNotPrepared();
108917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        return writeSize;
109917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
110917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
111917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@inheritDoc} */
112917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    @Override
113917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public int getAbsoluteItemOffset(Item item) {
114917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        OffsettedItem oi = (OffsettedItem) item;
115917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        return oi.getAbsoluteOffset();
116917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
117917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
118917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
119917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Gets the size of this instance, in items.
120917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     *
121917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @return {@code >= 0;} the size
122917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
123917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public int size() {
124917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        return items.size();
125917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
126917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
127917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
128917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Writes the portion of the file header that refers to this instance.
129917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     *
130917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param out {@code non-null;} where to write
131917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
132917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public void writeHeaderPart(AnnotatedOutput out) {
133917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throwIfNotPrepared();
134917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
135917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        if (writeSize == -1) {
136917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            throw new RuntimeException("write size not yet set");
137917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
138917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
139917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        int sz = writeSize;
140917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        int offset = (sz == 0) ? 0 : getFileOffset();
141917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        String name = getName();
142917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
143917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        if (name == null) {
144917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            name = "<unnamed>";
145917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
146917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
147917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        int spaceCount = 15 - name.length();
148917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        char[] spaceArr = new char[spaceCount];
149917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        Arrays.fill(spaceArr, ' ');
150917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        String spaces = new String(spaceArr);
151917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
152917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        if (out.annotates()) {
153917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            out.annotate(4, name + "_size:" + spaces + Hex.u4(sz));
154917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            out.annotate(4, name + "_off: " + spaces + Hex.u4(offset));
155917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
156917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
157917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        out.writeInt(sz);
158917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        out.writeInt(offset);
159917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
160917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
161917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
162917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Adds an item to this instance. This will in turn tell the given item
163917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * that it has been added to this instance. It is invalid to add the
164917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * same item to more than one instance, nor to add the same items
165917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * multiple times to a single instance.
166917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     *
167917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param item {@code non-null;} the item to add
168917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
169917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public void add(OffsettedItem item) {
170917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throwIfPrepared();
171917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
172917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        try {
173917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            if (item.getAlignment() > getAlignment()) {
174917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                throw new IllegalArgumentException(
175917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                        "incompatible item alignment");
176917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
177917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        } catch (NullPointerException ex) {
178917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            // Elucidate the exception.
179917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            throw new NullPointerException("item == null");
180917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
181917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
182917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        items.add(item);
183917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
184917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
185917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
186917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Interns an item in this instance, returning the interned instance
187917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * (which may not be the one passed in). This will add the item if no
188917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * equal item has been added.
189917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     *
190917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param item {@code non-null;} the item to intern
191917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @return {@code non-null;} the equivalent interned instance
192917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
193917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public <T extends OffsettedItem> T intern(T item) {
194917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throwIfPrepared();
195917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
196917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        OffsettedItem result = interns.get(item);
197917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
198917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        if (result != null) {
199917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            return (T) result;
200917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
201917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
202917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        add(item);
203917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        interns.put(item, item);
204917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        return item;
205917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
206917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
207917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
208917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Gets an item which was previously interned.
209917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     *
210917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param item {@code non-null;} the item to look for
211917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @return {@code non-null;} the equivalent already-interned instance
212917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
213917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public <T extends OffsettedItem> T get(T item) {
214917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throwIfNotPrepared();
215917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
216917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        OffsettedItem result = interns.get(item);
217917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
218917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        if (result != null) {
219917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            return (T) result;
220917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
221917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
222917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throw new NoSuchElementException(item.toString());
223917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
224917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
225917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
226917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Writes an index of contents of the items in this instance of the
227917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * given type. If there are none, this writes nothing. If there are any,
228917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * then the index is preceded by the given intro string.
229917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     *
230917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param out {@code non-null;} where to write to
231917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param itemType {@code non-null;} the item type of interest
232917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * @param intro {@code non-null;} the introductory string for non-empty indices
233917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
234917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public void writeIndexAnnotation(AnnotatedOutput out, ItemType itemType,
235917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            String intro) {
236917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throwIfNotPrepared();
237917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
238917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        TreeMap<String, OffsettedItem> index =
239917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            new TreeMap<String, OffsettedItem>();
240917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
241917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        for (OffsettedItem item : items) {
242917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            if (item.itemType() == itemType) {
243917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                String label = item.toHuman();
244917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                index.put(label, item);
245917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
246917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
247917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
248917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        if (index.size() == 0) {
249917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            return;
250917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
251917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
252917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        out.annotate(0, intro);
253917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
254917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        for (Map.Entry<String, OffsettedItem> entry : index.entrySet()) {
255917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            String label = entry.getKey();
256917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            OffsettedItem item = entry.getValue();
257917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            out.annotate(0, item.offsetString() + ' ' + label + '\n');
258917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
259917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
260917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
261917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@inheritDoc} */
262917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    @Override
263917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    protected void prepare0() {
264917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        DexFile file = getFile();
265917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
266917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        /*
267917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul         * It's okay for new items to be added as a result of an
268917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul         * addContents() call; we just have to deal with the possibility.
269917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul         */
270917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
271917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        int i = 0;
272917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        for (;;) {
273917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            int sz = items.size();
274917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            if (i >= sz) {
275917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                break;
276917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
277917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
278917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            for (/*i*/; i < sz; i++) {
279917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                OffsettedItem one = items.get(i);
280917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                one.addContents(file);
281917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
282917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
283917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
284917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
285917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /**
286917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * Places all the items in this instance at particular offsets. This
287917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * will call {@link OffsettedItem#place} on each item. If an item
288917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * does not know its write size before the call to {@code place},
289917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * it is that call which is responsible for setting the write size.
290917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * This method may only be called once per instance; subsequent calls
291917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     * will throw an exception.
292917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul     */
293917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    public void placeItems() {
294917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        throwIfNotPrepared();
295917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
296917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        switch (sort) {
297917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            case INSTANCE: {
298917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                Collections.sort(items);
299917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                break;
300917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
301917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            case TYPE: {
302917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                Collections.sort(items, TYPE_SORTER);
303917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                break;
304917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
305917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
306917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
307917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        int sz = items.size();
308917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        int outAt = 0;
309917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        for (int i = 0; i < sz; i++) {
310917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            OffsettedItem one = items.get(i);
311917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            try {
312917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                int placedAt = one.place(this, outAt);
313917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
314917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                if (placedAt < outAt) {
315917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                    throw new RuntimeException("bogus place() result for " +
316917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                            one);
317917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                }
318917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
319917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                outAt = placedAt + one.writeSize();
320917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            } catch (RuntimeException ex) {
321917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                throw ExceptionWithContext.withContext(ex,
322917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                        "...while placing " + one);
323917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
324917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
325917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
326917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        writeSize = outAt;
327917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
328917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
329917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    /** {@inheritDoc} */
330917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    @Override
331917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    protected void writeTo0(AnnotatedOutput out) {
332917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        boolean annotates = out.annotates();
333917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        boolean first = true;
334917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        DexFile file = getFile();
335917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        int at = 0;
336917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
337917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        for (OffsettedItem one : items) {
338917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            if (annotates) {
339917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                if (first) {
340917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                    first = false;
341917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                } else {
342917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                    out.annotate(0, "\n");
343917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                }
344917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
345917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
346917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            int alignMask = one.getAlignment() - 1;
347917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            int writeAt = (at + alignMask) & ~alignMask;
348917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
349917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            if (at != writeAt) {
350917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                out.writeZeroes(writeAt - at);
351917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul                at = writeAt;
352917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            }
353917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
354917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            one.writeTo(file, out);
355917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            at += one.writeSize();
356917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
357917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul
358917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        if (at != writeSize) {
359917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul            throw new RuntimeException("output size mismatch");
360917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul        }
361917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul    }
362917cb222329ee8c035c3ffaf947e4265761b9367Piotr Gurgul}
363