MapItem.java revision 579d7739c53a2707ad711a2d2cae46d7d782f061
1/*
2 * Copyright (C) 2008 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.ArrayList;
23
24/**
25 * Class that represents a map item.
26 */
27public final class MapItem extends OffsettedItem {
28    /** file alignment of this class, in bytes */
29    private static final int ALIGNMENT = 4;
30
31    /** write size of this class, in bytes: three {@code uint}s */
32    private static final int WRITE_SIZE = (4 * 3);
33
34    /** {@code non-null;} item type this instance covers */
35    private final ItemType type;
36
37    /** {@code non-null;} section this instance covers */
38    private final Section section;
39
40    /**
41     * {@code null-ok;} first item covered or {@code null} if this is
42     * a self-reference
43     */
44    private final Item firstItem;
45
46    /**
47     * {@code null-ok;} last item covered or {@code null} if this is
48     * a self-reference
49     */
50    private final Item lastItem;
51
52    /**
53     * {@code > 0;} count of items covered; {@code 1} if this
54     * is a self-reference
55     */
56    private final int itemCount;
57
58    /**
59     * Constructs a list item with instances of this class representing
60     * the contents of the given array of sections, adding it to the
61     * given map section.
62     *
63     * @param sections {@code non-null;} the sections
64     * @param mapSection {@code non-null;} the section that the resulting map
65     * should be added to; it should be empty on entry to this method
66     */
67    public static void addMap(Section[] sections,
68            MixedItemSection mapSection) {
69        if (sections == null) {
70            throw new NullPointerException("sections == null");
71        }
72
73        if (mapSection.items().size() != 0) {
74            throw new IllegalArgumentException(
75                    "mapSection.items().size() != 0");
76        }
77
78        ArrayList<MapItem> items = new ArrayList<MapItem>(50);
79
80        for (Section section : sections) {
81            ItemType currentType = null;
82            Item firstItem = null;
83            Item lastItem = null;
84            int count = 0;
85
86            for (Item item : section.items()) {
87                ItemType type = item.itemType();
88                if (type != currentType) {
89                    if (count != 0) {
90                        items.add(new MapItem(currentType, section,
91                                        firstItem, lastItem, count));
92                    }
93                    currentType = type;
94                    firstItem = item;
95                    count = 0;
96                }
97                lastItem = item;
98                count++;
99            }
100
101            if (count != 0) {
102                // Add a MapItem for the final items in the section.
103                items.add(new MapItem(currentType, section,
104                                firstItem, lastItem, count));
105            } else if (section == mapSection) {
106                // Add a MapItem for the self-referential section.
107                items.add(new MapItem(mapSection));
108            }
109        }
110
111        mapSection.add(
112                new UniformListItem<MapItem>(ItemType.TYPE_MAP_LIST, items));
113    }
114
115    /**
116     * Constructs an instance.
117     *
118     * @param type {@code non-null;} item type this instance covers
119     * @param section {@code non-null;} section this instance covers
120     * @param firstItem {@code non-null;} first item covered
121     * @param lastItem {@code non-null;} last item covered
122     * @param itemCount {@code > 0;} count of items covered
123     */
124    private MapItem(ItemType type, Section section, Item firstItem,
125            Item lastItem, int itemCount) {
126        super(ALIGNMENT, WRITE_SIZE);
127
128        if (type == null) {
129            throw new NullPointerException("type == null");
130        }
131
132        if (section == null) {
133            throw new NullPointerException("section == null");
134        }
135
136        if (firstItem == null) {
137            throw new NullPointerException("firstItem == null");
138        }
139
140        if (lastItem == null) {
141            throw new NullPointerException("lastItem == null");
142        }
143
144        if (itemCount <= 0) {
145            throw new IllegalArgumentException("itemCount <= 0");
146        }
147
148        this.type = type;
149        this.section = section;
150        this.firstItem = firstItem;
151        this.lastItem = lastItem;
152        this.itemCount = itemCount;
153    }
154
155    /**
156     * Constructs a self-referential instance. This instance is meant to
157     * represent the section containing the {@code map_list}.
158     *
159     * @param section {@code non-null;} section this instance covers
160     */
161    private MapItem(Section section) {
162        super(ALIGNMENT, WRITE_SIZE);
163
164        if (section == null) {
165            throw new NullPointerException("section == null");
166        }
167
168        this.type = ItemType.TYPE_MAP_LIST;
169        this.section = section;
170        this.firstItem = null;
171        this.lastItem = null;
172        this.itemCount = 1;
173    }
174
175    /** {@inheritDoc} */
176    @Override
177    public ItemType itemType() {
178        return ItemType.TYPE_MAP_ITEM;
179    }
180
181    /** {@inheritDoc} */
182    @Override
183    public String toString() {
184        StringBuffer sb = new StringBuffer(100);
185
186        sb.append(getClass().getName());
187        sb.append('{');
188        sb.append(section.toString());
189        sb.append(' ');
190        sb.append(type.toHuman());
191        sb.append('}');
192
193        return sb.toString();
194    }
195
196    /** {@inheritDoc} */
197    @Override
198    public void addContents(DexFile file) {
199        // We have nothing to add.
200    }
201
202    /** {@inheritDoc} */
203    @Override
204    public final String toHuman() {
205        return toString();
206    }
207
208    /** {@inheritDoc} */
209    @Override
210    protected void writeTo0(DexFile file, AnnotatedOutput out) {
211        int value = type.getMapValue();
212        int offset;
213
214        if (firstItem == null) {
215            offset = section.getFileOffset();
216        } else {
217            offset = section.getAbsoluteItemOffset(firstItem);
218        }
219
220        if (out.annotates()) {
221            out.annotate(0, offsetString() + ' ' + type.getTypeName() +
222                    " map");
223            out.annotate(2, "  type:   " + Hex.u2(value) + " // " +
224                    type.toString());
225            out.annotate(2, "  unused: 0");
226            out.annotate(4, "  size:   " + Hex.u4(itemCount));
227            out.annotate(4, "  offset: " + Hex.u4(offset));
228        }
229
230        out.writeShort(value);
231        out.writeShort(0); // unused
232        out.writeInt(itemCount);
233        out.writeInt(offset);
234    }
235}
236