summaryrefslogtreecommitdiff
path: root/dx/src/com/android/dx/dex/file/UniformListItem.java
blob: 88919c7cb1665e9f58e177cab81b1768d8bf9ed0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.dx.dex.file;

import com.android.dx.util.AnnotatedOutput;
import com.android.dx.util.Hex;

import java.util.HashMap;
import java.util.List;

/**
 * Class that represents a contiguous list of uniform items. Each
 * item in the list, in particular, must have the same write size and
 * alignment.
 *
 * <p>This class inherits its alignment from its items, bumped up to
 * {@code 4} if the items have a looser alignment requirement. If
 * it is more than {@code 4}, then there will be a gap after the
 * output list size (which is four bytes) and before the first item.</p>
 *
 * @param <T> type of element contained in an instance
 */
public final class UniformListItem<T extends OffsettedItem>
        extends OffsettedItem {
    /** the size of the list header */
    private static final int HEADER_SIZE = 4;

    /** {@code non-null;} the item type */
    private final ItemType itemType;

    /** {@code non-null;} the contents */
    private final List<T> items;

    /**
     * Constructs an instance. It is illegal to modify the given list once
     * it is used to construct an instance of this class.
     *
     * @param itemType {@code non-null;} the type of the item
     * @param items {@code non-null and non-empty;} list of items to represent
     */
    public UniformListItem(ItemType itemType, List<T> items) {
        super(getAlignment(items), writeSize(items));

        if (itemType == null) {
            throw new NullPointerException("itemType == null");
        }

        this.items = items;
        this.itemType = itemType;
    }

    /**
     * Helper for {@link #UniformListItem}, which returns the alignment
     * requirement implied by the given list. See the header comment for
     * more details.
     *
     * @param items {@code non-null;} list of items being represented
     * @return {@code >= 4;} the alignment requirement
     */
    private static int getAlignment(List<? extends OffsettedItem> items) {
        try {
            // Since they all must have the same alignment, any one will do.
            return Math.max(HEADER_SIZE, items.get(0).getAlignment());
        } catch (IndexOutOfBoundsException ex) {
            // Translate the exception.
            throw new IllegalArgumentException("items.size() == 0");
        } catch (NullPointerException ex) {
            // Translate the exception.
            throw new NullPointerException("items == null");
        }
    }

    /**
     * Calculates the write size for the given list.
     *
     * @param items {@code non-null;} the list in question
     * @return {@code >= 0;} the write size
     */
    private static int writeSize(List<? extends OffsettedItem> items) {
        /*
         * This class assumes all included items are the same size,
         * an assumption which is verified in place0().
         */
        OffsettedItem first = items.get(0);
        return (items.size() * first.writeSize()) + getAlignment(items);
    }

    /** {@inheritDoc} */
    @Override
    public ItemType itemType() {
        return itemType;
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer(100);

        sb.append(getClass().getName());
        sb.append(items);

        return sb.toString();
    }

    /** {@inheritDoc} */
    @Override
    public void addContents(DexFile file) {
        for (OffsettedItem i : items) {
            i.addContents(file);
        }
    }

    /** {@inheritDoc} */
    @Override
    public final String toHuman() {
        StringBuffer sb = new StringBuffer(100);
        boolean first = true;

        sb.append("{");

        for (OffsettedItem i : items) {
            if (first) {
                first = false;
            } else {
                sb.append(", ");
            }
            sb.append(i.toHuman());
        }

        sb.append("}");
        return sb.toString();
    }

    /**
     * Gets the underlying list of items.
     *
     * @return {@code non-null;} the list
     */
    public final List<T> getItems() {
        return items;
    }

    /** {@inheritDoc} */
    @Override
    protected void place0(Section addedTo, int offset) {
        offset += headerSize();

        boolean first = true;
        int theSize = -1;
        int theAlignment = -1;

        for (OffsettedItem i : items) {
            int size = i.writeSize();
            if (first) {
                theSize = size;
                theAlignment = i.getAlignment();
                first = false;
            } else {
                if (size != theSize) {
                    throw new UnsupportedOperationException(
                            "item size mismatch");
                }
                if (i.getAlignment() != theAlignment) {
                    throw new UnsupportedOperationException(
                            "item alignment mismatch");
                }
            }

            offset = i.place(addedTo, offset) + size;
        }
    }

    /** {@inheritDoc} */
    @Override
    protected void writeTo0(DexFile file, AnnotatedOutput out) {
        int size = items.size();

        if (out.annotates()) {
            out.annotate(0, offsetString() + " " + typeName());
            out.annotate(4, "  size: " + Hex.u4(size));
        }

        out.writeInt(size);

        for (OffsettedItem i : items) {
            i.writeTo(file, out);
        }
    }

    /**
     * Get the size of the header of this list.
     *
     * @return {@code >= 0;} the header size
     */
    private int headerSize() {
        /*
         * Because of how this instance was set up, this is the same
         * as the alignment.
         */
        return getAlignment();
    }
}