Mirror of the BLOAT repository https://www.cs.purdue.edu/homes/hosking/bloat/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
bloat/src/EDU/purdue/cs/bloat/editor/FieldEditor.java

562 lines
15 KiB

/**
* All files in the distribution of BLOAT (Bytecode Level Optimization and
* Analysis tool for Java(tm)) are Copyright 1997-2001 by the Purdue
* Research Foundation of Purdue University. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package EDU.purdue.cs.bloat.editor;
import java.io.*;
import EDU.purdue.cs.bloat.reflect.*;
/**
* <tt>FieldEditor</tt> provides a means to edit a field of a class. A
* <tt>FieldEditor</tt> is created from a <tt>ClassEditor</tt> and a
* <tt>reflect.FieldInfo</tt>. A <tt>FieldEditor</tt> knows its name, type
* (descriptor), and its constant value (if it has one).
*
* @see EDU.purdue.cs.bloat.reflect.FieldInfo
*
* @author Nate Nystrom (<a
* href="mailto:nystrom@cs.purdue.edu">nystrom@cs.purdue.edu</a>)
*/
public class FieldEditor {
private ClassEditor editor;
private FieldInfo fieldInfo;
private String name;
private Type type;
private Object constantValue;
private boolean isDirty;
private boolean isDeleted = false;
/**
* Creates a new <code>FieldEditor</code> for editing a field in a given
* class with the given modifiers, type and name
*
* @throws IllegalArgumentException
* If a field with the desired name already exists in the class
*/
public FieldEditor(final ClassEditor editor, final int modifiers,
final Type type, final String name) {
this(editor, modifiers, type, name, null);
}
public FieldEditor(final ClassEditor editor, final int modifiers,
final Class type, final String name, final Object constantValue) {
this(editor, modifiers, Type.getType(type), name, constantValue);
}
public FieldEditor(final ClassEditor editor, final int modifiers,
final Class type, final String name) {
this(editor, modifiers, Type.getType(type), name, null);
}
/**
* Creates a new <code>FieldEditor</code> for editing a field in a given
* class with the given modifiers, type, name, and constant value.
*
* @param modifiers
* Fields that have a constant value must be <code>static</code>
* and <code>final</code>
*
* @throws IllegalArgumentException
* If a field with the desired name already exists in the class
* or if <code>constantValue</code> is non-null and neither a
* <code>String</code>, <code>Integer</code>,
* <code>Long</code>, <code>Float</code>, nor
* <code>Double</code>.
*/
public FieldEditor(final ClassEditor editor, final int modifiers,
final Type type, final String name, final Object constantValue) {
// Does the class already have a field with this name?
final FieldInfo[] fields = editor.fields();
for (int i = 0; i < fields.length; i++) {
final FieldEditor fe = new FieldEditor(editor, fields[i]);
if (fe.name().equals(name)) {
final String s = "A field named " + name
+ " already exists in " + editor.name();
throw new IllegalArgumentException(s);
}
}
this.editor = editor;
final ConstantPool cp = editor.constants();
this.name = name;
this.type = type;
final int typeIndex = cp.getUTF8Index(this.type.descriptor());
final int nameIndex = cp.getUTF8Index(name);
final ClassInfo classInfo = editor.classInfo();
if (constantValue != null) {
// Only static final field may have constant values
if (((modifiers & Modifiers.STATIC) == 0)
|| ((modifiers & Modifiers.FINAL) == 0)) {
final String s = "Field " + name
+ " with a constant value must be static and final";
throw new IllegalArgumentException(s);
}
// Create an entry in the constant pool for the constant value
// of this field
int valueIndex;
if (constantValue instanceof String) {
if (!type.equals(Type.STRING)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getStringIndex((String) constantValue);
} else if (constantValue instanceof Integer) {
if (!type.equals(Type.INTEGER)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getIntegerIndex((Integer) constantValue);
} else if (constantValue instanceof Long) {
if (!type.equals(Type.LONG)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getLongIndex((Long) constantValue);
} else if (constantValue instanceof Float) {
if (!type.equals(Type.FLOAT)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getFloatIndex((Float) constantValue);
} else if (constantValue instanceof Double) {
if (!type.equals(Type.DOUBLE)) {
final String s = "Can't have field type of "
+ type.className() + " with a constant value of \""
+ constantValue + "\"";
throw new IllegalArgumentException(s);
}
valueIndex = cp.getDoubleIndex((Double) constantValue);
} else {
final String s = "Cannot have a constant value of type "
+ constantValue.getClass().getName();
throw new IllegalArgumentException(s);
}
this.constantValue = constantValue;
final int cvNameIndex = cp.getUTF8Index("ConstantValue");
this.fieldInfo = classInfo.addNewField(modifiers, typeIndex,
nameIndex, cvNameIndex, valueIndex);
} else {
this.fieldInfo = classInfo.addNewField(modifiers, typeIndex,
nameIndex);
}
this.isDirty = true;
}
/**
* Constructor.
*
* @param editor
* The class containing the field.
* @param fieldInfo
* The field to edit.
*
* @see ClassEditor
* @see FieldInfo
*/
public FieldEditor(final ClassEditor editor, final FieldInfo fieldInfo) {
final ConstantPool cp = editor.constants();
this.fieldInfo = fieldInfo;
this.editor = editor;
int index;
index = fieldInfo.nameIndex();
name = (String) cp.constantAt(index);
index = fieldInfo.typeIndex();
final String typeName = (String) cp.constantAt(index);
type = Type.getType(typeName);
index = fieldInfo.constantValue();
constantValue = cp.constantAt(index);
this.isDirty = false;
}
/**
* Returns the <tt>ClassEditor</tt> used to edit the class in which this
* field resides.
*/
public ClassEditor declaringClass() {
return editor;
}
/**
* Returns <tt>true</tt> if this field has been modified.
*/
public boolean isDirty() {
return (this.isDirty);
}
/**
* Sets the dirty flag of this method. The dirty flag is <tt>true</tt> if
* the method has been modified.
*
* @throws IllegalStateException This field has been marked for deletion
*/
public void setDirty(final boolean isDirty) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
this.isDirty = isDirty;
if (isDirty == true) {
this.editor.setDirty(true);
}
}
/**
* Marks this field for deletion. Once a field has been marked for deletion
* all attempts to change it will throw an
* <code>IllegalStateException</code>.
*/
public void delete() {
this.setDirty(true);
this.isDeleted = true;
}
/**
* Returns the raw FieldInfo of the field being edited.
*/
public FieldInfo fieldInfo() {
return fieldInfo;
}
public Object constantValue() {
return constantValue;
}
public boolean isPublic() {
return (fieldInfo.modifiers() & Modifiers.PUBLIC) != 0;
}
public boolean isPrivate() {
return (fieldInfo.modifiers() & Modifiers.PRIVATE) != 0;
}
public boolean isProtected() {
return (fieldInfo.modifiers() & Modifiers.PROTECTED) != 0;
}
/**
* Returns true, if the field has package level visibility.
*/
public boolean isPackage() {
return (!isPublic() && !isPrivate() && !isProtected());
}
public boolean isStatic() {
return (fieldInfo.modifiers() & Modifiers.STATIC) != 0;
}
public boolean isFinal() {
return (fieldInfo.modifiers() & Modifiers.FINAL) != 0;
}
public boolean isVolatile() {
return (fieldInfo.modifiers() & Modifiers.VOLATILE) != 0;
}
public boolean isTransient() {
return (fieldInfo.modifiers() & Modifiers.TRANSIENT) != 0;
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setPublic(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PUBLIC;
} else {
modifiers &= ~Modifiers.PUBLIC;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setPrivate(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PRIVATE;
} else {
modifiers &= ~Modifiers.PRIVATE;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setProtected(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.PROTECTED;
} else {
modifiers &= ~Modifiers.PROTECTED;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setStatic(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.STATIC;
} else {
modifiers &= ~Modifiers.STATIC;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setFinal(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.FINAL;
} else {
modifiers &= ~Modifiers.FINAL;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setTransient(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.TRANSIENT;
} else {
modifiers &= ~Modifiers.TRANSIENT;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* @throws IllegalStateException This field has been marked for deletion
*/
public void setVolatile(final boolean flag) {
if (this.isDeleted) {
final String s = "Cannot change a field once it has been marked "
+ "for deletion";
throw new IllegalStateException(s);
}
int modifiers = fieldInfo.modifiers();
if (flag) {
modifiers |= Modifiers.VOLATILE;
} else {
modifiers &= ~Modifiers.VOLATILE;
}
fieldInfo.setModifiers(modifiers);
this.setDirty(true);
}
/**
* Returns the name of the field.
*/
public String name() {
return name;
}
/**
* Returns the type of the field.
*/
public Type type() {
return type;
}
/**
* Returns a <tt>NameAndType</tt> of the field.
*/
public NameAndType nameAndType() {
return (new NameAndType(this.name(), this.type()));
}
/**
* Returns a <code>MemberRef</code> for the field
*/
public MemberRef memberRef() {
return (new MemberRef(this.declaringClass().type(), this.nameAndType()));
}
/**
* Commit changes to the field back to the ClassEditor. Note that the field
* is committed regardless of whether or not it is dirty.
*/
public void commit() {
if (this.isDeleted) {
// Even if the field is newly-added, we can still delete it
// without problems because its FieldInfo was already noted with
// the ClassInfo.
final ConstantPool cp = editor.constants();
final int nameIndex = cp.getUTF8Index(name);
this.editor.classInfo().deleteField(nameIndex);
} else {
final ConstantPool cp = editor.constants();
fieldInfo.setNameIndex(cp.addConstant(Constant.UTF8, name));
fieldInfo.setTypeIndex(cp.addConstant(Constant.UTF8, type
.descriptor()));
if (constantValue != null) {
if (constantValue instanceof Long) {
fieldInfo.setConstantValue(cp.addConstant(Constant.LONG,
constantValue));
} else if (constantValue instanceof Float) {
fieldInfo.setConstantValue(cp.addConstant(Constant.FLOAT,
constantValue));
} else if (constantValue instanceof Double) {
fieldInfo.setConstantValue(cp.addConstant(Constant.DOUBLE,
constantValue));
} else if (constantValue instanceof Integer) {
fieldInfo.setConstantValue(cp.addConstant(Constant.INTEGER,
constantValue));
} else if (constantValue instanceof String) {
fieldInfo.setConstantValue(cp.addConstant(Constant.STRING,
constantValue));
}
}
}
// This field is no longer dirty
this.isDirty = false;
}
/**
* Print the field.
*
* @param out
* Stream to which to print.
*/
public void print(final PrintStream out) {
out.println("field " + name + " " + type);
}
/**
* Returns a String that contains the declaring class name and the name of
* the field
*/
public String fullName() {
return declaringClass().name() + "." + this.name();
}
public String toString() {
return ("[FieldEditor for " + this.name + this.type + "]");
}
}