1/*
2 * Copyright (C) 2007 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.rop.annotation;
18
19import com.android.dx.rop.cst.CstType;
20import com.android.dx.util.MutabilityControl;
21
22import java.util.Collection;
23import java.util.Collections;
24import java.util.Iterator;
25import java.util.TreeMap;
26
27/**
28 * List of {@link Annotation} instances.
29 */
30public final class Annotations extends MutabilityControl
31        implements Comparable<Annotations> {
32    /** {@code non-null;} immutable empty instance */
33    public static final Annotations EMPTY = new Annotations();
34
35    static {
36        EMPTY.setImmutable();
37    }
38
39    /** {@code non-null;} map from types to annotations */
40    private final TreeMap<CstType, Annotation> annotations;
41
42    /**
43     * Constructs an immutable instance which is the combination of the
44     * two given instances. The two instances must contain disjoint sets
45     * of types.
46     *
47     * @param a1 {@code non-null;} an instance
48     * @param a2 {@code non-null;} the other instance
49     * @return {@code non-null;} the combination
50     * @throws IllegalArgumentException thrown if there is a duplicate type
51     */
52    public static Annotations combine(Annotations a1, Annotations a2) {
53        Annotations result = new Annotations();
54
55        result.addAll(a1);
56        result.addAll(a2);
57        result.setImmutable();
58
59        return result;
60    }
61
62    /**
63     * Constructs an immutable instance which is the combination of the
64     * given instance with the given additional annotation. The latter's
65     * type must not already appear in the former.
66     *
67     * @param annotations {@code non-null;} the instance to augment
68     * @param annotation {@code non-null;} the additional annotation
69     * @return {@code non-null;} the combination
70     * @throws IllegalArgumentException thrown if there is a duplicate type
71     */
72    public static Annotations combine(Annotations annotations,
73            Annotation annotation) {
74        Annotations result = new Annotations();
75
76        result.addAll(annotations);
77        result.add(annotation);
78        result.setImmutable();
79
80        return result;
81    }
82
83    /**
84     * Constructs an empty instance.
85     */
86    public Annotations() {
87        annotations = new TreeMap<CstType, Annotation>();
88    }
89
90    /** {@inheritDoc} */
91    @Override
92    public int hashCode() {
93        return annotations.hashCode();
94    }
95
96    /** {@inheritDoc} */
97    @Override
98    public boolean equals(Object other) {
99        if (! (other instanceof Annotations)) {
100            return false;
101        }
102
103        Annotations otherAnnotations = (Annotations) other;
104
105        return annotations.equals(otherAnnotations.annotations);
106    }
107
108    /** {@inheritDoc} */
109    public int compareTo(Annotations other) {
110        Iterator<Annotation> thisIter = annotations.values().iterator();
111        Iterator<Annotation> otherIter = other.annotations.values().iterator();
112
113        while (thisIter.hasNext() && otherIter.hasNext()) {
114            Annotation thisOne = thisIter.next();
115            Annotation otherOne = otherIter.next();
116
117            int result = thisOne.compareTo(otherOne);
118            if (result != 0) {
119                return result;
120            }
121        }
122
123        if (thisIter.hasNext()) {
124            return 1;
125        } else if (otherIter.hasNext()) {
126            return -1;
127        }
128
129        return 0;
130    }
131
132    /** {@inheritDoc} */
133    public String toString() {
134        StringBuilder sb = new StringBuilder();
135        boolean first = true;
136
137        sb.append("annotations{");
138
139        for (Annotation a : annotations.values()) {
140            if (first) {
141                first = false;
142            } else {
143                sb.append(", ");
144            }
145            sb.append(a.toHuman());
146        }
147
148        sb.append("}");
149        return sb.toString();
150    }
151
152    /**
153     * Gets the number of elements in this instance.
154     *
155     * @return {@code >= 0;} the size
156     */
157    public int size() {
158        return annotations.size();
159    }
160
161    /**
162     * Adds an element to this instance. There must not already be an
163     * element of the same type.
164     *
165     * @param annotation {@code non-null;} the element to add
166     * @throws IllegalArgumentException thrown if there is a duplicate type
167     */
168    public void add(Annotation annotation) {
169        throwIfImmutable();
170
171        if (annotation == null) {
172            throw new NullPointerException("annotation == null");
173        }
174
175        CstType type = annotation.getType();
176
177        if (annotations.containsKey(type)) {
178            throw new IllegalArgumentException("duplicate type: " +
179                    type.toHuman());
180        }
181
182        annotations.put(type, annotation);
183    }
184
185    /**
186     * Adds all of the elements of the given instance to this one. The
187     * instances must not have any duplicate types.
188     *
189     * @param toAdd {@code non-null;} the annotations to add
190     * @throws IllegalArgumentException thrown if there is a duplicate type
191     */
192    public void addAll(Annotations toAdd) {
193        throwIfImmutable();
194
195        if (toAdd == null) {
196            throw new NullPointerException("toAdd == null");
197        }
198
199        for (Annotation a : toAdd.annotations.values()) {
200            add(a);
201        }
202    }
203
204    /**
205     * Gets the set of annotations contained in this instance. The
206     * result is always unmodifiable.
207     *
208     * @return {@code non-null;} the set of annotations
209     */
210    public Collection<Annotation> getAnnotations() {
211        return Collections.unmodifiableCollection(annotations.values());
212    }
213}
214