TypeListItem.java revision 4d68e05fb5e3262c58bc9896befe910698daa6a8
1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2009 Ben Gruver
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib;
30
31import org.jf.dexlib.Util.Input;
32import org.jf.dexlib.Util.AnnotatedOutput;
33import org.jf.dexlib.Util.ReadOnlyArrayList;
34
35import java.util.List;
36
37public class TypeListItem extends Item<TypeListItem> {
38    private int hashCode = 0;
39
40    private TypeIdItem[] typeList;
41
42    /**
43     * Creates a new uninitialized <code>TypeListItem</code>
44     * @param dexFile The <code>DexFile</code> that this item belongs to
45     */
46    protected TypeListItem(DexFile dexFile) {
47        super(dexFile);
48    }
49
50    /**
51     * Creates a new <code>TypeListItem</code> for the given string
52     * @param dexFile The <code>DexFile</code> that this item belongs to
53     * @param typeList A list of the types that this <code>TypeListItem</code> represents
54     */
55    private TypeListItem(DexFile dexFile, List<TypeIdItem> typeList) {
56        super(dexFile);
57
58        this.typeList = new TypeIdItem[typeList.size()];
59        typeList.toArray(this.typeList);
60    }
61
62    /**
63     * Returns a <code>TypeListItem</code> for the given values, and that has been interned into
64     * the given <code>DexFile</code>
65     * @param dexFile The <code>DexFile</code> that this item belongs to
66     * @param typeList A list of the types that this <code>TypeListItem</code> represents
67     * @return a <code>TypeListItem</code> for the given values, and that has been interned into
68     * the given <code>DexFile</code>
69     */
70    public static TypeListItem getInternedTypeListItem(DexFile dexFile, List<TypeIdItem> typeList) {
71        TypeListItem typeListItem = new TypeListItem(dexFile, typeList);
72        return dexFile.TypeListsSection.intern(typeListItem);
73    }
74
75    /** {@inheritDoc} */
76    protected void readItem(Input in, ReadContext readContext) {
77        int size = in.readInt();
78        typeList = new TypeIdItem[size];
79        for (int i=0; i<size; i++) {
80            int typeIndex = in.readShort();
81            typeList[i] = dexFile.TypeIdsSection.getItemByIndex(typeIndex);
82        }
83    }
84
85    /** {@inheritDoc} */
86    protected int placeItem(int offset) {
87        return offset + 4 + typeList.length * 2;
88    }
89
90    /** {@inheritDoc} */
91    protected void writeItem(AnnotatedOutput out) {
92        //yes, the code to write the item is duplicated. This eliminates the need to iterate over the list twice
93
94        if (out.annotates()) {
95            out.annotate(4, "size: 0x" + Integer.toHexString(typeList.length) + " (" + typeList.length +")");
96
97            for (TypeIdItem typeIdItem: typeList) {
98                out.annotate(2, "type_id_item: " + typeIdItem.getTypeDescriptor());
99            }
100        }
101        out.writeInt(typeList.length);
102        for (TypeIdItem typeIdItem: typeList) {
103            out.writeShort(typeIdItem.getIndex());
104        }
105    }
106
107    /** {@inheritDoc} */
108    public ItemType getItemType() {
109        return ItemType.TYPE_TYPE_LIST;
110    }
111
112    /** {@inheritDoc} */
113    public String getConciseIdentity() {
114        return "type_list: " + getTypeListString("");
115    }
116
117    /** {@inheritDoc} */
118    public int compareTo(TypeListItem o) {
119        if (o == null) {
120            return 1;
121        }
122
123        int thisSize = typeList.length;
124        int otherSize = o.typeList.length;
125        int size = Math.min(thisSize, otherSize);
126
127        for (int i = 0; i < size; i++) {
128            int result = typeList[i].compareTo(o.typeList[i]);
129            if (result != 0) {
130                return result;
131            }
132        }
133
134        if (thisSize < otherSize) {
135            return -1;
136        } else if (thisSize > otherSize) {
137            return 1;
138        } else {
139            return 0;
140        }
141    }
142
143    /**
144     * @return the number of registers required for this <code>TypeListItem</code>
145     */
146    public int getRegisterCount() {
147        int wordCount = 0;
148        for (TypeIdItem typeIdItem: typeList) {
149            wordCount += typeIdItem.getRegisterCount();
150        }
151        return wordCount;
152    }
153
154    /**
155     * @return a string consisting of the type descriptors in this <code>TypeListItem</code>
156     * that are separated by the given separator
157     * @param separator the separator between each type
158     */
159    public String getTypeListString(String separator) {
160        int size = 0;
161        for (TypeIdItem typeIdItem: typeList) {
162            size += typeIdItem.getTypeDescriptor().length();
163            size += separator.length();
164        }
165
166        StringBuilder sb = new StringBuilder(size);
167        for (TypeIdItem typeIdItem: typeList) {
168            sb.append(typeIdItem.getTypeDescriptor());
169            sb.append(separator);
170        }
171        if (typeList.length > 0) {
172            sb.delete(sb.length() - separator.length(), sb.length());
173        }
174        return sb.toString();
175    }
176
177    /**
178     * @return a string consisting of the shorty form of the type descriptors in this
179     * <code>TypeListItem</code> that are directly concatenated together
180     */
181    public String getShortyString() {
182        StringBuilder sb = new StringBuilder();
183        for (TypeIdItem typeIdItem: typeList) {
184            sb.append(typeIdItem.toShorty());
185        }
186        return sb.toString();
187    }
188
189    /**
190     * @param index the index of the <code>TypeIdItem</code> to get
191     * @return the <code>TypeIdItem</code> at the given index
192     */
193    public TypeIdItem getTypeIdItem(int index) {
194        return typeList[index];
195    }
196
197    /**
198     * @return the number of types in this <code>TypeListItem</code>
199     */
200    public int getTypeCount() {
201        return typeList.length;
202    }
203
204    /**
205     * @return an array of the <code>TypeIdItems</code> in this <code>TypeListItem</code>
206     */
207    public List<TypeIdItem> getTypes() {
208        return new ReadOnlyArrayList<TypeIdItem>(typeList);
209    }
210
211    /**
212     * calculate and cache the hashcode
213     */
214    private void calcHashCode() {
215        int hashCode = 1;
216
217        for (TypeIdItem typeIdItem: typeList) {
218            hashCode = 31 * hashCode + typeIdItem.hashCode();
219        }
220        this.hashCode = hashCode;
221    }
222
223    @Override
224    public int hashCode() {
225        //there's a small possibility that the actual hash code will be 0. If so, we'll
226        //just end up recalculating it each time
227        if (hashCode == 0)
228            calcHashCode();
229        return hashCode;
230    }
231
232    @Override
233    public boolean equals(Object o) {
234        if (this==o) {
235            return true;
236        }
237        if (o==null || !this.getClass().equals(o.getClass())) {
238            return false;
239        }
240
241        //This assumes that the referenced items have been interned in both objects.
242        //This is a valid assumption because all outside code must use the static
243        //"getInterned..." style methods to make new items, and any item created
244        //internally is guaranteed to be interned
245        TypeListItem other = (TypeListItem)o;
246        if (typeList.length != other.typeList.length) {
247            return false;
248        }
249
250        for (int i=0; i<typeList.length; i++) {
251            if (typeList[i] != other.typeList[i]) {
252                return false;
253            }
254        }
255        return true;
256    }
257}
258