blob: f09cec19a2f64d722ae9fbb252a8b018e2f2ed9b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.netbeans.modules.refactoring.java.plugins;
import com.sun.source.tree.*;
import com.sun.source.util.TreePath;
import org.netbeans.api.java.source.support.ErrorAwareTreeScanner;
import com.sun.source.util.Trees;
import java.io.IOException;
import java.util.*;
import javax.lang.model.element.*;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Types;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.source.*;
import org.netbeans.modules.refactoring.api.AbstractRefactoring;
import org.netbeans.modules.refactoring.api.Problem;
import org.netbeans.modules.refactoring.java.RefactoringUtils;
import org.netbeans.modules.refactoring.java.api.ExtractSuperclassRefactoring;
import org.netbeans.modules.refactoring.java.api.JavaRefactoringUtils;
import org.netbeans.modules.refactoring.java.api.MemberInfo;
import org.netbeans.modules.refactoring.java.spi.JavaRefactoringPlugin;
import org.netbeans.modules.refactoring.java.spi.RefactoringVisitor;
import org.netbeans.modules.refactoring.spi.RefactoringElementsBag;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
/** Plugin that implements the core functionality of Extract Super Class refactoring.
*
* @author Martin Matula, Jan Pokorsky
*/
public final class ExtractSuperclassRefactoringPlugin extends JavaRefactoringPlugin {
/** Reference to the parent refactoring instance */
private final ExtractSuperclassRefactoring refactoring;
/** source class */
private ElementHandle<TypeElement> classHandle;
private String pkgName;
/** Creates a new instance of ExtractSuperClassRefactoringPlugin
* @param refactoring Parent refactoring instance.
*/
ExtractSuperclassRefactoringPlugin(ExtractSuperclassRefactoring refactoring) {
this.refactoring = refactoring;
}
@Override
protected JavaSource getJavaSource(Phase p) {
return JavaSource.forFileObject(refactoring.getSourceType().getFileObject());
}
@Override
protected Problem preCheck(CompilationController javac) throws IOException {
// fire operation start on the registered progress listeners (2 step)
fireProgressListenerStart(AbstractRefactoring.PRE_CHECK, 2);
javac.toPhase(JavaSource.Phase.RESOLVED);
try {
TreePathHandle sourceType = refactoring.getSourceType();
// check whether the element is valid
Problem result = isElementAvail(sourceType, javac);
if (result != null) {
// fatal error -> don't continue with further checks
return result;
}
// check whether the element is an unresolved class
Element sourceElm = sourceType.resolveElement(javac);
result = JavaPluginUtils.isSourceElement(sourceElm, javac);
if (result != null) {
return result;
}
if (sourceElm == null) {
// fatal error -> return
return new Problem(true, NbBundle.getMessage(ExtractSuperclassRefactoringPlugin.class, "ERR_ElementNotAvailable")); // NOI18N
}
if (sourceElm.getKind() != ElementKind.CLASS) {
return new Problem(true, NbBundle.getMessage(ExtractSuperclassRefactoringPlugin.class, "ERR_ExtractSC_MustBeClass"));
}
classHandle = ElementHandle.<TypeElement>create((TypeElement) sourceElm);
PackageElement pkgElm = (PackageElement) javac.getElementUtilities().outermostTypeElement(sourceElm).getEnclosingElement();
pkgName = pkgElm.getQualifiedName().toString();
// increase progress (step 1)
fireProgressListenerStep();
// all checks passed -> return null
return null;
} finally {
// fire operation end on the registered progress listeners
fireProgressListenerStop();
}
}
@Override
public Problem fastCheckParameters() {
Problem result = null;
String newName = refactoring.getSuperClassName();
if (!Utilities.isJavaIdentifier(newName)) {
result = createProblem(result, true, NbBundle.getMessage(ExtractSuperclassRefactoringPlugin.class, "ERR_InvalidIdentifier", newName)); // NOI18N
return result;
}
FileObject primFile = refactoring.getSourceType().getFileObject();
FileObject folder = primFile.getParent();
FileObject[] children = folder.getChildren();
for (FileObject child: children) {
if (!child.isVirtual() && child.getName().equals(newName) && "java".equals(child.getExt())) { // NOI18N
result = createProblem(result, true, NbBundle.getMessage(ExtractSuperclassRefactoringPlugin.class, "ERR_ClassClash", newName, pkgName)); // NOI18N
return result;
}
}
return super.fastCheckParameters();
}
@Override
protected Problem fastCheckParameters(CompilationController javac) throws IOException {
Problem result = null;
String newName = refactoring.getSuperClassName();
TypeMirror parsedType = javac.getTreeUtilities().parseType(newName, classHandle.resolve(javac));
if(parsedType != null && parsedType.getKind() != TypeKind.ERROR) {
result = createProblem(result, true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ClassClash", newName, pkgName)); // NOI18N
return result;
}
return super.fastCheckParameters(javac);
}
@Override
public Problem checkParameters() {
MemberInfo[] members = refactoring.getMembers();
if (members.length == 0) {
return new Problem(true, NbBundle.getMessage(ExtractSuperclassRefactoringPlugin.class, "ERR_ExtractSuperClass_MembersNotAvailable")); // NOI18N);
}
return super.checkParameters();
}
@Override
protected Problem checkParameters(CompilationController javac) throws IOException {
javac.toPhase(JavaSource.Phase.RESOLVED);
TypeElement sourceType = (TypeElement) refactoring.getSourceType().resolveElement(javac);
assert sourceType != null;
Set<? extends Element> members = new HashSet<Element>(sourceType.getEnclosedElements());
fireProgressListenerStart(AbstractRefactoring.PARAMETERS_CHECK, refactoring.getMembers().length);
try {
for (MemberInfo info : refactoring.getMembers()) {
Problem p = null;
switch(info.getGroup()) {
case FIELD:
@SuppressWarnings("unchecked")
ElementHandle<VariableElement> vehandle = (ElementHandle<VariableElement>) info.getElementHandle();
VariableElement field = vehandle.resolve(javac);
p = checkFieldParameter(javac, field, members);
break;
case METHOD:
@SuppressWarnings("unchecked")
ElementHandle<ExecutableElement> eehandle = (ElementHandle<ExecutableElement>) info.getElementHandle();
ExecutableElement method = eehandle.resolve(javac);
p = checkMethodParameter(javac, method, members);
break;
}
if (p != null) {
return p;
}
fireProgressListenerStep();
}
} finally {
fireProgressListenerStop();
}
// XXX check refactoring.getImplements()
return null;
}
private Problem checkFieldParameter(CompilationController javac, VariableElement elm, Set<? extends Element> members) throws IOException {
if (elm == null) {
return new Problem(true, NbBundle.getMessage(ExtractSuperclassRefactoringPlugin.class, "ERR_ElementNotAvailable")); // NOI18N
}
if (javac.getElementUtilities().isSynthetic(elm) || elm.getKind() != ElementKind.FIELD) {
return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractSuperClass_UnknownMember", // NOI18N
elm.toString()));
}
if (!members.contains(elm)) {
return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractSuperClass_UnknownMember", // NOI18N
elm.toString()));
}
// Set<Modifier> mods = elm.getModifiers();
// if (mods.contains(Modifier.PUBLIC) && mods.contains(Modifier.STATIC) && mods.contains(Modifier.FINAL)) {
// VariableTree tree = (VariableTree) javac.getTrees().getTree(elm);
// if (tree.getInitializer() != null) {
// continue;
// }
// }
// return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractInterface_WrongModifiers", elm.getSimpleName().toString())); // NOI18N
return null;
}
private Problem checkMethodParameter(CompilationController javac, ExecutableElement elm, Set<? extends Element> members) throws IOException {
if (elm == null) {
return new Problem(true, NbBundle.getMessage(ExtractSuperclassRefactoringPlugin.class, "ERR_ElementNotAvailable")); // NOI18N
}
if (javac.getElementUtilities().isSynthetic(elm) || elm.getKind() != ElementKind.METHOD) {
return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractSuperClass_UnknownMember", // NOI18N
elm.toString()));
}
if (!members.contains(elm)) {
return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractSuperClass_UnknownMember", // NOI18N
elm.toString()));
}
// Set<Modifier> mods = elm.getModifiers();
// if (!mods.contains(Modifier.PUBLIC) || mods.contains(Modifier.STATIC)) {
// return new Problem(true, NbBundle.getMessage(ExtractInterfaceRefactoringPlugin.class, "ERR_ExtractInterface_WrongModifiers", elm.getSimpleName().toString())); // NOI18N
// }
return null;
}
private Set<FileObject> getRelevantFiles() {
final Set<FileObject> set = new HashSet<FileObject>();
set.add(refactoring.getSourceType().getFileObject());
return set;
}
private ClasspathInfo getClasspathInfo(Set<FileObject> a) {
ClasspathInfo cpInfo;
cpInfo = JavaRefactoringUtils.getClasspathInfoFor(a.toArray(new FileObject[a.size()]));
return cpInfo;
}
@Override
public Problem prepare(RefactoringElementsBag bag) {
RefactoringVisitor visitor = new ExtractSuperclassTransformer(refactoring, classHandle);
Set<FileObject> a = getRelevantFiles();
fireProgressListenerStart(AbstractRefactoring.PREPARE, a.size());
TransformTask transform = new TransformTask(visitor, refactoring.getSourceType());
Problem problem = createAndAddElements(a, transform, bag, refactoring, getClasspathInfo(a));
fireProgressListenerStop();
return problem;
}
private final static class ExtractSuperclassTransformer extends RefactoringVisitor {
private final ExtractSuperclassRefactoring refactoring;
private final ElementHandle<TypeElement> sourceType;
private List<Tree> members;
private boolean makeAbstract;
private List<Tree> members2Remove;
private ExtractSuperclassTransformer(ExtractSuperclassRefactoring refactoring, ElementHandle<TypeElement> sourceType) {
this.sourceType = sourceType;
this.refactoring = refactoring;
}
@Override
public Tree visitClass(ClassTree classTree, Element p) {
TypeElement clazz = this.sourceType.resolve(workingCopy);
assert clazz != null;
Element current = workingCopy.getTrees().getElement(getCurrentPath());
if(current == clazz) {
GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy);
makeAbstract = false;
members = new LinkedList<Tree>();
members2Remove = new LinkedList<Tree>();
addConstructors(clazz);
super.visitClass(classTree, p);
List<Tree> implementsList = new ArrayList<Tree>();
for (MemberInfo/*<ElementHandle<? extends Element>>*/ member : refactoring.getMembers()) {
if (member.getGroup() == MemberInfo.Group.IMPLEMENTS) {
TypeMirrorHandle handle = (TypeMirrorHandle) member.getElementHandle();
// XXX check if interface is not aready there; the templates might be changed by user :-(
TypeMirror implMirror = handle.resolve(workingCopy);
implementsList.add(make.Type(implMirror));
// XXX needs more granular check
makeAbstract |= true;
}
}
DeclaredType supType = (DeclaredType) clazz.getSuperclass();
TypeElement supEl = (TypeElement) supType.asElement();
Tree superClass = supEl.getSuperclass().getKind() == TypeKind.NONE
? null
: make.Type(supType);
makeAbstract |= supEl.getModifiers().contains(Modifier.ABSTRACT);
ModifiersTree classModifiersTree = make.Modifiers(makeAbstract ? EnumSet.of(Modifier.PUBLIC, Modifier.ABSTRACT) : EnumSet.of(Modifier.PUBLIC));
final List<? extends TypeMirror> typeParams = findUsedGenericTypes(clazz);
List<TypeParameterTree> newTypeParams = new ArrayList<TypeParameterTree>(typeParams.size());
for (TypeParameterElement typeParam : clazz.getTypeParameters()) {
TypeMirror origParam = typeParam.asType();
for (TypeMirror newParam : typeParams) {
if (workingCopy.getTypes().isSameType(origParam, newParam)) {
Tree t = workingCopy.getTrees().getTree(typeParam);
if (t.getKind() == Tree.Kind.TYPE_PARAMETER) {
TypeParameterTree typeParamTree = (TypeParameterTree) t;
if (!typeParamTree.getBounds().isEmpty()) {
typeParamTree = (TypeParameterTree) genUtils.importFQNs(t);
}
newTypeParams.add(typeParamTree);
}
}
}
}
ClassTree newClassTree = make.Class(
classModifiersTree,
refactoring.getSuperClassName(),
newTypeParams,
superClass,
implementsList,
Collections.<Tree>emptyList());
newClassTree = GeneratorUtilities.get(workingCopy).insertClassMembers(newClassTree, members);
FileObject fileObject = refactoring.getSourceType().getFileObject();
FileObject sourceRoot = ClassPath.getClassPath(fileObject, ClassPath.SOURCE).findOwnerRoot(fileObject);
String relativePath = FileUtil.getRelativePath(sourceRoot, fileObject.getParent()) + "/" + refactoring.getSuperClassName() + ".java";
CompilationUnitTree cu = JavaPluginUtils.createCompilationUnit(sourceRoot, relativePath, newClassTree, workingCopy, make);
rewrite(null, cu);
// fake interface since interface file does not exist yet
Tree superClassTree;
if (typeParams.isEmpty()) {
superClassTree = make.Identifier(refactoring.getSuperClassName());
} else {
List<ExpressionTree> typeParamTrees = new ArrayList<ExpressionTree>(typeParams.size());
for (TypeMirror typeParam : typeParams) {
Tree t = make.Type(typeParam);
typeParamTrees.add((ExpressionTree) t);
}
superClassTree = make.ParameterizedType(
make.Identifier(refactoring.getSuperClassName()),
typeParamTrees
);
}
Set<Tree> interfaces2Remove = new HashSet<Tree>();
interfaces2Remove.addAll(getImplements2Remove(workingCopy, refactoring.getMembers(), clazz));
// filter out obsolete members
List<Tree> newMembers = new ArrayList<Tree>();
for (Tree tree : classTree.getMembers()) {
if (!members2Remove.contains(tree)) {
newMembers.add(tree);
}
}
// filter out obsolete implements trees
List<Tree> newImpls = resolveImplements(classTree.getImplementsClause(), interfaces2Remove);
ClassTree nc;
nc = make.Class(
classTree.getModifiers(),
classTree.getSimpleName(),
classTree.getTypeParameters(),
superClassTree,
newImpls,
newMembers);
rewrite(classTree, nc);
return classTree;
}
return super.visitClass(classTree, p);
}
@Override
public Tree visitVariable(VariableTree variableTree, Element p) {
Element current = workingCopy.getTrees().getElement(getCurrentPath());
if (current != null) {
for (MemberInfo<ElementHandle<? extends Element>> memberInfo : refactoring.getMembers()) {
if (memberInfo.getGroup() == MemberInfo.Group.FIELD
&& memberInfo.getElementHandle().resolve(workingCopy) == current) {
GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy);
members2Remove.add(variableTree);
VariableTree copy = genUtils.importComments(variableTree, workingCopy.getCompilationUnit());
copy = genUtils.importFQNs(copy);
ModifiersTree modifiers = copy.getModifiers();
if (modifiers.getFlags().contains(Modifier.PRIVATE)) {
modifiers = make.removeModifiersModifier(modifiers, Modifier.PRIVATE);
modifiers = make.addModifiersModifier(modifiers, Modifier.PROTECTED);
copy = make.Variable(modifiers, copy.getName(), copy.getType(), copy.getInitializer());
genUtils.copyComments(variableTree, copy, false);
genUtils.copyComments(variableTree, copy, true);
}
members.add(copy);
break;
}
}
}
return variableTree;
}
@Override
public Tree visitMethod(final MethodTree methodTree, Element p) {
final Trees trees = workingCopy.getTrees();
Element current = trees.getElement(getCurrentPath());
if (current != null) {
for (MemberInfo<ElementHandle<? extends Element>> memberInfo : refactoring.getMembers()) {
if (memberInfo.getGroup() == MemberInfo.Group.METHOD
&& memberInfo.getElementHandle().resolve(workingCopy) == current) {
if(!memberInfo.isMakeAbstract()) {
members2Remove.add(methodTree);
}
GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy);
MethodTree newMethod = genUtils.importComments(methodTree, workingCopy.getCompilationUnit());
ModifiersTree modifiers = methodTree.getModifiers();
if (modifiers.getFlags().contains(Modifier.PRIVATE)) {
modifiers = make.removeModifiersModifier(modifiers, Modifier.PRIVATE);
modifiers = make.addModifiersModifier(modifiers, Modifier.PROTECTED);
}
newMethod = genUtils.importFQNs(newMethod);
modifiers = genUtils.importFQNs(modifiers);
final List<? extends TypeMirror> thrownTypes = ((ExecutableElement)current).getThrownTypes();
List<ExpressionTree> newThrownTypes = new ArrayList<ExpressionTree>(thrownTypes.size());
for (TypeMirror typeMirror : thrownTypes) {
newThrownTypes.add((ExpressionTree) make.Type(typeMirror)); // Necessary as this is not covered by importFQNs
}
if (memberInfo.isMakeAbstract() && !current.getModifiers().contains(Modifier.ABSTRACT)) {
newMethod = make.Method(
RefactoringUtils.makeAbstract(make, modifiers),
newMethod.getName(),
newMethod.getReturnType(),
newMethod.getTypeParameters(),
newMethod.getParameters(),
newThrownTypes,
(BlockTree) null,
null);
} else {
newMethod = make.Method(modifiers,
newMethod.getName(),
newMethod.getReturnType(),
newMethod.getTypeParameters(),
newMethod.getParameters(),
newThrownTypes,
newMethod.getBody(),
(ExpressionTree) newMethod.getDefaultValue());
}
genUtils.copyComments(methodTree, newMethod, false);
genUtils.copyComments(methodTree, newMethod, true);
makeAbstract |= newMethod.getModifiers().getFlags().contains(Modifier.ABSTRACT);
members.add(newMethod);
break;
}
}
}
return methodTree;
}
private List<Tree> getImplements2Remove(CompilationInfo javac,MemberInfo[] members, TypeElement clazz) {
if (members == null || members.length == 0) {
return Collections.<Tree>emptyList();
}
// resolve members to remove
List<TypeMirror> memberTypes = new ArrayList<TypeMirror>(members.length);
for (MemberInfo member : members) {
if (member.getGroup() == MemberInfo.Group.IMPLEMENTS) {
TypeMirrorHandle handle = (TypeMirrorHandle) member.getElementHandle();
TypeMirror tm = handle.resolve(javac);
memberTypes.add(tm);
}
}
ClassTree classTree = javac.getTrees().getTree(clazz);
List<Tree> result = new ArrayList<Tree>();
Types types = javac.getTypes();
// map TypeMirror to Tree
for (Tree tree : classTree.getImplementsClause()) {
TreePath path = javac.getTrees().getPath(javac.getCompilationUnit(), tree);
TypeMirror existingTM = javac.getTrees().getTypeMirror(path);
for (TypeMirror tm : memberTypes) {
if (types.isSameType(tm, existingTM)) {
result.add(tree);
break;
}
}
}
return result;
}
private static List<Tree> resolveImplements(List<? extends Tree> allImpls, Set<Tree> impls2Remove) {
List<Tree> ret;
if (allImpls == null) {
ret = new ArrayList<Tree>(1);
} else {
ret = new ArrayList<Tree>(allImpls.size() + 1);
ret.addAll(allImpls);
}
if (impls2Remove != null && !impls2Remove.isEmpty()) {
ret.removeAll(impls2Remove);
}
return ret;
}
private List<TypeMirror> findUsedGenericTypes(TypeElement javaClass) {
List<TypeMirror> typeArgs = JavaRefactoringUtils.elementsToTypes(javaClass.getTypeParameters());
if (typeArgs.isEmpty()) {
return typeArgs;
}
Types typeUtils = workingCopy.getTypes();
Set<TypeMirror> used = Collections.newSetFromMap(new IdentityHashMap<TypeMirror, Boolean>());
// check super class
TypeMirror superClass = javaClass.getSuperclass();
RefactoringUtils.findUsedGenericTypes(typeUtils, typeArgs, used, superClass);
MemberInfo[] members = refactoring.getMembers();
for (int i = 0; i < members.length && !typeArgs.isEmpty(); i++) {
if (members[i].getGroup() == MemberInfo.Group.METHOD) {
// check methods
@SuppressWarnings("unchecked")
ElementHandle<ExecutableElement> handle = (ElementHandle<ExecutableElement>) members[i].getElementHandle();
ExecutableElement elm = handle.resolve(workingCopy);
RefactoringUtils.findUsedGenericTypes(typeUtils, typeArgs, used, elm.getReturnType());
for (Iterator<? extends VariableElement> paramIter = elm.getParameters().iterator(); paramIter.hasNext() && !typeArgs.isEmpty();) {
VariableElement param = paramIter.next();
RefactoringUtils.findUsedGenericTypes(typeUtils, typeArgs, used, param.asType());
}
} else if (members[i].getGroup() == MemberInfo.Group.FIELD) {
if (members[i].getModifiers().contains(Modifier.STATIC)) {
// do not check since static fields cannot use type parameter of the enclosing class
continue;
}
@SuppressWarnings("unchecked")
ElementHandle<VariableElement> handle = (ElementHandle<VariableElement>) members[i].getElementHandle();
VariableElement elm = handle.resolve(workingCopy);
TypeMirror asType = elm.asType();
RefactoringUtils.findUsedGenericTypes(typeUtils, typeArgs, used, asType);
} else if (members[i].getGroup() == MemberInfo.Group.IMPLEMENTS) {
// check implements
TypeMirrorHandle handle = (TypeMirrorHandle) members[i].getElementHandle();
TypeMirror implemetz = handle.resolve(workingCopy);
RefactoringUtils.findUsedGenericTypes(typeUtils, typeArgs, used, implemetz);
}
// do not check fields since static fields cannot use type parameter of the enclosing class
}
return RefactoringUtils.filterTypes(typeArgs, used);
}
// --- helper methods ----------------------------------
/* in case there are constructors delegating to old superclass it is necessery to create delegates in new superclass */
private void addConstructors(final TypeElement origClass) {
final GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy);
// cache of already resolved constructors
final Set<Element> added = new HashSet<Element>();
for (ExecutableElement constr : ElementFilter.constructorsIn(origClass.getEnclosedElements())) {
if (workingCopy.getElementUtilities().isSynthetic(constr)) {
continue;
}
TreePath path = workingCopy.getTrees().getPath(constr);
MethodTree mc = (MethodTree) (path != null? path.getLeaf(): null);
if (mc != null) {
for (StatementTree stmt : mc.getBody().getStatements()) {
// search super(...); statement
if (stmt.getKind() == Tree.Kind.EXPRESSION_STATEMENT) {
ExpressionStatementTree estmt = (ExpressionStatementTree) stmt;
boolean isSyntheticSuper = workingCopy.getTreeUtilities().isSynthetic(workingCopy.getTrees().getPath(path.getCompilationUnit(), estmt));
ExpressionTree expr = estmt.getExpression();
TreePath expath = workingCopy.getTrees().getPath(path.getCompilationUnit(), expr);
Element el = workingCopy.getTrees().getElement(expath);
if (el != null && el.getKind() == ElementKind.CONSTRUCTOR && added.add(el)) {
ExecutableElement superclassConstr = (ExecutableElement) el;
MethodInvocationTree invk = (MethodInvocationTree) expr;
// create constructor block with super call
BlockTree block = isSyntheticSuper
? make.Block(Collections.<StatementTree>emptyList(), false)
: make.Block(Collections.<StatementTree>singletonList(
make.ExpressionStatement(
make.MethodInvocation(
Collections.<ExpressionTree>emptyList(),
invk.getMethodSelect(),
params2Arguments(make, superclassConstr.getParameters())
))), false);
// create constructor
MethodTree newConstr = make.Method(superclassConstr, block);
newConstr = removeRuntimeExceptions(workingCopy, superclassConstr, make, newConstr);
newConstr = genUtils.importFQNs(newConstr);
members.add(newConstr);
}
}
// take just first statement super(...)
break;
}
}
}
}
private static MethodTree removeRuntimeExceptions(final WorkingCopy javac, ExecutableElement superclassConstr, final TreeMaker make, MethodTree newConstr) {
int i = 0;
TypeMirror rte = javac.getElements().getTypeElement("java.lang.RuntimeException").asType(); //NOI18N
ArrayList<Integer> rtes = new ArrayList<Integer>();
for (TypeMirror throwz : superclassConstr.getThrownTypes()) {
if (javac.getTypes().isSubtype(throwz, rte)) {
rtes.add(i);
}
i++;
}
for (int j = rtes.size()-1; j >= 0; j--) {
newConstr = make.removeMethodThrows(newConstr, rtes.get(j));
}
return newConstr;
}
private static List<? extends ExpressionTree> params2Arguments(TreeMaker make, List<? extends VariableElement> params) {
if (params.isEmpty()) {
return Collections.<ExpressionTree>emptyList();
}
List<ExpressionTree> args = new ArrayList<ExpressionTree>(params.size());
for (VariableElement param : params) {
args.add(make.Identifier(param.getSimpleName()));
}
return args;
}
}
}