/*
* 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.AnnotationEntry;
import org.apache.bcel.classfile.Attribute;
import org.apache.bcel.classfile.ParameterAnnotationEntry;
import org.apache.bcel.classfile.ParameterAnnotations;
import org.apache.bcel.classfile.RuntimeInvisibleParameterAnnotations;
import org.apache.bcel.classfile.RuntimeVisibleParameterAnnotations;
/**
* Patches a "RuntimeVisibleParameterAnnotations" or
* "RuntimeInvisibleParameterAnnotations" attribute.
*
* Merges with previous.
* @author Thomas Kerber
* @version 1.0.0
*/
public class ParameterAnnotationsPatch extends AttributePatch{
private static final long serialVersionUID = -7407590930928616809L;
/**
* The annotations to add.
*
* (First dimension: parameter no.)
*/
private final AnnotationEntry[][] annotsAdd;
/**
* The annotations to remove.
*
* (First dimension: parameter no.)
*/
private final AnnotationEntry[][] annotsRem;
/**
* Whether or not the patch describes runtime annotations.
*/
public final boolean isRuntime;
/**
* Creates.
* @param annotsAdd The annotations to add.
* (First dimension: parameter no.)
* @param annotsRem The annotations to remove.
* (First dimension: parameter no.)
* @param isRuntime Whether or not runtime annotations are being
* describled.
*/
public ParameterAnnotationsPatch(AnnotationEntry[][] annotsAdd,
AnnotationEntry[][] annotsRem, boolean isRuntime){
this.annotsAdd = annotsAdd;
this.annotsRem = annotsRem;
this.isRuntime = isRuntime;
}
/**
* 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){
for(boolean isRuntime : new boolean[]{true, false}){
// Find old Annotations
ParameterAnnotations oldAnnots = null;
for(int i = 0; i < old.length; i++){
boolean correct = false;
correct |= isRuntime &&
old[i] instanceof RuntimeVisibleParameterAnnotations;
correct |= !isRuntime &&
old[i] instanceof RuntimeInvisibleParameterAnnotations;
if(correct){
oldAnnots = (ParameterAnnotations)old[i];
break;
}
}
//Find new Annotations
ParameterAnnotations newAnnots = null;
for(int i = 0; i < new_.length; i++){
boolean correct = false;
correct |= isRuntime &&
new_[i] instanceof RuntimeVisibleParameterAnnotations;
correct |= !isRuntime &&
new_[i] instanceof
RuntimeInvisibleParameterAnnotations;
if(correct){
newAnnots = (ParameterAnnotations)new_[i];
break;
}
}
ParameterAnnotationEntry[] oldEntries = oldAnnots == null ?
new ParameterAnnotationEntry[0] :
oldAnnots.getParameterAnnotationEntries();
ParameterAnnotationEntry[] newEntries = newAnnots == null ?
new ParameterAnnotationEntry[0] :
newAnnots.getParameterAnnotationEntries();
int paes = oldEntries.length;
if(newEntries.length > paes)
paes = newEntries.length;
// Finds entries which are new.
AnnotationEntry[][] addedEntries = new AnnotationEntry[paes][];
AnnotationEntry[][] removedEntries = new AnnotationEntry[paes][];
for(int i = 0; i < paes; i++){
AnnotationEntry[] oldAes = oldEntries.length > i ?
oldEntries[i].getAnnotationEntries() :
new AnnotationEntry[0];
AnnotationEntry[] newAes = newEntries.length > i ?
newEntries[i].getAnnotationEntries() :
new AnnotationEntry[0];
List<AnnotationEntry> addedAes =
new ArrayList<AnnotationEntry>();
List<AnnotationEntry> removedAes =
new ArrayList<AnnotationEntry>();
// Find added entries
outer: for(AnnotationEntry newAe : newAes){
for(AnnotationEntry oldAe : oldAes){
if(Util.equals(newAe, oldAe))
continue outer;
}
addedAes.add(newAe);
}
// Find removed entries
outer: for(AnnotationEntry oldAe : oldAes){
for(AnnotationEntry newAe : newAes){
if(Util.equals(newAe, oldAe))
continue outer;
}
removedAes.add(oldAe);
}
addedEntries[i] = addedAes.toArray(
new AnnotationEntry[addedAes.size()]);
removedEntries[i] = removedAes.toArray(
new AnnotationEntry[removedAes.size()]);
}
patches.add(new ParameterAnnotationsPatch(
addedEntries,
removedEntries,
isRuntime));
}
}
@Override
public List<Attribute> patch(List<Attribute> attribs, CPoolMap map){
// Get exisiting annotations...
AnnotationEntry[][] newAnnots =
new AnnotationEntry[annotsAdd.length][];
for(int i = 0; i < newAnnots.length; i++){
newAnnots[i] = new AnnotationEntry[annotsAdd[i].length];
for(int f = 0; f < newAnnots[i].length; f++)
newAnnots[i][f] = map.applyTo(annotsAdd[i][f]);
}
AnnotationEntry[][] remAnnots =
new AnnotationEntry[annotsRem.length][];
for(int i = 0; i < remAnnots.length; i++){
remAnnots[i] = new AnnotationEntry[annotsRem[i].length];
for(int f = 0; f < remAnnots[i].length; f++)
remAnnots[i][f] = map.applyTo(annotsRem[i][f]);
}
int index = findName(
attribs,
isRuntime ?
"RuntimeVisibleParameterAnnotations" :
"RuntimeInvisibleParameterAnnotations");
AnnotationEntry[][] oldAnnots;
if(index == -1)
oldAnnots = new AnnotationEntry[0][0];
else{
ParameterAnnotations pa = (ParameterAnnotations)attribs.get(index);
ParameterAnnotationEntry[] paes =
pa.getParameterAnnotationEntries();
oldAnnots = new AnnotationEntry[pa.getNumParameterAnnotation()][];
for(int i = 0; i < oldAnnots.length; i++)
oldAnnots[i] = paes[i].getAnnotationEntries();
}
int num_paes = newAnnots.length;
if(oldAnnots.length > num_paes)
num_paes = oldAnnots.length;
// Unify...
List<ParameterAnnotationEntry> paes =
new ArrayList<ParameterAnnotationEntry>();
for(int i = 0; i < num_paes; i++){
List<AnnotationEntry> aes = new ArrayList<AnnotationEntry>();
// Add old annotations.
if(oldAnnots.length > i){
entries: for(AnnotationEntry oldAe : oldAnnots[i]){
if(remAnnots.length > i){
for(AnnotationEntry remAe : remAnnots[i]){
if(Util.equals(oldAe, remAe))
continue entries;
}
}
aes.add(oldAe);
}
}
// Add new annotations
if(newAnnots.length > i){
for(AnnotationEntry newAe : newAnnots[i])
aes.add(newAe);
}
// Pack into ParameterAnnotationEntry
ParameterAnnotationEntry pae = new ParameterAnnotationEntry(
aes.toArray(new AnnotationEntry[aes.size()]));
paes.add(pae);
}
// Get length
boolean nonEmptyFound = false;
int length = 1; // num_parameters
for(int i = 0; i < paes.size(); i++){
length += 2; // num_annotations
for(AnnotationEntry e : paes.get(i).getAnnotationEntries()){
length += Util.getLength(e);
nonEmptyFound = true;
}
}
if(!nonEmptyFound)
return attribs;
// Pack into ParamterAnnotations
ParameterAnnotations pa;
if(isRuntime)
pa = new RuntimeVisibleParameterAnnotations(
Util.findConstantStringIn(map,
"RuntimeVisibleParameterAnnotations"),
length,
paes.toArray(new ParameterAnnotationEntry[paes.size()]),
map.to);
else
pa = new RuntimeInvisibleParameterAnnotations(
Util.findConstantStringIn(map,
"RuntimeInvisibleParameterAnnotations"),
length,
paes.toArray(new ParameterAnnotationEntry[paes.size()]),
map.to);
return replaceTypeWith(
attribs,
isRuntime ?
"RuntimeVisibleParameterAnnotations" :
"RuntimeInvisibleParameterAnnotions",
pa);
}
}