/*
* 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.Constants;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ConstantPool;
import org.apache.bcel.classfile.ExceptionTable;
/**
* Patches an "Exceptions" attribute.
*
* Merges with previous.
* @author Thomas Kerber
* @version 1.0.1
*/
public class ExceptionsPatch extends AttributePatch{
private static final long serialVersionUID = -3064317345029686641L;
/**
* The indexes - in the diffs constant pool - of added exceptions.
*/
private final int[] addedIndexes;
/**
* The names of removed exceptions.
*/
private final String[] removedNames;
/**
* Creates the patch.
* @param addedIndexes The added exceptions indexes.
* @param removedIndexes The removed exceptions names.
*/
public ExceptionsPatch(int[] addedIndexes, String[] removedNames){
this.addedIndexes = addedIndexes;
this.removedNames = removedNames;
}
/**
* 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){
ExceptionTable newExceptions = null;
ExceptionTable oldExceptions = null;
for(Attribute a : old){
if(a instanceof ExceptionTable){
oldExceptions = (ExceptionTable)a;
break;
}
}
for(Attribute a : new_){
if(a instanceof ExceptionTable){
newExceptions = (ExceptionTable)a;
break;
}
}
int[] newIndexes = new int[0];
String[] newStrings = new String[0];
if(newExceptions != null){
newIndexes = newExceptions.getExceptionIndexTable();
newStrings = newExceptions.getExceptionNames();
}
int[] oldIndexes = new int[0];
String[] oldStrings = new String[0];
if(oldExceptions != null){
oldIndexes = oldExceptions.getExceptionIndexTable();
oldStrings = oldExceptions.getExceptionNames();
}
List<Integer> removedIndexes = new ArrayList<Integer>();
List<Integer> addedIndexes = new ArrayList<Integer>();
// Find added
outer: for(int i = 0; i < newStrings.length; i++){
for(int f = 0; f < oldStrings.length; f++){
if(newStrings[i].equals(oldStrings[f]))
continue outer;
}
addedIndexes.add(newIndexes[i]);
}
// Find removed
outer: for(int i = 0; i < oldStrings.length; i++){
for(int f = 0; f < newStrings.length; f++){
if(newStrings[f].equals(oldStrings[i]))
continue outer;
}
removedIndexes.add(oldIndexes[i]);
}
if(addedIndexes.size() == 0 && removedIndexes.size() == 0)
return;
int[] addedInt = new int[addedIndexes.size()];
String[] removedStr = new String[removedIndexes.size()];
for(int i = 0; i < addedInt.length; i++)
addedInt[i] = addedIndexes.get(i);
ConstantPool c = null;
for(int i = 0; i < removedStr.length; i++){
if(c == null)
// Guaranteed to be set as some where apparently "removed"
// if this point is reached.
c = old[0].getConstantPool();
removedStr[i] = c.getConstantString(removedIndexes.get(i),
Constants.CONSTANT_Class);
}
patches.add(new ExceptionsPatch(addedInt, removedStr));
}
/**
*
* @return The indexes of added exceptions (in the diff constant pool).
*/
public int[] getAddedIndexes(){
return addedIndexes;
}
/**
*
* @return The names of exceptions removed by this patch.
*/
public String[] getRemovedNames(){
return removedNames;
}
@Override
public List<Attribute> patch(List<Attribute> attribs, CPoolMap map){
int[] newIndexes = new int[addedIndexes.length];
for(int i = 0; i < addedIndexes.length; i++)
newIndexes[i] = map.get(addedIndexes[i]);
int[] remIndexes = new int[removedNames.length];
for(int i = 0; i < removedNames.length; i++)
remIndexes[i] = Util.findConstantClassIn(map, removedNames[i],
true);
int index = findName(attribs, "Exceptions");
int[] oldIndexes;
if(index == -1) // None previously
oldIndexes = new int[0];
else
oldIndexes = ((ExceptionTable)attribs.get(index)).
getExceptionIndexTable();
List<Integer> indexes = new ArrayList<Integer>();
// Add old indexes not removed
outer: for(int i : oldIndexes){
for(int f : remIndexes){
if(i == f)
continue outer;
}
indexes.add(i);
}
// Add new indexes
for(int i : newIndexes)
//TODO: here and other: prevent duplicate additions.
indexes.add(i);
if(indexes.size() == 0)
return attribs;
int[] intIndexes = new int[indexes.size()];
for(int i = 0; i < intIndexes.length; i++)
intIndexes[i] = indexes.get(i);
ExceptionTable e = new ExceptionTable(
Util.findConstantStringIn(map, "Exceptions"),
2 + 2 * indexes.size(),
intIndexes,
map.to);
return replaceTypeWith(attribs, "Exceptions", e);
}
}