OffsettedItem.java revision f6c387128427e121477c1b32ad35cdcaa5101ba3
1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dx.dex.file;
18
19import com.android.dx.util.AnnotatedOutput;
20import com.android.dx.util.ExceptionWithContext;
21
22/**
23 * An item in a Dalvik file which is referenced by absolute offset.
24 */
25public abstract class OffsettedItem extends Item
26        implements Comparable<OffsettedItem> {
27    /** &gt; 0; alignment requirement */
28    private final int alignment;
29
30    /** &gt;= -1; the size of this instance when written, in bytes, or
31     * <code>-1</code> if not yet known */
32    private int writeSize;
33
34    /**
35     * null-ok; section the item was added to, or <code>null</code> if
36     * not yet added
37     */
38    private Section addedTo;
39
40    /**
41     * &gt;= -1; assigned offset of the item from the start of its section,
42     * or <code>-1</code> if not yet assigned
43     */
44    private int offset;
45
46    /**
47     * Gets the absolute offset of the given item, returning <code>0</code>
48     * if handed <code>null</code>.
49     *
50     * @param item null-ok; the item in question
51     * @return &gt;= 0; the item's absolute offset, or <code>0</code>
52     * if <code>item == null</code>
53     */
54    public static int getAbsoluteOffsetOr0(OffsettedItem item) {
55        if (item == null) {
56            return 0;
57        }
58
59        return item.getAbsoluteOffset();
60    }
61
62    /**
63     * Constructs an instance. The offset is initially unassigned.
64     *
65     * @param alignment &gt; 0; output alignment requirement; must be a
66     * power of 2
67     * @param writeSize &gt;= -1; the size of this instance when written,
68     * in bytes, or <code>-1</code> if not immediately known
69     */
70    public OffsettedItem(int alignment, int writeSize) {
71        Section.validateAlignment(alignment);
72
73        if (writeSize < -1) {
74            throw new IllegalArgumentException("writeSize < -1");
75        }
76
77        this.alignment = alignment;
78        this.writeSize = writeSize;
79        this.addedTo = null;
80        this.offset = -1;
81    }
82
83    /**
84     * {@inheritDoc}
85     *
86     * Comparisons for this class are defined to be type-major (if the
87     * types don't match then the objects are not equal), with
88     * {@link #compareTo0} deciding same-type comparisons.
89     */
90    @Override
91    public final boolean equals(Object other) {
92        if (this == other) {
93            return true;
94        }
95
96        OffsettedItem otherItem = (OffsettedItem) other;
97        ItemType thisType = itemType();
98        ItemType otherType = otherItem.itemType();
99
100        if (thisType != otherType) {
101            return false;
102        }
103
104        return (compareTo0(otherItem) == 0);
105    }
106
107    /**
108     * {@inheritDoc}
109     *
110     * Comparisons for this class are defined to be class-major (if the
111     * classes don't match then the objects are not equal), with
112     * {@link #compareTo0} deciding same-class comparisons.
113     */
114    public final int compareTo(OffsettedItem other) {
115        if (this == other) {
116            return 0;
117        }
118
119        ItemType thisType = itemType();
120        ItemType otherType = other.itemType();
121
122        if (thisType != otherType) {
123            return thisType.compareTo(otherType);
124        }
125
126        return compareTo0(other);
127    }
128
129    /**
130     * Sets the write size of this item. This may only be called once
131     * per instance, and only if the size was unknown upon instance
132     * creation.
133     *
134     * @param writeSize &gt; 0; the write size, in bytes
135     */
136    public final void setWriteSize(int writeSize) {
137        if (writeSize < 0) {
138            throw new IllegalArgumentException("writeSize < 0");
139        }
140
141        if (this.writeSize >= 0) {
142            throw new UnsupportedOperationException("writeSize already set");
143        }
144
145        this.writeSize = writeSize;
146    }
147
148    /** {@inheritDoc}
149     *
150     * @throws UnsupportedOperationException thrown if the write size
151     * is not yet known
152     */
153    @Override
154    public final int writeSize() {
155        if (writeSize < 0) {
156            throw new UnsupportedOperationException("writeSize is unknown");
157        }
158
159        return writeSize;
160    }
161
162    /** {@inheritDoc} */
163    @Override
164    public final void writeTo(DexFile file, AnnotatedOutput out) {
165        out.alignTo(alignment);
166
167        try {
168            if (writeSize < 0) {
169                throw new UnsupportedOperationException(
170                        "writeSize is unknown");
171            }
172            out.assertCursor(getAbsoluteOffset());
173        } catch (RuntimeException ex) {
174            throw ExceptionWithContext.withContext(ex,
175                    "...while writing " + this);
176        }
177
178        writeTo0(file, out);
179    }
180
181    /**
182     * Gets the relative item offset. The offset is from the start of
183     * the section which the instance was written to.
184     *
185     * @return &gt;= 0; the offset
186     * @throws RuntimeException thrown if the offset is not yet known
187     */
188    public final int getRelativeOffset() {
189        if (offset < 0) {
190            throw new RuntimeException("offset not yet known");
191        }
192
193        return offset;
194    }
195
196    /**
197     * Gets the absolute item offset. The offset is from the start of
198     * the file which the instance was written to.
199     *
200     * @return &gt;= 0; the offset
201     * @throws RuntimeException thrown if the offset is not yet known
202     */
203    public final int getAbsoluteOffset() {
204        if (offset < 0) {
205            throw new RuntimeException("offset not yet known");
206        }
207
208        return addedTo.getAbsoluteOffset(offset);
209    }
210
211    /**
212     * Indicates that this item has been added to the given section at
213     * the given offset. It is only valid to call this method once per
214     * instance.
215     *
216     * @param addedTo non-null; the section this instance has been added to
217     * @param offset &gt;= 0; the desired offset from the start of the
218     * section where this instance was placed
219     * @return &gt;= 0; the offset that this instance should be placed at
220     * in order to meet its alignment constraint
221     */
222    public final int place(Section addedTo, int offset) {
223        if (addedTo == null) {
224            throw new NullPointerException("addedTo == null");
225        }
226
227        if (offset < 0) {
228            throw new IllegalArgumentException("offset < 0");
229        }
230
231        if (this.addedTo != null) {
232            throw new RuntimeException("already written");
233        }
234
235        int mask = alignment - 1;
236        offset = (offset + mask) & ~mask;
237
238        this.addedTo = addedTo;
239        this.offset = offset;
240
241        place0(addedTo, offset);
242
243        return offset;
244    }
245
246    /**
247     * Gets the alignment requirement of this instance. An instance should
248     * only be written when so aligned.
249     *
250     * @return &gt; 0; the alignment requirement; must be a power of 2
251     */
252    public final int getAlignment() {
253        return alignment;
254    }
255
256    /**
257     * Gets the absolute offset of this item as a string, suitable for
258     * including in annotations.
259     *
260     * @return non-null; the offset string
261     */
262    public final String offsetString() {
263        return '[' + Integer.toHexString(getAbsoluteOffset()) + ']';
264    }
265
266    /**
267     * Gets a short human-readable string representing this instance.
268     *
269     * @return non-null; the human form
270     */
271    public abstract String toHuman();
272
273    /**
274     * Compares this instance to another which is guaranteed to be of
275     * the same class. The default implementation of this method is to
276     * throw an exception (unsupported operation). If a particular
277     * class needs to actually sort, then it should override this
278     * method.
279     *
280     * @param other non-null; instance to compare to
281     * @return <code>-1</code>, <code>0</code>, or <code>1</code>, depending
282     * on the sort order of this instance and the other
283     */
284    protected int compareTo0(OffsettedItem other) {
285        throw new UnsupportedOperationException("unsupported");
286    }
287
288    /**
289     * Does additional work required when placing an instance. The
290     * default implementation of this method is a no-op. If a
291     * particular class needs to do something special, then it should
292     * override this method. In particular, if this instance did not
293     * know its write size up-front, then this method is responsible
294     * for setting it.
295     *
296     * @param addedTo non-null; the section this instance has been added to
297     * @param offset &gt;= 0; the offset from the start of the
298     * section where this instance was placed
299     */
300    protected void place0(Section addedTo, int offset) {
301        // This space intentionally left blank.
302    }
303
304    /**
305     * Performs the actual write of the contents of this instance to
306     * the given data section. This is called by {@link #writeTo},
307     * which will have taken care of ensuring alignment.
308     *
309     * @param file non-null; the file to use for reference
310     * @param out non-null; where to write to
311     */
312    protected abstract void writeTo0(DexFile file, AnnotatedOutput out);
313}
314