1 /* 2 * Copyright (C) 2008 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.dex.file; 18 19 import com.android.dexgen.rop.annotation.Annotations; 20 import com.android.dexgen.rop.annotation.AnnotationsList; 21 import com.android.dexgen.rop.cst.CstFieldRef; 22 import com.android.dexgen.rop.cst.CstMethodRef; 23 import com.android.dexgen.util.AnnotatedOutput; 24 import com.android.dexgen.util.Hex; 25 26 import java.io.PrintWriter; 27 import java.util.ArrayList; 28 import java.util.Collections; 29 30 /** 31 * Per-class directory of annotations. 32 */ 33 public final class AnnotationsDirectoryItem extends OffsettedItem { 34 /** the required alignment for instances of this class */ 35 private static final int ALIGNMENT = 4; 36 37 /** write size of this class's header, in bytes */ 38 private static final int HEADER_SIZE = 16; 39 40 /** write size of a list element, in bytes */ 41 private static final int ELEMENT_SIZE = 8; 42 43 /** {@code null-ok;} the class-level annotations, if any */ 44 private AnnotationSetItem classAnnotations; 45 46 /** {@code null-ok;} the annotated fields, if any */ 47 private ArrayList<FieldAnnotationStruct> fieldAnnotations; 48 49 /** {@code null-ok;} the annotated methods, if any */ 50 private ArrayList<MethodAnnotationStruct> methodAnnotations; 51 52 /** {@code null-ok;} the annotated parameters, if any */ 53 private ArrayList<ParameterAnnotationStruct> parameterAnnotations; 54 55 /** 56 * Constructs an empty instance. 57 */ 58 public AnnotationsDirectoryItem() { 59 super(ALIGNMENT, -1); 60 61 classAnnotations = null; 62 fieldAnnotations = null; 63 methodAnnotations = null; 64 parameterAnnotations = null; 65 } 66 67 /** {@inheritDoc} */ 68 @Override 69 public ItemType itemType() { 70 return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM; 71 } 72 73 /** 74 * Returns whether this item is empty (has no contents). 75 * 76 * @return {@code true} if this item is empty, or {@code false} 77 * if not 78 */ 79 public boolean isEmpty() { 80 return (classAnnotations == null) && 81 (fieldAnnotations == null) && 82 (methodAnnotations == null) && 83 (parameterAnnotations == null); 84 } 85 86 /** 87 * Returns whether this item is a candidate for interning. The only 88 * interning candidates are ones that <i>only</i> have a non-null 89 * set of class annotations, with no other lists. 90 * 91 * @return {@code true} if this is an interning candidate, or 92 * {@code false} if not 93 */ 94 public boolean isInternable() { 95 return (classAnnotations != null) && 96 (fieldAnnotations == null) && 97 (methodAnnotations == null) && 98 (parameterAnnotations == null); 99 } 100 101 /** {@inheritDoc} */ 102 @Override 103 public int hashCode() { 104 if (classAnnotations == null) { 105 return 0; 106 } 107 108 return classAnnotations.hashCode(); 109 } 110 111 /** 112 * {@inheritDoc} 113 * 114 * <p><b>Note:</b>: This throws an exception if this item is not 115 * internable.</p> 116 * 117 * @see #isInternable 118 */ 119 @Override 120 public int compareTo0(OffsettedItem other) { 121 if (! isInternable()) { 122 throw new UnsupportedOperationException("uninternable instance"); 123 } 124 125 AnnotationsDirectoryItem otherDirectory = 126 (AnnotationsDirectoryItem) other; 127 return classAnnotations.compareTo(otherDirectory.classAnnotations); 128 } 129 130 /** 131 * Sets the direct annotations on this instance. These are annotations 132 * made on the class, per se, as opposed to on one of its members. 133 * It is only valid to call this method at most once per instance. 134 * 135 * @param annotations {@code non-null;} annotations to set for this class 136 */ 137 public void setClassAnnotations(Annotations annotations) { 138 if (annotations == null) { 139 throw new NullPointerException("annotations == null"); 140 } 141 142 if (classAnnotations != null) { 143 throw new UnsupportedOperationException( 144 "class annotations already set"); 145 } 146 147 classAnnotations = new AnnotationSetItem(annotations); 148 } 149 150 /** 151 * Adds a field annotations item to this instance. 152 * 153 * @param field {@code non-null;} field in question 154 * @param annotations {@code non-null;} associated annotations to add 155 */ 156 public void addFieldAnnotations(CstFieldRef field, 157 Annotations annotations) { 158 if (fieldAnnotations == null) { 159 fieldAnnotations = new ArrayList<FieldAnnotationStruct>(); 160 } 161 162 fieldAnnotations.add(new FieldAnnotationStruct(field, 163 new AnnotationSetItem(annotations))); 164 } 165 166 /** 167 * Adds a method annotations item to this instance. 168 * 169 * @param method {@code non-null;} method in question 170 * @param annotations {@code non-null;} associated annotations to add 171 */ 172 public void addMethodAnnotations(CstMethodRef method, 173 Annotations annotations) { 174 if (methodAnnotations == null) { 175 methodAnnotations = new ArrayList<MethodAnnotationStruct>(); 176 } 177 178 methodAnnotations.add(new MethodAnnotationStruct(method, 179 new AnnotationSetItem(annotations))); 180 } 181 182 /** 183 * Adds a parameter annotations item to this instance. 184 * 185 * @param method {@code non-null;} method in question 186 * @param list {@code non-null;} associated list of annotation sets to add 187 */ 188 public void addParameterAnnotations(CstMethodRef method, 189 AnnotationsList list) { 190 if (parameterAnnotations == null) { 191 parameterAnnotations = new ArrayList<ParameterAnnotationStruct>(); 192 } 193 194 parameterAnnotations.add(new ParameterAnnotationStruct(method, list)); 195 } 196 197 /** 198 * Gets the method annotations for a given method, if any. This is 199 * meant for use by debugging / dumping code. 200 * 201 * @param method {@code non-null;} the method 202 * @return {@code null-ok;} the method annotations, if any 203 */ 204 public Annotations getMethodAnnotations(CstMethodRef method) { 205 if (methodAnnotations == null) { 206 return null; 207 } 208 209 for (MethodAnnotationStruct item : methodAnnotations) { 210 if (item.getMethod().equals(method)) { 211 return item.getAnnotations(); 212 } 213 } 214 215 return null; 216 } 217 218 /** 219 * Gets the parameter annotations for a given method, if any. This is 220 * meant for use by debugging / dumping code. 221 * 222 * @param method {@code non-null;} the method 223 * @return {@code null-ok;} the parameter annotations, if any 224 */ 225 public AnnotationsList getParameterAnnotations(CstMethodRef method) { 226 if (parameterAnnotations == null) { 227 return null; 228 } 229 230 for (ParameterAnnotationStruct item : parameterAnnotations) { 231 if (item.getMethod().equals(method)) { 232 return item.getAnnotationsList(); 233 } 234 } 235 236 return null; 237 } 238 239 /** {@inheritDoc} */ 240 public void addContents(DexFile file) { 241 MixedItemSection wordData = file.getWordData(); 242 243 if (classAnnotations != null) { 244 classAnnotations = wordData.intern(classAnnotations); 245 } 246 247 if (fieldAnnotations != null) { 248 for (FieldAnnotationStruct item : fieldAnnotations) { 249 item.addContents(file); 250 } 251 } 252 253 if (methodAnnotations != null) { 254 for (MethodAnnotationStruct item : methodAnnotations) { 255 item.addContents(file); 256 } 257 } 258 259 if (parameterAnnotations != null) { 260 for (ParameterAnnotationStruct item : parameterAnnotations) { 261 item.addContents(file); 262 } 263 } 264 } 265 266 /** {@inheritDoc} */ 267 @Override 268 public String toHuman() { 269 throw new RuntimeException("unsupported"); 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 protected void place0(Section addedTo, int offset) { 275 // We just need to set the write size here. 276 277 int elementCount = listSize(fieldAnnotations) 278 + listSize(methodAnnotations) + listSize(parameterAnnotations); 279 setWriteSize(HEADER_SIZE + (elementCount * ELEMENT_SIZE)); 280 } 281 282 /** {@inheritDoc} */ 283 @Override 284 protected void writeTo0(DexFile file, AnnotatedOutput out) { 285 boolean annotates = out.annotates(); 286 int classOff = OffsettedItem.getAbsoluteOffsetOr0(classAnnotations); 287 int fieldsSize = listSize(fieldAnnotations); 288 int methodsSize = listSize(methodAnnotations); 289 int parametersSize = listSize(parameterAnnotations); 290 291 if (annotates) { 292 out.annotate(0, offsetString() + " annotations directory"); 293 out.annotate(4, " class_annotations_off: " + Hex.u4(classOff)); 294 out.annotate(4, " fields_size: " + 295 Hex.u4(fieldsSize)); 296 out.annotate(4, " methods_size: " + 297 Hex.u4(methodsSize)); 298 out.annotate(4, " parameters_size: " + 299 Hex.u4(parametersSize)); 300 } 301 302 out.writeInt(classOff); 303 out.writeInt(fieldsSize); 304 out.writeInt(methodsSize); 305 out.writeInt(parametersSize); 306 307 if (fieldsSize != 0) { 308 Collections.sort(fieldAnnotations); 309 if (annotates) { 310 out.annotate(0, " fields:"); 311 } 312 for (FieldAnnotationStruct item : fieldAnnotations) { 313 item.writeTo(file, out); 314 } 315 } 316 317 if (methodsSize != 0) { 318 Collections.sort(methodAnnotations); 319 if (annotates) { 320 out.annotate(0, " methods:"); 321 } 322 for (MethodAnnotationStruct item : methodAnnotations) { 323 item.writeTo(file, out); 324 } 325 } 326 327 if (parametersSize != 0) { 328 Collections.sort(parameterAnnotations); 329 if (annotates) { 330 out.annotate(0, " parameters:"); 331 } 332 for (ParameterAnnotationStruct item : parameterAnnotations) { 333 item.writeTo(file, out); 334 } 335 } 336 } 337 338 /** 339 * Gets the list size of the given list, or {@code 0} if given 340 * {@code null}. 341 * 342 * @param list {@code null-ok;} the list in question 343 * @return {@code >= 0;} its size 344 */ 345 private static int listSize(ArrayList<?> list) { 346 if (list == null) { 347 return 0; 348 } 349 350 return list.size(); 351 } 352 353 /** 354 * Prints out the contents of this instance, in a debugging-friendly 355 * way. This is meant to be called from {@link ClassDefItem#debugPrint}. 356 * 357 * @param out {@code non-null;} where to output to 358 */ 359 /*package*/ void debugPrint(PrintWriter out) { 360 if (classAnnotations != null) { 361 out.println(" class annotations: " + classAnnotations); 362 } 363 364 if (fieldAnnotations != null) { 365 out.println(" field annotations:"); 366 for (FieldAnnotationStruct item : fieldAnnotations) { 367 out.println(" " + item.toHuman()); 368 } 369 } 370 371 if (methodAnnotations != null) { 372 out.println(" method annotations:"); 373 for (MethodAnnotationStruct item : methodAnnotations) { 374 out.println(" " + item.toHuman()); 375 } 376 } 377 378 if (parameterAnnotations != null) { 379 out.println(" parameter annotations:"); 380 for (ParameterAnnotationStruct item : parameterAnnotations) { 381 out.println(" " + item.toHuman()); 382 } 383 } 384 } 385 } 386