UniformListItem.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
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.Hex;
21
22import java.util.HashMap;
23import java.util.List;
24
25/**
26 * Class that represents a contiguous list of uniform items. Each
27 * item in the list, in particular, must have the same write size and
28 * alignment.
29 *
30 * <p>This class inherits its alignment from its items, bumped up to
31 * {@code 4} if the items have a looser alignment requirement. If
32 * it is more than {@code 4}, then there will be a gap after the
33 * output list size (which is four bytes) and before the first item.</p>
34 *
35 * @param <T> type of element contained in an instance
36 */
37public final class UniformListItem<T extends OffsettedItem>
38        extends OffsettedItem {
39    /** the size of the list header */
40    private static final int HEADER_SIZE = 4;
41
42    /** {@code non-null;} the item type */
43    private final ItemType itemType;
44
45    /** {@code non-null;} the contents */
46    private final List<T> items;
47
48    /**
49     * Constructs an instance. It is illegal to modify the given list once
50     * it is used to construct an instance of this class.
51     *
52     * @param itemType {@code non-null;} the type of the item
53     * @param items {@code non-null and non-empty;} list of items to represent
54     */
55    public UniformListItem(ItemType itemType, List<T> items) {
56        super(getAlignment(items), writeSize(items));
57
58        if (itemType == null) {
59            throw new NullPointerException("itemType == null");
60        }
61
62        this.items = items;
63        this.itemType = itemType;
64    }
65
66    /**
67     * Helper for {@link #UniformListItem}, which returns the alignment
68     * requirement implied by the given list. See the header comment for
69     * more details.
70     *
71     * @param items {@code non-null;} list of items being represented
72     * @return {@code >= 4;} the alignment requirement
73     */
74    private static int getAlignment(List<? extends OffsettedItem> items) {
75        try {
76            // Since they all must have the same alignment, any one will do.
77            return Math.max(HEADER_SIZE, items.get(0).getAlignment());
78        } catch (IndexOutOfBoundsException ex) {
79            // Translate the exception.
80            throw new IllegalArgumentException("items.size() == 0");
81        } catch (NullPointerException ex) {
82            // Translate the exception.
83            throw new NullPointerException("items == null");
84        }
85    }
86
87    /**
88     * Calculates the write size for the given list.
89     *
90     * @param items {@code non-null;} the list in question
91     * @return {@code >= 0;} the write size
92     */
93    private static int writeSize(List<? extends OffsettedItem> items) {
94        /*
95         * This class assumes all included items are the same size,
96         * an assumption which is verified in place0().
97         */
98        OffsettedItem first = items.get(0);
99        return (items.size() * first.writeSize()) + getAlignment(items);
100    }
101
102    /** {@inheritDoc} */
103    @Override
104    public ItemType itemType() {
105        return itemType;
106    }
107
108    /** {@inheritDoc} */
109    @Override
110    public String toString() {
111        StringBuffer sb = new StringBuffer(100);
112
113        sb.append(getClass().getName());
114        sb.append(items);
115
116        return sb.toString();
117    }
118
119    /** {@inheritDoc} */
120    @Override
121    public void addContents(DexFile file) {
122        for (OffsettedItem i : items) {
123            i.addContents(file);
124        }
125    }
126
127    /** {@inheritDoc} */
128    @Override
129    public final String toHuman() {
130        StringBuffer sb = new StringBuffer(100);
131        boolean first = true;
132
133        sb.append("{");
134
135        for (OffsettedItem i : items) {
136            if (first) {
137                first = false;
138            } else {
139                sb.append(", ");
140            }
141            sb.append(i.toHuman());
142        }
143
144        sb.append("}");
145        return sb.toString();
146    }
147
148    /**
149     * Gets the underlying list of items.
150     *
151     * @return {@code non-null;} the list
152     */
153    public final List<T> getItems() {
154        return items;
155    }
156
157    /** {@inheritDoc} */
158    @Override
159    protected void place0(Section addedTo, int offset) {
160        offset += headerSize();
161
162        boolean first = true;
163        int theSize = -1;
164        int theAlignment = -1;
165
166        for (OffsettedItem i : items) {
167            int size = i.writeSize();
168            if (first) {
169                theSize = size;
170                theAlignment = i.getAlignment();
171                first = false;
172            } else {
173                if (size != theSize) {
174                    throw new UnsupportedOperationException(
175                            "item size mismatch");
176                }
177                if (i.getAlignment() != theAlignment) {
178                    throw new UnsupportedOperationException(
179                            "item alignment mismatch");
180                }
181            }
182
183            offset = i.place(addedTo, offset) + size;
184        }
185    }
186
187    /** {@inheritDoc} */
188    @Override
189    protected void writeTo0(DexFile file, AnnotatedOutput out) {
190        int size = items.size();
191
192        if (out.annotates()) {
193            out.annotate(0, offsetString() + " " + typeName());
194            out.annotate(4, "  size: " + Hex.u4(size));
195        }
196
197        out.writeInt(size);
198
199        for (OffsettedItem i : items) {
200            i.writeTo(file, out);
201        }
202    }
203
204    /**
205     * Get the size of the header of this list.
206     *
207     * @return {@code >= 0;} the header size
208     */
209    private int headerSize() {
210        /*
211         * Because of how this instance was set up, this is the same
212         * as the alignment.
213         */
214        return getAlignment();
215    }
216}
217