1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21package proguard.classfile.editor; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.annotation.*; 25 26/** 27 * This class can add and delete element values to and from a given target 28 * annotation default attribute, annotation, or array element value. Element 29 * values to be added must be filled out beforehand, including their references 30 * to the constant pool. 31 * 32 * @author Eric Lafortune 33 */ 34public class ElementValuesEditor 35{ 36 private final ProgramClass targetClass; 37 private final Annotation targetAnnotation; 38 private final ArrayElementValue targetArrayElementValue; 39 private final boolean replaceElementValues; 40 41 42 /** 43 * Creates a new ElementValuesEditor that will edit element values in the 44 * given target annotation. 45 */ 46 public ElementValuesEditor(ProgramClass targetClass, 47 Annotation targetAnnotation, 48 boolean replaceElementValues) 49 { 50 this.targetClass = targetClass; 51 this.targetAnnotation = targetAnnotation; 52 this.targetArrayElementValue = null; 53 this.replaceElementValues = replaceElementValues; 54 } 55 56 57 /** 58 * Creates a new ElementValuesEditor that will edit element values in the 59 * given target array element value. 60 */ 61 public ElementValuesEditor(ProgramClass targetClass, 62 ArrayElementValue targetArrayElementValue, 63 boolean replaceElementValues) 64 { 65 this.targetClass = targetClass; 66 this.targetAnnotation = null; 67 this.targetArrayElementValue = targetArrayElementValue; 68 this.replaceElementValues = replaceElementValues; 69 } 70 71 72 /** 73 * Adds the given elementValue to the target. 74 */ 75 public void addElementValue(ElementValue elementValue) 76 { 77 // What's the target? 78 if (targetAnnotation != null) 79 { 80 // Try to replace an existing element value. 81 if (!replaceElementValues || 82 !replaceElementValue(targetAnnotation.u2elementValuesCount, 83 targetAnnotation.elementValues, 84 elementValue)) 85 { 86 // Otherwise append the element value. 87 targetAnnotation.elementValues = 88 addElementValue(targetAnnotation.u2elementValuesCount, 89 targetAnnotation.elementValues, 90 elementValue); 91 92 targetAnnotation.u2elementValuesCount++; 93 } 94 } 95 else 96 { 97 // Try to replace an existing element value. 98 if (!replaceElementValues || 99 !replaceElementValue(targetArrayElementValue.u2elementValuesCount, 100 targetArrayElementValue.elementValues, 101 elementValue)) 102 { 103 // Otherwise append the element value. 104 targetArrayElementValue.elementValues = 105 addElementValue(targetArrayElementValue.u2elementValuesCount, 106 targetArrayElementValue.elementValues, 107 elementValue); 108 109 targetArrayElementValue.u2elementValuesCount++; 110 } 111 } 112 } 113 114 115 /** 116 * Deletes the given elementValue to the target. 117 */ 118 public void deleteElementValue(String elementValueMethodName) 119 { 120 // What's the target? 121 if (targetAnnotation != null) 122 { 123 // Delete the element value to the target annotation. 124 targetAnnotation.u2elementValuesCount = 125 deleteElementValue(targetAnnotation.u2elementValuesCount, 126 targetAnnotation.elementValues, 127 elementValueMethodName); 128 } 129 else 130 { 131 // Delete the element value to the target array element value. 132 targetArrayElementValue.u2elementValuesCount = 133 deleteElementValue(targetArrayElementValue.u2elementValuesCount, 134 targetArrayElementValue.elementValues, 135 elementValueMethodName); 136 } 137 } 138 139 140 // Small utility methods. 141 142 /** 143 * Tries put the given element value in place of an existing element value 144 * of the same name, returning whether it was present. 145 */ 146 private boolean replaceElementValue(int elementValuesCount, 147 ElementValue[] elementValues, 148 ElementValue elementValue) 149 { 150 // Find the element value with the same name. 151 int index = findElementValue(elementValuesCount, 152 elementValues, 153 elementValue.getMethodName(targetClass)); 154 if (index < 0) 155 { 156 return false; 157 } 158 159 elementValues[index] = elementValue; 160 161 return true; 162 } 163 164 165 /** 166 * Appends the given element value to the given array of element values, 167 * creating a new array if necessary. 168 */ 169 private ElementValue[] addElementValue(int elementValuesCount, 170 ElementValue[] elementValues, 171 ElementValue elementValue) 172 { 173 // Is the array too small to contain the additional elementValue? 174 if (elementValues.length <= elementValuesCount) 175 { 176 // Create a new array and copy the elementValues into it. 177 ElementValue[] newElementValues = new ElementValue[elementValuesCount + 1]; 178 System.arraycopy(elementValues, 0, 179 newElementValues, 0, 180 elementValuesCount); 181 elementValues = newElementValues; 182 } 183 184 // Append the elementValue. 185 elementValues[elementValuesCount] = elementValue; 186 187 return elementValues; 188 } 189 190 191 /** 192 * Deletes the element values with the given name from the given array of 193 * element values, returning the new number of element values. 194 */ 195 private int deleteElementValue(int elementValuesCount, 196 ElementValue[] elementValues, 197 String elementValueMethodName) 198 { 199 // Find the element value. 200 int index = findElementValue(elementValuesCount, 201 elementValues, 202 elementValueMethodName); 203 if (index < 0) 204 { 205 return elementValuesCount; 206 } 207 208 // Shift the other element values in the array. 209 System.arraycopy(elementValues, index + 1, 210 elementValues, index, 211 elementValuesCount - index - 1); 212 213 // Clear the last entry in the array. 214 elementValues[--elementValuesCount] = null; 215 216 return elementValuesCount; 217 } 218 219 220 /** 221 * Finds the index of the element value with the given name in the given 222 * array of element values. 223 */ 224 private int findElementValue(int elementValuesCount, 225 ElementValue[] elementValues, 226 String elementValueName) 227 { 228 for (int index = 0; index < elementValuesCount; index++) 229 { 230 if (elementValues[index].getMethodName(targetClass).equals(elementValueName)) 231 { 232 return index; 233 } 234 } 235 236 return -1; 237 } 238}