/*
* 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.attribs;
import java.util.ArrayList;
import java.util.List;
import milk.jpatch.CPoolMap;
import milk.jpatch.Util;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.InnerClass;
import org.apache.bcel.classfile.InnerClasses;
/**
* Patches an "InnerClasses" attribute.
*
* Merges with previous.
* @author Thomas Kerber
* @version 1.0.0
*/
public class InnerClassesPatch extends AttributePatch{
private static final long serialVersionUID = 2233953464481485933L;
/**
* The inner classes to add.
*/
private InnerClass[] innerClassesAdd;
/**
* The inner classes to remove.
*/
private InnerClass[] innerClassesRem;
/**
* Creates.
* @param innerClassesAdd The inner classes to add.
* @param innerClassesRem The inner classes to remove.
*/
public InnerClassesPatch(InnerClass[] innerClassesAdd,
InnerClass[] innerClassesRem){
this.innerClassesAdd = innerClassesAdd;
this.innerClassesRem = innerClassesRem;
}
/**
* Generates.
* @param old The old attributes.
* @param new_ The new attributes.
* @param patches The already generated patches.
*/
public static void generate(Attribute[] old, Attribute[] new_,
List<AttributePatch> patches){
InnerClass[] oldIC = new InnerClass[0];
ConstantPool oldCP = null;
InnerClass[] newIC = new InnerClass[0];
ConstantPool newCP = null;
for(Attribute a : old){
if(a instanceof InnerClasses){
oldIC = ((InnerClasses)a).getInnerClasses();
oldCP = a.getConstantPool();
}
}
for(Attribute a : new_){
if(a instanceof InnerClasses){
newIC = ((InnerClasses)a).getInnerClasses();
newCP = a.getConstantPool();
}
}
List<InnerClass> added = new ArrayList<InnerClass>();
List<InnerClass> removed = new ArrayList<InnerClass>();
// Tries finding a corresponding old one for each new one
outer: for(InnerClass nic : newIC){
for(InnerClass oic : oldIC){
if(Util.equals(nic, oic, newCP, oldCP))
continue outer;
}
added.add(nic);
}
// Aaand the opposite.
outer: for(InnerClass oic : oldIC){
for(InnerClass nic : newIC){
if(Util.equals(nic, oic, newCP, oldCP))
continue outer;
}
removed.add(oic);
}
// Nothing to patch
if(added.size() == 0 && removed.size() == 0)
return;
patches.add(new InnerClassesPatch(
added.toArray(new InnerClass[added.size()]),
removed.toArray(new InnerClass[removed.size()])));
}
/**
*
* @return The inner classes added by this patch.
*/
public InnerClass[] getAddedInnerClasses(){
return innerClassesAdd;
}
/**
*
* @return The inner classes removed by this patch.
*/
public InnerClass[] getRemovedInnerClasses(){
return innerClassesRem;
}
@Override
public List<Attribute> patch(List<Attribute> attribs, CPoolMap map){
InnerClass[] newInnerClasses = new InnerClass[innerClassesAdd.length];
for(int i = 0; i < newInnerClasses.length; i++)
newInnerClasses[i] = map.applyTo(newInnerClasses[i]);
int index = findName(attribs, "InnerClasses");
InnerClass[] oldInnerClasses;
if(index == -1)
oldInnerClasses = new InnerClass[0];
else
oldInnerClasses = ((InnerClasses)attribs.get(index)).
getInnerClasses();
List<InnerClass> innerClasses = new ArrayList<InnerClass>();
// Adds all old classes, save those removed.
outer: for(InnerClass ic : oldInnerClasses){
for(InnerClass icRem : innerClassesRem)
if(Util.equals(icRem, ic, map.to, map.to))
continue outer;
innerClasses.add(ic);
}
// Adds all added classes.
for(InnerClass ic : newInnerClasses)
innerClasses.add(ic);
if(innerClasses.size() == 0)
return attribs;
InnerClasses ic = new InnerClasses(
Util.findConstantStringIn(map, "InnerClasses"),
2 + 8 * innerClasses.size(),
innerClasses.toArray(new InnerClass[innerClasses.size()]),
map.to);
return replaceTypeWith(attribs, "InnerClasses", ic);
}
}