/**
* Created with IntelliJ IDEA.
* User: cohen-j
* Date: 20/12/12
* Time: 15:01
* To change this template use File | Settings | File Templates.
*/
package com.intellij.refactoring.genUtils;
import com.intellij.psi.*;
import com.intellij.util.IncorrectOperationException;
import java.util.*;
/**
* Copyright 2012, 2016 Université de Nantes
* Contributor : Julien Cohen (Ascola team, Univ. Nantes)
*
* 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.
*/
public class GenSubstitutionUtils {
/** Compute the anti-unifier for a list of methods.
* Return the elements of the method profile (return type or parameter types) to be generified,
* with the corresponding type variable to replace each element.
* Also modifies the dependent-substitution that says how each class instantiates the type parameter
* of its (direct?) superclass */
public static ParamSubstitution antiunify(
List<PsiMethod> lm, /* TODO : can lm be empty? */
DependentSubstitution theSubstitution, /* this parameter indicates the previously existing intanciation of type variables (from extends statements), for potential reuse. */
/* This parameter can be modified (when new type parameters have to be introduced) */
String baseNameForTypeVariables,
PsiElementFactory factory, /* this parameter is used to build new type parameters. */
Collection<String> boundNames
){
assert(lm.size() != 0);
final ParamSubstitution result = new ParamSubstitution();
// first, check the return type
final List <PsiType> returnTypes = new Vector<PsiType>();
final Map<PsiClass, PsiType> returnTypesMap = new HashMap<PsiClass, PsiType>();
for (PsiMethod m: lm){ // collect the return types
returnTypes.add(m.getReturnType());
returnTypesMap.put(m.getContainingClass(), m.getReturnType());
}
// check these types (and modify 'result')
if (!Comparison.allTypesEquals(returnTypes)) {
if (!Comparison.allObjectTypes(returnTypes))
throw new IncorrectOperationException("Cannot generify primitive type.");
// we know that we have to generify that position, but we have to check if we can reuse an existing type parameter.
PsiTypeParameter selected;
if (theSubstitution.containsValue(returnTypesMap))
selected = getKeyFor(theSubstitution, returnTypesMap);
else {
final String fresh = GenBuildUtils.buildTypeParameter(-1, baseNameForTypeVariables, boundNames);
boundNames.add(fresh);
selected = factory.createTypeParameterFromText(fresh, null);
theSubstitution.put(selected, returnTypesMap); /* the new association is saved (for potential reuse) */
}
result.put(-1, selected); // -1 represent the return type (by convention)
// TODO : return also the list of created type parameters
}
else {} /* when all the return types are equals, nothing to do. */
// second, check the types of the method parameters
List<PsiType> consideredTypes;
Map<PsiClass, PsiType> consideredTypesMap;
for (int pos=0; pos<lm.get(0).getParameterList().getParametersCount(); pos++) { // for each position
consideredTypes = new Vector<PsiType>();
consideredTypesMap = new HashMap<PsiClass, PsiType>();
for (PsiMethod m: lm){ // collect the types at the considered position in the list of methods
consideredTypes.add(m.getParameterList().getParameters()[pos].getType());
consideredTypesMap.put(m.getContainingClass(), m.getParameterList().getParameters()[pos].getType());
}
// check these types (modify 'result')
if (!Comparison.allTypesEquals(consideredTypes)) {
if (Comparison.allObjectTypes(consideredTypes)) {
// we know that we have to generify that position, but we have to check if we can reuse an existing type parameter.
if(theSubstitution.containsValue(consideredTypesMap)) {
result.put(pos, getKeyFor(theSubstitution, consideredTypesMap));} // -1 represent the return type (by convention)
else {
final String fresh = GenBuildUtils.buildTypeParameter(pos, baseNameForTypeVariables, boundNames);
boundNames.add(fresh);
PsiTypeParameter t = factory.createTypeParameterFromText(fresh, null);
result.put(pos, t);
theSubstitution.put(t, consideredTypesMap);
}
}
else throw new IncorrectOperationException("Cannot generify primitive type.");
}
}
return result ;
}
static <A,B> A getKeyFor (Map <A, B> m , B v) throws Error {
for (A s: m.keySet()){
if (m.get(s).equals(v)) return s;
}
throw new Error("Internal error : key not found in map");
}
/** Compute the substitution existing before refactoring. */
public static DependentSubstitution computePreviousSub(
PsiClass superclass,
Collection<PsiClass> sisterClasses){
final DependentSubstitution result = new DependentSubstitution ();
final PsiTypeParameter[] a = superclass.getTypeParameters();
final int nbParams = a.length;
for (int i=0; i< nbParams; i++){
Map <PsiClass, PsiType> m= new HashMap <PsiClass, PsiType>();
for (PsiClass c : sisterClasses){
final PsiType[] parameters = GenBuildUtils.findReferenceToSuperclass(c, superclass).getTypeParameters();
PsiType t;
assert (i<parameters.length) ; // because we have aligned the parameters in extensions before.
t= parameters[i] ;
m.put(c, t);
}
result.put(a[i], m);
}
return result;
}
public static List<String> boundTypeNames(PsiClass c){
List<String> l = new ArrayList<String>();
l.add(c.getName());
for(PsiTypeParameter p : c.getTypeParameters()) l.add(p.getName());
for(PsiClass i : c.getAllInnerClasses()) l.add(i.getName());
return l;
}
}