1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2010 Ben Gruver (JesusFreke)
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.AnnotatedOutput;
32import org.jf.dexlib.Util.Input;
33
34import java.util.Arrays;
35import java.util.Comparator;
36import java.util.List;
37
38public class AnnotationSetItem extends Item<AnnotationSetItem> {
39    private int hashCode = 0;
40
41    private AnnotationItem[] annotations;
42
43    /**
44     * Creates a new uninitialized <code>AnnotationSetItem</code>
45     * @param dexFile The <code>DexFile</code> that this item belongs to
46     */
47    protected AnnotationSetItem(DexFile dexFile) {
48        super(dexFile);
49    }
50
51    /**
52     * Creates a new <code>AnnotationSetItem</code> for the given annotations
53     * @param dexFile The <code>DexFile</code> that this item belongs to
54     * @param annotations The annotations for this <code>AnnotationSetItem</code>
55     */
56    private AnnotationSetItem(DexFile dexFile, AnnotationItem[] annotations) {
57        super(dexFile);
58        this.annotations = annotations;
59    }
60
61    /**
62     * Returns an <code>AnnotationSetItem</code> for the given annotations, and that has been interned into the given
63     * <code>DexFile</code>
64     * @param dexFile The <code>DexFile</code> that this item belongs to
65     * @param annotations The annotations for this <code>AnnotationSetItem</code>
66     * @return an <code>AnnotationSetItem</code> for the given annotations
67     */
68    public static AnnotationSetItem internAnnotationSetItem(DexFile dexFile, List<AnnotationItem> annotations) {
69        AnnotationSetItem annotationSetItem;
70        if (annotations == null) {
71            annotationSetItem = new AnnotationSetItem(dexFile, new AnnotationItem[0]);
72        } else {
73            AnnotationItem[] annotationsArray = new AnnotationItem[annotations.size()];
74            annotations.toArray(annotationsArray);
75            annotationSetItem = new AnnotationSetItem(dexFile, annotationsArray);
76        }
77        return dexFile.AnnotationSetsSection.intern(annotationSetItem);
78    }
79
80    /** {@inheritDoc} */
81    protected void readItem(Input in, ReadContext readContext) {
82        annotations = new AnnotationItem[in.readInt()];
83
84        for (int i=0; i<annotations.length; i++) {
85            annotations[i] = (AnnotationItem)readContext.getOffsettedItemByOffset(ItemType.TYPE_ANNOTATION_ITEM,
86                    in.readInt());
87        }
88    }
89
90    /** {@inheritDoc} */
91    protected int placeItem(int offset) {
92        return offset + 4 + annotations.length * 4;
93    }
94
95    /** {@inheritDoc} */
96    protected void writeItem(AnnotatedOutput out) {
97        Arrays.sort(annotations, new Comparator<AnnotationItem>() {
98            public int compare(AnnotationItem annotationItem, AnnotationItem annotationItem2) {
99                int annotationItemIndex = annotationItem.getEncodedAnnotation().annotationType.getIndex();
100                int annotationItemIndex2 = annotationItem2.getEncodedAnnotation().annotationType.getIndex();
101                if (annotationItemIndex < annotationItemIndex2) {
102                    return -1;
103                } else if (annotationItemIndex == annotationItemIndex2) {
104                    return 0;
105                }
106                return 1;
107            }
108        });
109
110
111        if (out.annotates()) {
112            out.annotate(4, "size: 0x" + Integer.toHexString(annotations.length) + " (" + annotations.length + ")");
113            for (AnnotationItem annotationItem: annotations) {
114                out.annotate(4, "annotation_off: 0x" + Integer.toHexString(annotationItem.getOffset()) + " - " +
115                        annotationItem.getEncodedAnnotation().annotationType.getTypeDescriptor());
116            }
117        }
118        out.writeInt(annotations.length);
119        for (AnnotationItem annotationItem: annotations) {
120            out.writeInt(annotationItem.getOffset());
121        }
122    }
123
124    /** {@inheritDoc} */
125    public ItemType getItemType() {
126        return ItemType.TYPE_ANNOTATION_SET_ITEM;
127    }
128
129    /** {@inheritDoc} */
130    public String getConciseIdentity() {
131        return "annotation_set_item @0x" + Integer.toHexString(getOffset());
132    }
133
134/** {@inheritDoc} */
135    public int compareTo(AnnotationSetItem o) {
136        if (o == null) {
137            return 1;
138        }
139
140        int comp = annotations.length - o.annotations.length;
141        if (comp == 0) {
142            for (int i=0; i<annotations.length; i++) {
143                comp = annotations[i].compareTo(o.annotations[i]);
144                if (comp != 0) {
145                    return comp;
146                }
147            }
148        }
149        return comp;
150    }
151
152    /**
153     * @return An array of the <code>AnnotationItem</code> objects in this <code>AnnotationSetItem</code>
154     */
155    public AnnotationItem[] getAnnotations() {
156        return annotations;
157    }
158
159    /**
160     * calculate and cache the hashcode
161     */
162    private void calcHashCode() {
163        hashCode = 0;
164        for (AnnotationItem annotationItem: annotations) {
165            hashCode = hashCode * 31 + annotationItem.hashCode();
166        }
167    }
168
169    @Override
170    public int hashCode() {
171        //there's a small possibility that the actual hash code will be 0. If so, we'll
172        //just end up recalculating it each time
173        if (hashCode == 0)
174            calcHashCode();
175        return hashCode;
176    }
177
178    @Override
179    public boolean equals(Object o) {
180        if (this==o) {
181            return true;
182        }
183        if (o==null || !this.getClass().equals(o.getClass())) {
184            return false;
185        }
186
187        AnnotationSetItem other = (AnnotationSetItem)o;
188        return (this.compareTo(other) == 0);
189    }
190}
191