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