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.dx.rop.annotation;
18 
19 import com.android.dx.rop.cst.CstString;
20 import com.android.dx.rop.cst.CstType;
21 import com.android.dx.util.MutabilityControl;
22 import com.android.dx.util.ToHuman;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Iterator;
26 import java.util.TreeMap;
27 
28 /**
29  * An annotation on an element of a class. Annotations have an
30  * associated type and additionally consist of a set of (name, value)
31  * pairs, where the names are unique.
32  */
33 public final class Annotation extends MutabilityControl
34         implements Comparable<Annotation>, ToHuman {
35     /** {@code non-null;} type of the annotation */
36     private final CstType type;
37 
38     /** {@code non-null;} the visibility of the annotation */
39     private final AnnotationVisibility visibility;
40 
41     /** {@code non-null;} map from names to {@link NameValuePair} instances */
42     private final TreeMap<CstString, NameValuePair> elements;
43 
44     /**
45      * Construct an instance. It initially contains no elements.
46      *
47      * @param type {@code non-null;} type of the annotation
48      * @param visibility {@code non-null;} the visibility of the annotation
49      */
Annotation(CstType type, AnnotationVisibility visibility)50     public Annotation(CstType type, AnnotationVisibility visibility) {
51         if (type == null) {
52             throw new NullPointerException("type == null");
53         }
54 
55         if (visibility == null) {
56             throw new NullPointerException("visibility == null");
57         }
58 
59         this.type = type;
60         this.visibility = visibility;
61         this.elements = new TreeMap<CstString, NameValuePair>();
62     }
63 
64     /** {@inheritDoc} */
65     @Override
equals(Object other)66     public boolean equals(Object other) {
67         if (! (other instanceof Annotation)) {
68             return false;
69         }
70 
71         Annotation otherAnnotation = (Annotation) other;
72 
73         if (! (type.equals(otherAnnotation.type)
74                         && (visibility == otherAnnotation.visibility))) {
75             return false;
76         }
77 
78         return elements.equals(otherAnnotation.elements);
79     }
80 
81     /** {@inheritDoc} */
82     @Override
hashCode()83     public int hashCode() {
84         int hash = type.hashCode();
85         hash = (hash * 31) + elements.hashCode();
86         hash = (hash * 31) + visibility.hashCode();
87         return hash;
88     }
89 
90     /** {@inheritDoc} */
91     @Override
compareTo(Annotation other)92     public int compareTo(Annotation other) {
93         int result = type.compareTo(other.type);
94 
95         if (result != 0) {
96             return result;
97         }
98 
99         result = visibility.compareTo(other.visibility);
100 
101         if (result != 0) {
102             return result;
103         }
104 
105         Iterator<NameValuePair> thisIter = elements.values().iterator();
106         Iterator<NameValuePair> otherIter = other.elements.values().iterator();
107 
108         while (thisIter.hasNext() && otherIter.hasNext()) {
109             NameValuePair thisOne = thisIter.next();
110             NameValuePair otherOne = otherIter.next();
111 
112             result = thisOne.compareTo(otherOne);
113             if (result != 0) {
114                 return result;
115             }
116         }
117 
118         if (thisIter.hasNext()) {
119             return 1;
120         } else if (otherIter.hasNext()) {
121             return -1;
122         }
123 
124         return 0;
125     }
126 
127     /** {@inheritDoc} */
128     @Override
toString()129     public String toString() {
130         return toHuman();
131     }
132 
133     /** {@inheritDoc} */
134     @Override
toHuman()135     public String toHuman() {
136         StringBuilder sb = new StringBuilder();
137 
138         sb.append(visibility.toHuman());
139         sb.append("-annotation ");
140         sb.append(type.toHuman());
141         sb.append(" {");
142 
143         boolean first = true;
144         for (NameValuePair pair : elements.values()) {
145             if (first) {
146                 first = false;
147             } else {
148                 sb.append(", ");
149             }
150             sb.append(pair.getName().toHuman());
151             sb.append(": ");
152             sb.append(pair.getValue().toHuman());
153         }
154 
155         sb.append("}");
156         return sb.toString();
157     }
158 
159     /**
160      * Gets the type of this instance.
161      *
162      * @return {@code non-null;} the type
163      */
getType()164     public CstType getType() {
165         return type;
166     }
167 
168     /**
169      * Gets the visibility of this instance.
170      *
171      * @return {@code non-null;} the visibility
172      */
getVisibility()173     public AnnotationVisibility getVisibility() {
174         return visibility;
175     }
176 
177     /**
178      * Put an element into the set of (name, value) pairs for this instance.
179      * If there is a preexisting element with the same name, it will be
180      * replaced by this method.
181      *
182      * @param pair {@code non-null;} the (name, value) pair to place into this instance
183      */
put(NameValuePair pair)184     public void put(NameValuePair pair) {
185         throwIfImmutable();
186 
187         if (pair == null) {
188             throw new NullPointerException("pair == null");
189         }
190 
191         elements.put(pair.getName(), pair);
192     }
193 
194     /**
195      * Add an element to the set of (name, value) pairs for this instance.
196      * It is an error to call this method if there is a preexisting element
197      * with the same name.
198      *
199      * @param pair {@code non-null;} the (name, value) pair to add to this instance
200      */
add(NameValuePair pair)201     public void add(NameValuePair pair) {
202         throwIfImmutable();
203 
204         if (pair == null) {
205             throw new NullPointerException("pair == null");
206         }
207 
208         CstString name = pair.getName();
209 
210         if (elements.get(name) != null) {
211             throw new IllegalArgumentException("name already added: " + name);
212         }
213 
214         elements.put(name, pair);
215     }
216 
217     /**
218      * Gets the set of name-value pairs contained in this instance. The
219      * result is always unmodifiable.
220      *
221      * @return {@code non-null;} the set of name-value pairs
222      */
getNameValuePairs()223     public Collection<NameValuePair> getNameValuePairs() {
224         return Collections.unmodifiableCollection(elements.values());
225     }
226 }
227