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.Hex;
21579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
22579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.HashMap;
23579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonimport java.util.List;
24579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
25579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson/**
26579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * Class that represents a contiguous list of uniform items. Each
27579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * item in the list, in particular, must have the same write size and
28579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * alignment.
29579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
30579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * <p>This class inherits its alignment from its items, bumped up to
31579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * {@code 4} if the items have a looser alignment requirement. If
32579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * it is more than {@code 4}, then there will be a gap after the
33579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * output list size (which is four bytes) and before the first item.</p>
34579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson *
35579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson * @param <T> type of element contained in an instance
36579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson */
37579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilsonpublic final class UniformListItem<T extends OffsettedItem>
38579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        extends OffsettedItem {
39579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** the size of the list header */
40579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static final int HEADER_SIZE = 4;
41579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
42579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@code non-null;} the item type */
43579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private final ItemType itemType;
44579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
45579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@code non-null;} the contents */
46579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private final List<T> items;
47579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
48579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
49579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Constructs an instance. It is illegal to modify the given list once
50579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * it is used to construct an instance of this class.
51579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
52579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param itemType {@code non-null;} the type of the item
53579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param items {@code non-null and non-empty;} list of items to represent
54579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
55579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public UniformListItem(ItemType itemType, List<T> items) {
56579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        super(getAlignment(items), writeSize(items));
57579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
58579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (itemType == null) {
59579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new NullPointerException("itemType == null");
60579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
61579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
62579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.items = items;
63579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        this.itemType = itemType;
64579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
65579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
66579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
67579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Helper for {@link #UniformListItem}, which returns the alignment
68579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * requirement implied by the given list. See the header comment for
69579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * more details.
70579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
71579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param items {@code non-null;} list of items being represented
72579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code >= 4;} the alignment requirement
73579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
74579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static int getAlignment(List<? extends OffsettedItem> items) {
75579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        try {
76579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Since they all must have the same alignment, any one will do.
77579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            return Math.max(HEADER_SIZE, items.get(0).getAlignment());
78579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (IndexOutOfBoundsException ex) {
79579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Translate the exception.
80579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new IllegalArgumentException("items.size() == 0");
81579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        } catch (NullPointerException ex) {
82579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            // Translate the exception.
83579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            throw new NullPointerException("items == null");
84579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
85579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
86579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
87579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
88579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Calculates the write size for the given list.
89579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
90579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @param items {@code non-null;} the list in question
91579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code >= 0;} the write size
92579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
93579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private static int writeSize(List<? extends OffsettedItem> items) {
94579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        /*
95579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * This class assumes all included items are the same size,
96579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * an assumption which is verified in place0().
97579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         */
98579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        OffsettedItem first = items.get(0);
99579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return (items.size() * first.writeSize()) + getAlignment(items);
100579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
101579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
102579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
103579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @Override
104579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public ItemType itemType() {
105579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return itemType;
106579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
107579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
108579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
109579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @Override
110579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public String toString() {
111579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        StringBuffer sb = new StringBuffer(100);
112579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
113579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        sb.append(getClass().getName());
114579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        sb.append(items);
115579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
116579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return sb.toString();
117579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
118579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
119579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
120579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @Override
121579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public void addContents(DexFile file) {
122579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (OffsettedItem i : items) {
123579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            i.addContents(file);
124579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
125579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
126579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
127579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
128579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @Override
129579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public final String toHuman() {
130579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        StringBuffer sb = new StringBuffer(100);
131579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        boolean first = true;
132579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
133579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        sb.append("{");
134579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
135579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (OffsettedItem i : items) {
136579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (first) {
137579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                first = false;
138579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            } else {
139579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                sb.append(", ");
140579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
141579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            sb.append(i.toHuman());
142579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
143579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
144579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        sb.append("}");
145579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return sb.toString();
146579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
147579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
148579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
149579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Gets the underlying list of items.
150579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
151579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code non-null;} the list
152579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
153579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    public final List<T> getItems() {
154579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return items;
155579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
156579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
157579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
158579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @Override
159579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    protected void place0(Section addedTo, int offset) {
160579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        offset += headerSize();
161579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
162579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        boolean first = true;
163579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int theSize = -1;
164579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int theAlignment = -1;
165579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
166579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (OffsettedItem i : items) {
167579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            int size = i.writeSize();
168579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            if (first) {
169579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                theSize = size;
170579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                theAlignment = i.getAlignment();
171579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                first = false;
172579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            } else {
173579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (size != theSize) {
174579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    throw new UnsupportedOperationException(
175579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                            "item size mismatch");
176579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
177579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                if (i.getAlignment() != theAlignment) {
178579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                    throw new UnsupportedOperationException(
179579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                            "item alignment mismatch");
180579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson                }
181579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            }
182579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
183579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            offset = i.place(addedTo, offset) + size;
184579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
185579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
186579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
187579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /** {@inheritDoc} */
188579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    @Override
189579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    protected void writeTo0(DexFile file, AnnotatedOutput out) {
190579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        int size = items.size();
191579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
192579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        if (out.annotates()) {
193579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            out.annotate(0, offsetString() + " " + typeName());
194579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            out.annotate(4, "  size: " + Hex.u4(size));
195579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
196579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
197579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        out.writeInt(size);
198579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
199579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        for (OffsettedItem i : items) {
200579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson            i.writeTo(file, out);
201579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        }
202579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
203579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson
204579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    /**
205579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * Get the size of the header of this list.
206579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     *
207579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     * @return {@code >= 0;} the header size
208579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson     */
209579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    private int headerSize() {
210579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        /*
211579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * Because of how this instance was set up, this is the same
212579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         * as the alignment.
213579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson         */
214579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson        return getAlignment();
215579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson    }
216579d7739c53a2707ad711a2d2cae46d7d782f06Jesse Wilson}
217