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.dexgen.dex.file;
18
19import com.android.dexgen.rop.annotation.Annotation;
20import com.android.dexgen.rop.annotation.Annotations;
21import com.android.dexgen.util.AnnotatedOutput;
22import com.android.dexgen.util.Hex;
23
24/**
25 * Set of annotations, where no annotation type appears more than once.
26 */
27public final class AnnotationSetItem extends OffsettedItem {
28    /** the required alignment for instances of this class */
29    private static final int ALIGNMENT = 4;
30
31    /** the size of an entry int the set: one {@code uint} */
32    private static final int ENTRY_WRITE_SIZE = 4;
33
34    /** {@code non-null;} the set of annotations */
35    private final Annotations annotations;
36
37    /**
38     * {@code non-null;} set of annotations as individual items in an array.
39     * <b>Note:</b> The contents have to get sorted by type id before
40     * writing.
41     */
42    private final AnnotationItem[] items;
43
44    /**
45     * Constructs an instance.
46     *
47     * @param annotations {@code non-null;} set of annotations
48     */
49    public AnnotationSetItem(Annotations annotations) {
50        super(ALIGNMENT, writeSize(annotations));
51
52        this.annotations = annotations;
53        this.items = new AnnotationItem[annotations.size()];
54
55        int at = 0;
56        for (Annotation a : annotations.getAnnotations()) {
57            items[at] = new AnnotationItem(a);
58            at++;
59        }
60    }
61
62    /**
63     * Gets the write size for the given set.
64     *
65     * @param annotations {@code non-null;} the set
66     * @return {@code > 0;} the write size
67     */
68    private static int writeSize(Annotations annotations) {
69        // This includes an int size at the start of the list.
70
71        try {
72            return (annotations.size() * ENTRY_WRITE_SIZE) + 4;
73        } catch (NullPointerException ex) {
74            // Elucidate the exception.
75            throw new NullPointerException("list == null");
76        }
77    }
78
79    /**
80     * Gets the underlying annotations of this instance
81     *
82     * @return {@code non-null;} the annotations
83     */
84    public Annotations getAnnotations() {
85        return annotations;
86    }
87
88    /** {@inheritDoc} */
89    @Override
90    public int hashCode() {
91        return annotations.hashCode();
92    }
93
94    /** {@inheritDoc} */
95    @Override
96    protected int compareTo0(OffsettedItem other) {
97        AnnotationSetItem otherSet = (AnnotationSetItem) other;
98
99        return annotations.compareTo(otherSet.annotations);
100    }
101
102    /** {@inheritDoc} */
103    @Override
104    public ItemType itemType() {
105        return ItemType.TYPE_ANNOTATION_SET_ITEM;
106    }
107
108    /** {@inheritDoc} */
109    @Override
110    public String toHuman() {
111        return annotations.toString();
112    }
113
114    /** {@inheritDoc} */
115    public void addContents(DexFile file) {
116        MixedItemSection byteData = file.getByteData();
117        int size = items.length;
118
119        for (int i = 0; i < size; i++) {
120            items[i] = byteData.intern(items[i]);
121        }
122    }
123
124    /** {@inheritDoc} */
125    @Override
126    protected void place0(Section addedTo, int offset) {
127        // Sort the array to be in type id index order.
128        AnnotationItem.sortByTypeIdIndex(items);
129    }
130
131    /** {@inheritDoc} */
132    @Override
133    protected void writeTo0(DexFile file, AnnotatedOutput out) {
134        boolean annotates = out.annotates();
135        int size = items.length;
136
137        if (annotates) {
138            out.annotate(0, offsetString() + " annotation set");
139            out.annotate(4, "  size: " + Hex.u4(size));
140        }
141
142        out.writeInt(size);
143
144        for (int i = 0; i < size; i++) {
145            AnnotationItem item = items[i];
146            int offset = item.getAbsoluteOffset();
147
148            if (annotates) {
149                out.annotate(4, "  entries[" + Integer.toHexString(i) + "]: " +
150                        Hex.u4(offset));
151                items[i].annotateTo(out, "    ");
152            }
153
154            out.writeInt(offset);
155        }
156    }
157}
158