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