/*
* Copyright 2012, Thomas Kerber
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package milk.jpatch.code;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.Code;
import milk.jpatch.CPoolMap;
import milk.jpatch.Util;
import milk.jpatch.attribs.AttributePatch;
import milk.jpatch.attribs.AttributesPatch;
/**
* A patch of a "Code" attribute.
* @author Thomas Kerber
* @version 1.0.0
*/
public abstract class CodePatch extends AttributePatch{
private static final long serialVersionUID = 2649429120448088550L;
/**
* Hooks for the old code points.
*/
protected List<CodePointHook> oldCPs = new ArrayList<CodePointHook>();
/**
* Hooks for the new code points.
*/
protected List<CodePointHook> newCPs = new ArrayList<CodePointHook>();
/**
* The component attributes patch.
*/
protected AttributesPatch attributesPatch;
/**
* Adds to the old code point hooks.
* @param cp The hook to add.
*/
public void addOldCP(CodePointHook cp){
oldCPs.add(cp);
}
/**
* Adds to the new code point hooks.
* @param cp The hook to add.
*/
public void addNewCP(CodePointHook cp){
newCPs.add(cp);
}
/**
* Creates
* @param attributesPatch The component attribute patch.
*/
protected CodePatch(AttributesPatch attributesPatch){
this.attributesPatch = attributesPatch;
}
/**
* Generates.
* @param old The old attributes.
* @param new_ The new attributes.
* @param patches The already applied attribute patches.
* @param map The cpool map.
*/
public static void generate(Attribute[] old, Attribute[] new_,
List<AttributePatch> patches, CPoolMap map){
Code oldCode = null;
for(Attribute a : old){
if(a instanceof Code){
oldCode = (Code)a;
break;
}
}
Code newCode = null;
for(Attribute a : new_){
if(a instanceof Code){
newCode = (Code)a;
break;
}
}
if(newCode == null)
return;
patches.add(generate(oldCode, newCode, map));
}
/**
* Generates.
* @param old The old code.
* @param new_ The new code.
* @param map The cpool map.
* @return The patch.
*/
public static CodePatch generate(Code old, Code new_, CPoolMap map){
// AttributesPatch
AttributesPatch attribPatch = AttributesPatch.generate(
old == null ? new Attribute[0] : old.getAttributes(),
new_.getAttributes(), map);
// TODO: better code patches.
if(old == null)
return new CodeReplace(attribPatch, new_);
if(Util.equals(old, new_, map))
return new CodeID(attribPatch);
return new CodeReplace(attribPatch, new_);
}
@Override
public List<Attribute> patch(List<Attribute> attribs, CPoolMap map){
for(int i = 0; i < attribs.size(); i++){
if(attribs.get(i) instanceof Code){
Code newCode = this.patch((Code)attribs.get(i), map);
// Aaand apply attribs patch...
List<Attribute> codeAttribs = new ArrayList<Attribute>(
Arrays.asList(newCode.getAttributes()));
codeAttribs = attributesPatch.patch(codeAttribs, map);
newCode.setAttributes(codeAttribs.toArray(
new Attribute[codeAttribs.size()]));
// Aaand add and return.
attribs.set(i, newCode);
return attribs;
}
}
throw new IllegalStateException("Cannot patch nonexistant code!");
}
public abstract Code patch(Code c, CPoolMap map);
}