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 
17 package com.android.dexgen.rop.annotation;
18 
19 import com.android.dexgen.rop.cst.CstType;
20 import com.android.dexgen.util.MutabilityControl;
21 
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.Iterator;
25 import java.util.TreeMap;
26 
27 /**
28  * List of {@link Annotation} instances.
29  */
30 public 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 {
EMPTY.setImmutable()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      */
combine(Annotations a1, Annotations a2)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      */
combine(Annotations annotations, Annotation annotation)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      */
Annotations()86     public Annotations() {
87         annotations = new TreeMap<CstType, Annotation>();
88     }
89 
90     /** {@inheritDoc} */
91     @Override
hashCode()92     public int hashCode() {
93         return annotations.hashCode();
94     }
95 
96     /** {@inheritDoc} */
97     @Override
equals(Object other)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} */
compareTo(Annotations other)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} */
toString()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      */
size()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      */
add(Annotation annotation)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      */
addAll(Annotations toAdd)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      */
getAnnotations()210     public Collection<Annotation> getAnnotations() {
211         return Collections.unmodifiableCollection(annotations.values());
212     }
213 }
214