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 29package org.jf.dexlib; 30 31import org.jf.dexlib.Util.AnnotatedOutput; 32import org.jf.dexlib.Util.Input; 33 34public 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}