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.CstString; 20import com.android.dx.rop.cst.CstType; 21import com.android.dx.util.MutabilityControl; 22import com.android.dx.util.ToHuman; 23import java.util.Collection; 24import java.util.Collections; 25import java.util.Iterator; 26import 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 */ 33public 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 */ 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 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 public int hashCode() { 83 int hash = type.hashCode(); 84 hash = (hash * 31) + elements.hashCode(); 85 hash = (hash * 31) + visibility.hashCode(); 86 return hash; 87 } 88 89 /** {@inheritDoc} */ 90 public int compareTo(Annotation other) { 91 int result = type.compareTo(other.type); 92 93 if (result != 0) { 94 return result; 95 } 96 97 result = visibility.compareTo(other.visibility); 98 99 if (result != 0) { 100 return result; 101 } 102 103 Iterator<NameValuePair> thisIter = elements.values().iterator(); 104 Iterator<NameValuePair> otherIter = other.elements.values().iterator(); 105 106 while (thisIter.hasNext() && otherIter.hasNext()) { 107 NameValuePair thisOne = thisIter.next(); 108 NameValuePair otherOne = otherIter.next(); 109 110 result = thisOne.compareTo(otherOne); 111 if (result != 0) { 112 return result; 113 } 114 } 115 116 if (thisIter.hasNext()) { 117 return 1; 118 } else if (otherIter.hasNext()) { 119 return -1; 120 } 121 122 return 0; 123 } 124 125 /** {@inheritDoc} */ 126 @Override 127 public String toString() { 128 return toHuman(); 129 } 130 131 /** {@inheritDoc} */ 132 public String toHuman() { 133 StringBuilder sb = new StringBuilder(); 134 135 sb.append(visibility.toHuman()); 136 sb.append("-annotation "); 137 sb.append(type.toHuman()); 138 sb.append(" {"); 139 140 boolean first = true; 141 for (NameValuePair pair : elements.values()) { 142 if (first) { 143 first = false; 144 } else { 145 sb.append(", "); 146 } 147 sb.append(pair.getName().toHuman()); 148 sb.append(": "); 149 sb.append(pair.getValue().toHuman()); 150 } 151 152 sb.append("}"); 153 return sb.toString(); 154 } 155 156 /** 157 * Gets the type of this instance. 158 * 159 * @return {@code non-null;} the type 160 */ 161 public CstType getType() { 162 return type; 163 } 164 165 /** 166 * Gets the visibility of this instance. 167 * 168 * @return {@code non-null;} the visibility 169 */ 170 public AnnotationVisibility getVisibility() { 171 return visibility; 172 } 173 174 /** 175 * Put an element into the set of (name, value) pairs for this instance. 176 * If there is a preexisting element with the same name, it will be 177 * replaced by this method. 178 * 179 * @param pair {@code non-null;} the (name, value) pair to place into this instance 180 */ 181 public void put(NameValuePair pair) { 182 throwIfImmutable(); 183 184 if (pair == null) { 185 throw new NullPointerException("pair == null"); 186 } 187 188 elements.put(pair.getName(), pair); 189 } 190 191 /** 192 * Add an element to the set of (name, value) pairs for this instance. 193 * It is an error to call this method if there is a preexisting element 194 * with the same name. 195 * 196 * @param pair {@code non-null;} the (name, value) pair to add to this instance 197 */ 198 public void add(NameValuePair pair) { 199 throwIfImmutable(); 200 201 if (pair == null) { 202 throw new NullPointerException("pair == null"); 203 } 204 205 CstString name = pair.getName(); 206 207 if (elements.get(name) != null) { 208 throw new IllegalArgumentException("name already added: " + name); 209 } 210 211 elements.put(name, pair); 212 } 213 214 /** 215 * Gets the set of name-value pairs contained in this instance. The 216 * result is always unmodifiable. 217 * 218 * @return {@code non-null;} the set of name-value pairs 219 */ 220 public Collection<NameValuePair> getNameValuePairs() { 221 return Collections.unmodifiableCollection(elements.values()); 222 } 223} 224