blob: c4add271f562158ff14459bdd14153857399e966 [file] [log] [blame]
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.classfile.editor;
import proguard.classfile.ProgramClass;
import proguard.classfile.attribute.annotation.*;
/**
* This class can add and delete element values to and from a given target
* annotation default attribute, annotation, or array element value. Element
* values to be added must be filled out beforehand, including their references
* to the constant pool.
*
* @author Eric Lafortune
*/
public class ElementValuesEditor
{
private final ProgramClass targetClass;
private final Annotation targetAnnotation;
private final ArrayElementValue targetArrayElementValue;
private final boolean replaceElementValues;
/**
* Creates a new ElementValuesEditor that will edit element values in the
* given target annotation.
*/
public ElementValuesEditor(ProgramClass targetClass,
Annotation targetAnnotation,
boolean replaceElementValues)
{
this.targetClass = targetClass;
this.targetAnnotation = targetAnnotation;
this.targetArrayElementValue = null;
this.replaceElementValues = replaceElementValues;
}
/**
* Creates a new ElementValuesEditor that will edit element values in the
* given target array element value.
*/
public ElementValuesEditor(ProgramClass targetClass,
ArrayElementValue targetArrayElementValue,
boolean replaceElementValues)
{
this.targetClass = targetClass;
this.targetAnnotation = null;
this.targetArrayElementValue = targetArrayElementValue;
this.replaceElementValues = replaceElementValues;
}
/**
* Adds the given elementValue to the target.
*/
public void addElementValue(ElementValue elementValue)
{
// What's the target?
if (targetAnnotation != null)
{
// Try to replace an existing element value.
if (!replaceElementValues ||
!replaceElementValue(targetAnnotation.u2elementValuesCount,
targetAnnotation.elementValues,
elementValue))
{
// Otherwise append the element value.
targetAnnotation.elementValues =
addElementValue(targetAnnotation.u2elementValuesCount,
targetAnnotation.elementValues,
elementValue);
targetAnnotation.u2elementValuesCount++;
}
}
else
{
// Try to replace an existing element value.
if (!replaceElementValues ||
!replaceElementValue(targetArrayElementValue.u2elementValuesCount,
targetArrayElementValue.elementValues,
elementValue))
{
// Otherwise append the element value.
targetArrayElementValue.elementValues =
addElementValue(targetArrayElementValue.u2elementValuesCount,
targetArrayElementValue.elementValues,
elementValue);
targetArrayElementValue.u2elementValuesCount++;
}
}
}
/**
* Deletes the given elementValue to the target.
*/
public void deleteElementValue(String elementValueMethodName)
{
// What's the target?
if (targetAnnotation != null)
{
// Delete the element value to the target annotation.
targetAnnotation.u2elementValuesCount =
deleteElementValue(targetAnnotation.u2elementValuesCount,
targetAnnotation.elementValues,
elementValueMethodName);
}
else
{
// Delete the element value to the target array element value.
targetArrayElementValue.u2elementValuesCount =
deleteElementValue(targetArrayElementValue.u2elementValuesCount,
targetArrayElementValue.elementValues,
elementValueMethodName);
}
}
// Small utility methods.
/**
* Tries put the given element value in place of an existing element value
* of the same name, returning whether it was present.
*/
private boolean replaceElementValue(int elementValuesCount,
ElementValue[] elementValues,
ElementValue elementValue)
{
// Find the element value with the same name.
int index = findElementValue(elementValuesCount,
elementValues,
elementValue.getMethodName(targetClass));
if (index < 0)
{
return false;
}
elementValues[index] = elementValue;
return true;
}
/**
* Appends the given element value to the given array of element values,
* creating a new array if necessary.
*/
private ElementValue[] addElementValue(int elementValuesCount,
ElementValue[] elementValues,
ElementValue elementValue)
{
// Is the array too small to contain the additional elementValue?
if (elementValues.length <= elementValuesCount)
{
// Create a new array and copy the elementValues into it.
ElementValue[] newElementValues = new ElementValue[elementValuesCount + 1];
System.arraycopy(elementValues, 0,
newElementValues, 0,
elementValuesCount);
elementValues = newElementValues;
}
// Append the elementValue.
elementValues[elementValuesCount] = elementValue;
return elementValues;
}
/**
* Deletes the element values with the given name from the given array of
* element values, returning the new number of element values.
*/
private int deleteElementValue(int elementValuesCount,
ElementValue[] elementValues,
String elementValueMethodName)
{
// Find the element value.
int index = findElementValue(elementValuesCount,
elementValues,
elementValueMethodName);
if (index < 0)
{
return elementValuesCount;
}
// Shift the other element values in the array.
System.arraycopy(elementValues, index + 1,
elementValues, index,
elementValuesCount - index - 1);
// Clear the last entry in the array.
elementValues[--elementValuesCount] = null;
return elementValuesCount;
}
/**
* Finds the index of the element value with the given name in the given
* array of element values.
*/
private int findElementValue(int elementValuesCount,
ElementValue[] elementValues,
String elementValueName)
{
for (int index = 0; index < elementValuesCount; index++)
{
if (elementValues[index].getMethodName(targetClass).equals(elementValueName))
{
return index;
}
}
return -1;
}
}