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 org.jf.dexlib.Util.AnnotatedOutput;
     32 import org.jf.dexlib.Util.Input;
     33 
     34 public class FieldIdItem extends Item<FieldIdItem> implements Convertible<FieldIdItem> {
     35     private int hashCode = 0;
     36 
     37     private TypeIdItem classType;
     38     private TypeIdItem fieldType;
     39     private StringIdItem fieldName;
     40 
     41     /**
     42      * Creates a new uninitialized <code>FieldIdItem</code>
     43      * @param dexFile The <code>DexFile</code> that this item belongs to
     44      */
     45     protected FieldIdItem(DexFile dexFile) {
     46         super(dexFile);
     47     }
     48 
     49     /**
     50      * Creates a new <code>FieldIdItem</code> for the given class, type and name
     51      * @param dexFile The <code>DexFile</code> that this item belongs to
     52      * @param classType the class that the field is a member of
     53      * @param fieldType the type of the field
     54      * @param fieldName the name of the field
     55      */
     56     private FieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType, StringIdItem fieldName) {
     57         this(dexFile);
     58 
     59         assert classType.dexFile == dexFile;
     60         assert fieldType.dexFile == dexFile;
     61         assert fieldName.dexFile == dexFile;
     62 
     63         this.classType = classType;
     64         this.fieldType = fieldType;
     65         this.fieldName = fieldName;
     66     }
     67 
     68     /**
     69      * Returns a <code>FieldIdItem</code> for the given values, and that has been interned into
     70      * the given <code>DexFile</code>
     71      * @param dexFile The <code>DexFile</code> that this item belongs to
     72      * @param classType the class that the field is a member of
     73      * @param fieldType the type of the field
     74      * @param fieldName the name of the field
     75      * @return a <code>FieldIdItem</code> for the given values, and that has been interned into
     76      * the given <code>DexFile</code>
     77      */
     78     public static FieldIdItem internFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType,
     79                                               StringIdItem fieldName) {
     80         FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName);
     81         return dexFile.FieldIdsSection.intern(fieldIdItem);
     82     }
     83 
     84     /**
     85      * Looks up a <code>FieldIdItem</code> from the given <code>DexFile</code> for the given
     86      * values
     87      * @param dexFile The <code>DexFile</code> that this item belongs to
     88      * @param classType the class that the field is a member of
     89      * @param fieldType the type of the field
     90      * @param fieldName the name of the field
     91      * @return a <code>FieldIdItem</code> from the given <code>DexFile</code> for the given
     92      * values, or null if it doesn't exist
     93      */
     94     public static FieldIdItem lookupFieldIdItem(DexFile dexFile, TypeIdItem classType, TypeIdItem fieldType,
     95                                               StringIdItem fieldName) {
     96         FieldIdItem fieldIdItem = new FieldIdItem(dexFile, classType, fieldType, fieldName);
     97         return dexFile.FieldIdsSection.getInternedItem(fieldIdItem);
     98     }
     99 
    100     /** {@inheritDoc} */
    101     protected void readItem(Input in, ReadContext readContext) {
    102         classType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
    103         fieldType = dexFile.TypeIdsSection.getItemByIndex(in.readShort());
    104         fieldName = dexFile.StringIdsSection.getItemByIndex(in.readInt());
    105     }
    106 
    107     /** {@inheritDoc} */
    108     protected int placeItem(int offset) {
    109         return offset + 8;
    110     }
    111 
    112     /** {@inheritDoc} */
    113     protected void writeItem(AnnotatedOutput out) {
    114         if (out.annotates()) {
    115             out.annotate(2, "class_type: " + classType.getTypeDescriptor());
    116             out.annotate(2, "field_type: " + fieldType.getTypeDescriptor());
    117             out.annotate(4, "field_name: " + fieldName.getStringValue());
    118         }
    119 
    120         int classIndex = classType.getIndex();
    121         if (classIndex > 0xffff) {
    122             throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of " +
    123                     "defining class %s is too large", getFieldString(), classType.getTypeDescriptor()));
    124         }
    125         out.writeShort(classIndex);
    126 
    127         int typeIndex = fieldType.getIndex();
    128         if (typeIndex > 0xffff) {
    129             throw new RuntimeException(String.format("Error writing field_id_item for %s. The type index of field " +
    130                     "type %s is too large", getFieldString(), fieldType.getTypeDescriptor()));
    131         }
    132         out.writeShort(typeIndex);
    133 
    134         out.writeInt(fieldName.getIndex());
    135     }
    136 
    137     /** {@inheritDoc} */
    138     public ItemType getItemType() {
    139         return ItemType.TYPE_FIELD_ID_ITEM;
    140     }
    141 
    142     /** {@inheritDoc} */
    143     public String getConciseIdentity() {
    144         return getFieldString();
    145     }
    146 
    147     /** {@inheritDoc} */
    148     public int compareTo(FieldIdItem o) {
    149         int result = classType.compareTo(o.classType);
    150         if (result != 0) {
    151             return result;
    152         }
    153 
    154         result = fieldName.compareTo(o.fieldName);
    155         if (result != 0) {
    156             return result;
    157         }
    158 
    159         return fieldType.compareTo(o.fieldType);
    160     }
    161 
    162     /**
    163      * @return the class that this field is a member of
    164      */
    165     public TypeIdItem getContainingClass() {
    166         return classType;
    167     }
    168 
    169     /**
    170      * @return the type of this field
    171      */
    172     public TypeIdItem getFieldType() {
    173         return fieldType;
    174     }
    175 
    176     /**
    177      * @return the field name
    178      */
    179     public StringIdItem getFieldName() {
    180         return fieldName;
    181     }
    182 
    183     String cachedFieldString = null;
    184     /**
    185      * @return a string formatted like LclassName;->fieldName:fieldType
    186      */
    187     public String getFieldString() {
    188         if (cachedFieldString == null) {
    189             String typeDescriptor = classType.getTypeDescriptor();
    190             String fieldName = this.fieldName.getStringValue();
    191             String fieldType = this.fieldType.getTypeDescriptor();
    192 
    193             StringBuffer sb = new StringBuffer(typeDescriptor.length() + fieldName.length() + fieldType.length() + 3);
    194             sb.append(typeDescriptor);
    195             sb.append("->");
    196             sb.append(fieldName);
    197             sb.append(":");
    198             sb.append(fieldType);
    199             cachedFieldString = sb.toString();
    200         }
    201         return cachedFieldString;
    202     }
    203 
    204     String cachedShortFieldString = null;
    205     /**
    206      * @return a "short" string containing just the field name and type, formatted like fieldName:fieldType
    207      */
    208     public String getShortFieldString() {
    209         if (cachedShortFieldString == null) {
    210             String fieldName = this.fieldName.getStringValue();
    211             String fieldType = this.fieldType.getTypeDescriptor();
    212 
    213             StringBuffer sb = new StringBuffer(fieldName.length() + fieldType.length() + 1);
    214             sb.append(fieldName);
    215             sb.append(":");
    216             sb.append(fieldType);
    217             cachedShortFieldString = sb.toString();
    218         }
    219         return cachedShortFieldString;
    220     }
    221 
    222 
    223     /**
    224      * calculate and cache the hashcode
    225      */
    226     private void calcHashCode() {
    227         hashCode = classType.hashCode();
    228         hashCode = 31 * hashCode + fieldType.hashCode();
    229         hashCode = 31 * hashCode + fieldName.hashCode();
    230     }
    231 
    232     @Override
    233     public int hashCode() {
    234         //there's a small possibility that the actual hash code will be 0. If so, we'll
    235         //just end up recalculating it each time
    236         if (hashCode == 0)
    237             calcHashCode();
    238         return hashCode;
    239     }
    240 
    241     @Override
    242     public boolean equals(Object o) {
    243         if (this==o) {
    244             return true;
    245         }
    246         if (o==null || !this.getClass().equals(o.getClass())) {
    247             return false;
    248         }
    249 
    250         //This assumes that the referenced items have been interned in both objects.
    251         //This is a valid assumption because all outside code must use the static
    252         //"getInterned..." style methods to make new items, and any item created
    253         //internally is guaranteed to be interned
    254         FieldIdItem other = (FieldIdItem)o;
    255         return (classType == other.classType &&
    256                 fieldType == other.fieldType &&
    257                 fieldName == other.fieldName);
    258     }
    259 
    260     public FieldIdItem convert() {
    261         return this;
    262     }
    263 }