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