Home | History | Annotate | Download | only in dexlib
      1 /*
      2  * [The "BSD licence"]
      3  * Copyright (c) 2010 Ben Gruver (JesusFreke)
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  * 3. The name of the author may not be used to endorse or promote products
     15  *    derived from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 package org.jf.dexlib;
     30 
     31 import com.google.common.base.Preconditions;
     32 import org.jf.dexlib.Util.AnnotatedOutput;
     33 import org.jf.dexlib.Util.ExceptionWithContext;
     34 import org.jf.dexlib.Util.Input;
     35 import org.jf.dexlib.Util.ReadOnlyArrayList;
     36 
     37 import javax.annotation.Nonnull;
     38 import javax.annotation.Nullable;
     39 import java.util.*;
     40 
     41 public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
     42     @Nullable
     43     private AnnotationSetItem classAnnotations;
     44     @Nullable
     45     private FieldAnnotation[] fieldAnnotations;
     46     @Nullable
     47     private MethodAnnotation[] methodAnnotations;
     48     @Nullable
     49     private ParameterAnnotation[] parameterAnnotations;
     50 
     51     /**
     52      * Creates a new uninitialized <code>AnnotationDirectoryItem</code>
     53      * @param dexFile The <code>DexFile</code> that this item belongs to
     54      */
     55     protected AnnotationDirectoryItem(DexFile dexFile) {
     56         super(dexFile);
     57     }
     58 
     59     /**
     60      * Creates a new <code>AnnotationDirectoryItem</code> with the given values
     61      * @param dexFile The <code>DexFile</code> that this item belongs to
     62      * @param classAnnotations The annotations associated with the overall class
     63      * @param fieldAnnotations A list of <code>FieldAnnotation</code> objects that contain the field annotations for
     64      * this class
     65      * @param methodAnnotations A list of <code>MethodAnnotation</code> objects that contain the method annotations for
     66      * this class
     67      * @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects that contain the parameter
     68      * annotations for the methods in this class
     69      */
     70     private AnnotationDirectoryItem(DexFile dexFile, @Nullable AnnotationSetItem classAnnotations,
     71                                     @Nullable List<FieldAnnotation> fieldAnnotations,
     72                                     @Nullable List<MethodAnnotation> methodAnnotations,
     73                                     @Nullable List<ParameterAnnotation> parameterAnnotations) {
     74         super(dexFile);
     75         this.classAnnotations = classAnnotations;
     76 
     77         if (fieldAnnotations == null || fieldAnnotations.size() == 0) {
     78             this.fieldAnnotations = null;
     79         } else {
     80             this.fieldAnnotations = new FieldAnnotation[fieldAnnotations.size()];
     81             this.fieldAnnotations = fieldAnnotations.toArray(this.fieldAnnotations);
     82             Arrays.sort(this.fieldAnnotations);
     83         }
     84 
     85         if (methodAnnotations == null || methodAnnotations.size() == 0) {
     86             this.methodAnnotations = null;
     87         } else {
     88             this.methodAnnotations = new MethodAnnotation[methodAnnotations.size()];
     89             this.methodAnnotations = methodAnnotations.toArray(this.methodAnnotations);
     90             Arrays.sort(this.methodAnnotations);
     91         }
     92 
     93         if (parameterAnnotations == null || parameterAnnotations.size() == 0) {
     94             this.parameterAnnotations = null;
     95         } else {
     96             this.parameterAnnotations = new ParameterAnnotation[parameterAnnotations.size()];
     97             this.parameterAnnotations = parameterAnnotations.toArray(this.parameterAnnotations);
     98             Arrays.sort(this.parameterAnnotations);
     99         }
    100     }
    101 
    102     /**
    103      * Returns an <code>AnnotationDirectoryItem</code> for the given values, and that has been interned into the given
    104      * <code>DexFile</code>
    105      * @param dexFile The <code>DexFile</code> that this item belongs to
    106      * @param classAnnotations The annotations associated with the class
    107      * @param fieldAnnotations A list of <code>FieldAnnotation</code> objects containing the field annotations
    108      * @param methodAnnotations A list of <code>MethodAnnotation</code> objects containing the method annotations
    109      * @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects containin the parameter
    110      * annotations
    111      * @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
    112      * <code>DexFile</code>
    113      */
    114     public static AnnotationDirectoryItem internAnnotationDirectoryItem(DexFile dexFile,
    115                                     AnnotationSetItem classAnnotations,
    116                                     List<FieldAnnotation> fieldAnnotations,
    117                                     List<MethodAnnotation> methodAnnotations,
    118                                     List<ParameterAnnotation> parameterAnnotations) {
    119         AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations,
    120                 fieldAnnotations, methodAnnotations, parameterAnnotations);
    121         return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem);
    122     }
    123 
    124     /** {@inheritDoc} */
    125     protected void readItem(Input in, ReadContext readContext) {
    126         classAnnotations = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset(
    127                 ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
    128 
    129         int fieldAnnotationCount = in.readInt();
    130         if (fieldAnnotationCount > 0) {
    131             fieldAnnotations = new FieldAnnotation[fieldAnnotationCount];
    132         } else {
    133             fieldAnnotations = null;
    134         }
    135 
    136         int methodAnnotationCount = in.readInt();
    137         if (methodAnnotationCount > 0) {
    138             methodAnnotations = new MethodAnnotation[methodAnnotationCount];
    139         } else {
    140             methodAnnotations = null;
    141         }
    142 
    143         int parameterAnnotationCount = in.readInt();
    144         if (parameterAnnotationCount > 0) {
    145             parameterAnnotations = new ParameterAnnotation[parameterAnnotationCount];
    146         } else {
    147             parameterAnnotations = null;
    148         }
    149 
    150         if (fieldAnnotations != null) {
    151             for (int i=0; i<fieldAnnotations.length; i++) {
    152                 try {
    153                     FieldIdItem fieldIdItem = dexFile.FieldIdsSection.getItemByIndex(in.readInt());
    154                     AnnotationSetItem fieldAnnotationSet = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
    155                             ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
    156                     fieldAnnotations[i] = new FieldAnnotation(fieldIdItem, fieldAnnotationSet);
    157                 } catch (Exception ex) {
    158                     throw ExceptionWithContext.withContext(ex,
    159                             "Error occured while reading FieldAnnotation at index " + i);
    160                 }
    161             }
    162         }
    163 
    164         if (methodAnnotations != null) {
    165             for (int i=0; i<methodAnnotations.length; i++) {
    166                 try {
    167                     MethodIdItem methodIdItem = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
    168                     AnnotationSetItem methodAnnotationSet = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
    169                             ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
    170                     methodAnnotations[i] = new MethodAnnotation(methodIdItem, methodAnnotationSet);
    171                 } catch (Exception ex) {
    172                     throw ExceptionWithContext.withContext(ex,
    173                             "Error occured while reading MethodAnnotation at index " + i);
    174                 }
    175             }
    176         }
    177 
    178         if (parameterAnnotations != null) {
    179             for (int i=0; i<parameterAnnotations.length; i++) {
    180                 try {
    181                     MethodIdItem methodIdItem = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
    182                     AnnotationSetRefList paramaterAnnotationSet = (AnnotationSetRefList)readContext.getOffsettedItemByOffset(
    183                             ItemType.TYPE_ANNOTATION_SET_REF_LIST, in.readInt());
    184                     parameterAnnotations[i] = new ParameterAnnotation(methodIdItem, paramaterAnnotationSet);
    185                 } catch (Exception ex) {
    186                     throw ExceptionWithContext.withContext(ex,
    187                             "Error occured while reading ParameterAnnotation at index " + i);
    188                 }
    189             }
    190         }
    191     }
    192 
    193     /** {@inheritDoc} */
    194     protected int placeItem(int offset) {
    195         return offset + 16 + (
    196                 (fieldAnnotations==null?0:fieldAnnotations.length) +
    197                 (methodAnnotations==null?0:methodAnnotations.length) +
    198                 (parameterAnnotations==null?0:parameterAnnotations.length)) * 8;
    199     }
    200 
    201     /** {@inheritDoc} */
    202     protected void writeItem(AnnotatedOutput out) {
    203         if (out.annotates()) {
    204             TypeIdItem parentType = getParentType();
    205             if (parentType != null) {
    206                 out.annotate(0, parentType.getTypeDescriptor());
    207             }
    208             if (classAnnotations != null) {
    209                 out.annotate(4, "class_annotations_off: 0x" + Integer.toHexString(classAnnotations.getOffset()));
    210             } else {
    211                 out.annotate(4, "class_annotations_off:");
    212             }
    213 
    214             int length = fieldAnnotations==null?0:fieldAnnotations.length;
    215             out.annotate(4, "annotated_fields_size: 0x" + Integer.toHexString(length) + " (" +
    216                     length + ")");
    217             length = methodAnnotations==null?0:methodAnnotations.length;
    218             out.annotate(4, "annotated_methods_size: 0x" + Integer.toHexString(length) + " (" +
    219                     length + ")");
    220             length = parameterAnnotations==null?0:parameterAnnotations.length;
    221             out.annotate(4, "annotated_parameters_size: 0x" + Integer.toHexString(length) + " (" +
    222                     length + ")");
    223 
    224             int index;
    225             if (fieldAnnotations != null) {
    226                index = 0;
    227                 for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
    228                     out.annotate(0, "[" + index++ + "] field_annotation");
    229 
    230                     out.indent();
    231                     out.annotate(4, "field: " + fieldAnnotation.field.getFieldName().getStringValue() + ":" +
    232                             fieldAnnotation.field.getFieldType().getTypeDescriptor());
    233                     out.annotate(4, "annotations_off: 0x" +
    234                             Integer.toHexString(fieldAnnotation.annotationSet.getOffset()));
    235                     out.deindent();
    236                 }
    237             }
    238 
    239             if (methodAnnotations != null) {
    240                 index = 0;
    241                 for (MethodAnnotation methodAnnotation: methodAnnotations) {
    242                     out.annotate(0, "[" + index++ + "] method_annotation");
    243                     out.indent();
    244                     out.annotate(4, "method: " + methodAnnotation.method.getMethodString());
    245                     out.annotate(4, "annotations_off: 0x" +
    246                             Integer.toHexString(methodAnnotation.annotationSet.getOffset()));
    247                     out.deindent();
    248                 }
    249             }
    250 
    251             if (parameterAnnotations != null) {
    252                 index = 0;
    253                 for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
    254                     out.annotate(0, "[" + index++ + "] parameter_annotation");
    255                     out.indent();
    256                     out.annotate(4, "method: " + parameterAnnotation.method.getMethodString());
    257                     out.annotate(4, "annotations_off: 0x" +
    258                             Integer.toHexString(parameterAnnotation.annotationSet.getOffset()));
    259                 }
    260             }
    261         }
    262 
    263         out.writeInt(classAnnotations==null?0:classAnnotations.getOffset());
    264         out.writeInt(fieldAnnotations==null?0:fieldAnnotations.length);
    265         out.writeInt(methodAnnotations==null?0:methodAnnotations.length);
    266         out.writeInt(parameterAnnotations==null?0:parameterAnnotations.length);
    267 
    268         if (fieldAnnotations != null) {
    269             for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
    270                 out.writeInt(fieldAnnotation.field.getIndex());
    271                 out.writeInt(fieldAnnotation.annotationSet.getOffset());
    272             }
    273         }
    274 
    275         if (methodAnnotations != null) {
    276             for (MethodAnnotation methodAnnotation: methodAnnotations) {
    277                 out.writeInt(methodAnnotation.method.getIndex());
    278                 out.writeInt(methodAnnotation.annotationSet.getOffset());
    279             }
    280         }
    281 
    282         if (parameterAnnotations != null) {
    283             for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
    284                 out.writeInt(parameterAnnotation.method.getIndex());
    285                 out.writeInt(parameterAnnotation.annotationSet.getOffset());
    286             }
    287         }
    288     }
    289 
    290     /** {@inheritDoc} */
    291     public ItemType getItemType() {
    292         return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
    293     }
    294 
    295     /** {@inheritDoc} */
    296     public String getConciseIdentity() {
    297         TypeIdItem parentType = getParentType();
    298         if (parentType == null) {
    299             return "annotation_directory_item @0x" + Integer.toHexString(getOffset());
    300         }
    301         return "annotation_directory_item @0x" + Integer.toHexString(getOffset()) +
    302                " (" + parentType.getTypeDescriptor() + ")";
    303     }
    304 
    305     /** {@inheritDoc} */
    306     public int compareTo(AnnotationDirectoryItem o) {
    307         Preconditions.checkNotNull(o);
    308 
    309         TypeIdItem parentType = getParentType();
    310         TypeIdItem otherParentType = o.getParentType();
    311         if (parentType != null) {
    312             if (otherParentType != null) {
    313                 return parentType.compareTo(otherParentType);
    314             }
    315             return 1;
    316         }
    317         if (otherParentType != null) {
    318             return -1;
    319         }
    320 
    321         if (classAnnotations != null) {
    322             if (o.classAnnotations != null) {
    323                 return classAnnotations.compareTo(o.classAnnotations);
    324             }
    325             return 1;
    326         }
    327         return -1;
    328     }
    329 
    330     /**
    331      * Returns the parent type for an AnnotationDirectoryItem that is guaranteed to have a single parent, or null
    332      * for one that may be referenced by multiple classes.
    333      *
    334      * Specifically, the AnnotationDirectoryItem may be referenced by multiple classes if it has only class annotations,
    335      * but not field/method/parameter annotations.
    336      *
    337      * @return The parent type for this AnnotationDirectoryItem, or null if it may have multiple parents
    338      */
    339     @Nullable
    340     public TypeIdItem getParentType() {
    341         if (fieldAnnotations != null && fieldAnnotations.length > 0) {
    342             return fieldAnnotations[0].field.getContainingClass();
    343         }
    344         if (methodAnnotations != null && methodAnnotations.length > 0) {
    345             return methodAnnotations[0].method.getContainingClass();
    346         }
    347         if (parameterAnnotations != null && parameterAnnotations.length > 0) {
    348             return parameterAnnotations[0].method.getContainingClass();
    349         }
    350         return null;
    351     }
    352 
    353     /**
    354      * @return An <code>AnnotationSetItem</code> containing the annotations associated with this class, or null
    355      * if there are no class annotations
    356      */
    357     @Nullable
    358     public AnnotationSetItem getClassAnnotations() {
    359         return classAnnotations;
    360     }
    361 
    362     /**
    363      * Get a list of the field annotations in this <code>AnnotationDirectoryItem</code>
    364      * @return A list of FieldAnnotation objects, or null if there are no field annotations
    365      */
    366     @Nonnull
    367     public List<FieldAnnotation> getFieldAnnotations() {
    368         if (fieldAnnotations == null) {
    369             return Collections.emptyList();
    370         }
    371         return ReadOnlyArrayList.of(fieldAnnotations);
    372     }
    373 
    374     /**
    375      * Get a list of the method annotations in this <code>AnnotationDirectoryItem</code>
    376      * @return A list of MethodAnnotation objects, or null if there are no method annotations
    377      */
    378     @Nonnull
    379     public List<MethodAnnotation> getMethodAnnotations() {
    380         if (methodAnnotations == null) {
    381             return Collections.emptyList();
    382         }
    383         return ReadOnlyArrayList.of(methodAnnotations);
    384     }
    385 
    386     /**
    387      * Get a list of the parameter annotations in this <code>AnnotationDirectoryItem</code>
    388      * @return A list of ParameterAnnotation objects, or null if there are no parameter annotations
    389      */
    390     @Nonnull
    391     public List<ParameterAnnotation> getParameterAnnotations() {
    392         if (parameterAnnotations == null) {
    393             return Collections.emptyList();
    394         }
    395         return ReadOnlyArrayList.of(parameterAnnotations);
    396     }
    397 
    398     /**
    399      * Gets the field annotations for the given field, or null if no annotations are defined for that field
    400      * @param fieldIdItem The field to get the annotations for
    401      * @return An <code>AnnotationSetItem</code> containing the field annotations, or null if none are found
    402      */
    403     @Nullable
    404     public AnnotationSetItem getFieldAnnotations(FieldIdItem fieldIdItem) {
    405         if (fieldAnnotations == null) {
    406             return null;
    407         }
    408         int index = Arrays.binarySearch(fieldAnnotations, fieldIdItem);
    409         if (index < 0) {
    410             return null;
    411         }
    412         return fieldAnnotations[index].annotationSet;
    413     }
    414 
    415     /**
    416      * Gets the method annotations for the given method, or null if no annotations are defined for that method
    417      * @param methodIdItem The method to get the annotations for
    418      * @return An <code>AnnotationSetItem</code> containing the method annotations, or null if none are found
    419      */
    420     @Nullable
    421     public AnnotationSetItem getMethodAnnotations(MethodIdItem methodIdItem) {
    422         if (methodAnnotations == null) {
    423             return null;
    424         }
    425         int index = Arrays.binarySearch(methodAnnotations, methodIdItem);
    426         if (index < 0) {
    427             return null;
    428         }
    429         return methodAnnotations[index].annotationSet;
    430     }
    431 
    432     /**
    433      * Gets the parameter annotations for the given method, or null if no parameter annotations are defined for that
    434      * method
    435      * @param methodIdItem The method to get the parameter annotations for
    436      * @return An <code>AnnotationSetRefList</code> containing the parameter annotations, or null if none are found
    437      */
    438     @Nullable
    439     public AnnotationSetRefList getParameterAnnotations(MethodIdItem methodIdItem) {
    440         if (parameterAnnotations == null) {
    441             return null;
    442         }
    443         int index = Arrays.binarySearch(parameterAnnotations, methodIdItem);
    444         if (index < 0) {
    445             return null;
    446         }
    447         return parameterAnnotations[index].annotationSet;
    448     }
    449 
    450     /**
    451      *
    452      */
    453     public int getClassAnnotationCount() {
    454         if (classAnnotations == null) {
    455             return 0;
    456         }
    457         AnnotationItem[] annotations = classAnnotations.getAnnotations();
    458         return annotations.length;
    459     }
    460 
    461     /**
    462      * @return The number of field annotations in this <code>AnnotationDirectoryItem</code>
    463      */
    464     public int getFieldAnnotationCount() {
    465         if (fieldAnnotations == null) {
    466             return 0;
    467         }
    468         return fieldAnnotations.length;
    469     }
    470 
    471     /**
    472      * @return The number of method annotations in this <code>AnnotationDirectoryItem</code>
    473      */
    474     public int getMethodAnnotationCount() {
    475         if (methodAnnotations == null) {
    476             return 0;
    477         }
    478         return methodAnnotations.length;
    479     }
    480 
    481     /**
    482      * @return The number of parameter annotations in this <code>AnnotationDirectoryItem</code>
    483      */
    484     public int getParameterAnnotationCount() {
    485         if (parameterAnnotations == null) {
    486             return 0;
    487         }
    488         return parameterAnnotations.length;
    489     }
    490 
    491     @Override
    492     public int hashCode() {
    493         // If the item has a single parent, we can use the re-use the identity (hash) of that parent
    494         TypeIdItem parentType = getParentType();
    495         if (parentType != null) {
    496             return parentType.hashCode();
    497         }
    498         if (classAnnotations != null) {
    499             return classAnnotations.hashCode();
    500         }
    501         return 0;
    502     }
    503 
    504     @Override
    505     public boolean equals(Object o) {
    506         if (this==o) {
    507             return true;
    508         }
    509         if (o==null || !this.getClass().equals(o.getClass())) {
    510             return false;
    511         }
    512 
    513         AnnotationDirectoryItem other = (AnnotationDirectoryItem)o;
    514         return (this.compareTo(other) == 0);
    515     }
    516 
    517     public static class FieldAnnotation implements Comparable<Convertible<FieldIdItem>>, Convertible<FieldIdItem> {
    518         public final FieldIdItem field;
    519         public final AnnotationSetItem annotationSet;
    520 
    521         public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) {
    522             this.field = field;
    523             this.annotationSet = annotationSet;
    524         }
    525 
    526         public int compareTo(Convertible<FieldIdItem> other) {
    527             return field.compareTo(other.convert());
    528         }
    529 
    530         @Override
    531         public boolean equals(Object o) {
    532             if (this == o) return true;
    533             if (o == null || getClass() != o.getClass()) return false;
    534 
    535             return compareTo((FieldAnnotation)o) == 0;
    536         }
    537 
    538         @Override
    539         public int hashCode() {
    540             return field.hashCode() + 31 * annotationSet.hashCode();
    541         }
    542 
    543         public FieldIdItem convert() {
    544             return field;
    545         }
    546     }
    547 
    548     public static class MethodAnnotation implements Comparable<Convertible<MethodIdItem>>, Convertible<MethodIdItem> {
    549         public final MethodIdItem method;
    550         public final AnnotationSetItem annotationSet;
    551 
    552         public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) {
    553             this.method = method;
    554             this.annotationSet = annotationSet;
    555         }
    556 
    557         public int compareTo(Convertible<MethodIdItem> other) {
    558             return method.compareTo(other.convert());
    559         }
    560 
    561         @Override
    562         public boolean equals(Object o) {
    563             if (this == o) return true;
    564             if (o == null || getClass() != o.getClass()) return false;
    565 
    566             return compareTo((MethodAnnotation)o) == 0;
    567         }
    568 
    569         @Override
    570         public int hashCode() {
    571             return method.hashCode() + 31 * annotationSet.hashCode();
    572         }
    573 
    574         public MethodIdItem convert() {
    575             return method;
    576         }
    577     }
    578 
    579     public static class ParameterAnnotation implements Comparable<Convertible<MethodIdItem>>,
    580             Convertible<MethodIdItem> {
    581         public final MethodIdItem method;
    582         public final AnnotationSetRefList annotationSet;
    583 
    584         public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) {
    585             this.method = method;
    586             this.annotationSet = annotationSet;
    587         }
    588 
    589         public int compareTo(Convertible<MethodIdItem> other) {
    590             return method.compareTo(other.convert());
    591         }
    592 
    593         @Override
    594         public boolean equals(Object o) {
    595             if (this == o) return true;
    596             if (o == null || getClass() != o.getClass()) return false;
    597 
    598             return compareTo((ParameterAnnotation)o) == 0;
    599         }
    600 
    601         @Override
    602         public int hashCode() {
    603             return method.hashCode() + 31 * annotationSet.hashCode();
    604         }
    605 
    606         public MethodIdItem convert() {
    607             return method;
    608         }
    609     }
    610 }
    611