diff --git a/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/Bundle.properties b/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/Bundle.properties --- a/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/Bundle.properties +++ b/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/Bundle.properties @@ -137,6 +137,8 @@ ERR_EmptyReferenceName=Name of the reference to the outer class can not be empty. ERR_OuterNameAlreadyUsed=Name "{0}" is already used as a name of field from {1}. WRN_OuterNameAlreadyUsed=Name "{0}" is already used as a name of field from {1}. +ERR_InnerToOuter_OuterNameClash=Name "{0}" is already used as a parameter of constructor from {1}. +ERR_InnerToOuter_OuterNameClashSubtype=Name {0} is already used in subtype of {1} ({2}) as a parameter of constructor. # --- USE SUPERTYPE REFACTORING ------------------------------------------------------ TXT_UseSuperType=Use Supertype of {0} where possible diff --git a/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterRefactoringPlugin.java b/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterRefactoringPlugin.java --- a/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterRefactoringPlugin.java +++ b/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterRefactoringPlugin.java @@ -54,6 +54,7 @@ import java.util.List; import java.util.Set; import javax.lang.model.element.Element; +import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.NestingKind; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; @@ -179,6 +180,17 @@ break; } } + + List constructors = ElementFilter.constructorsIn(((TypeElement)resolved).getEnclosedElements()); + for( ExecutableElement execElement: constructors ) { + List parameters = execElement.getParameters(); + + for( VariableElement variableElement: parameters ) { + if(variableElement.getSimpleName().toString().equals(name)) { + return new Problem(true, NbBundle.getMessage(InnerToOuterRefactoringPlugin.class, "ERR_InnerToOuter_OuterNameClash", name, resolved.getSimpleName())); // NOI18N + } + } + } } } return problem; diff --git a/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterTransformer.java b/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterTransformer.java --- a/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterTransformer.java +++ b/refactoring.java/src/org/netbeans/modules/refactoring/java/plugins/InnerToOuterTransformer.java @@ -252,8 +252,8 @@ @Override public Tree visitClass(ClassTree classTree, Element element) { Element currentElement = workingCopy.getTrees().getElement(getCurrentPath()); - GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy); // helper - if (currentElement!=null && currentElement == outer) { + GeneratorUtilities genUtils = GeneratorUtilities.get(workingCopy); // helper + if (isOuterClass(currentElement)) { Element outerouter = outer.getEnclosingElement(); Tree superVisit = super.visitClass(classTree, element); TreePath tp = workingCopy.getTrees().getPath(inner); @@ -279,24 +279,6 @@ CompilationUnitTree compilationUnit = tp.getCompilationUnit(); String relativePath = RefactoringUtils.getPackageName(compilationUnit).replace('.', '/') + '/' + refactoring.getClassName() + ".java"; // NOI18N CompilationUnitTree newCompilation = JavaPluginUtils.createCompilationUnit(sourceRoot, relativePath, newInnerClass, workingCopy, make); -// try { -// newCompilation = genUtils.createFromTemplate(sourceRoot, relativePath, ElementKind.CLASS); -// List typeDecls = newCompilation.getTypeDecls(); -// if(workingCopy.getTreeUtilities().getComments(newInnerClass, true).isEmpty()) { -// if (!typeDecls.isEmpty()) { -// ClassTree templateClazz = (ClassTree) typeDecls.get(0); -// genUtils.copyComments(templateClazz, newInnerClass, true); -// } -// } -// if(typeDecls.isEmpty()) { -// newCompilation = make.addCompUnitTypeDecl(newCompilation, newInnerClass); -// } else { -// rewrite(typeDecls.get(0), newInnerClass); -// } -// } catch (IOException ex) { -// Exceptions.printStackTrace(ex); -// newCompilation = make.CompilationUnit(sourceRoot, relativePath, null, Collections.singletonList(newInnerClass)); -// } rewrite(null, newCompilation); return newOuter; } else { @@ -313,46 +295,49 @@ return newOuterOuter; } - } else if (refactoring.getReferenceName() != null && currentElement!=null && workingCopy.getTypes().isSubtype(currentElement.asType(), inner.asType()) && currentElement!=inner) { - VariableTree variable = make.Variable(make.Modifiers(Collections.emptySet()), refactoring.getReferenceName(), make.Type(outer.asType()), null); - for (Tree member:classTree.getMembers()) { - if (member.getKind() == Tree.Kind.METHOD) { - MethodTree m = (MethodTree) member; - if (m.getReturnType()==null) { - MethodInvocationTree superCall = (MethodInvocationTree) ((ExpressionStatementTree) m.getBody().getStatements().get(0)).getExpression(); - List newArgs = new ArrayList(superCall.getArguments()); - - MethodTree newConstructor = null; - ExpressionTree exprTree = (ExpressionTree)make.Identifier(variable.getName().toString()); - if (hasVarArgs(m)) { - int index = m.getParameters().size() - 1; - newArgs.add(index, exprTree); - newConstructor = make.insertMethodParameter(m, index, variable); - } else { - newArgs.add(exprTree); - newConstructor = make.addMethodParameter(m, variable); - } - MethodInvocationTree method = make.MethodInvocation( - Collections.emptyList(), - make.Identifier("super"), // NOI18N - newArgs); - - BlockTree block = make.insertBlockStatement(m.getBody(), 0, make.ExpressionStatement(method)); - block = make.removeBlockStatement(block, 1); - - newConstructor = make.Constructor( - make.Modifiers(newConstructor.getModifiers().getFlags(), newConstructor.getModifiers().getAnnotations()), - newConstructor.getTypeParameters(), - newConstructor.getParameters(), - newConstructor.getThrows(), - block); - - rewrite(m, newConstructor); + } else if (refactoring.getReferenceName() != null && isInnerSubclass(currentElement)) { + VariableTree outerVar = make.Variable(make.Modifiers(Collections.emptySet()), refactoring.getReferenceName(), make.Type(outer.asType()), null); + + for (MethodTree constructor: getConstructors(classTree)) { + + for( VariableTree var: constructor.getParameters() ) { + if( var.getName().contentEquals(refactoring.getReferenceName()) ) { + problem = MoveTransformer.createProblem(problem, true, NbBundle.getMessage(InnerToOuterTransformer.class, "ERR_InnerToOuter_OuterNameClashSubtype", refactoring.getReferenceName(), refactoring.getClassName(), currentElement.getSimpleName())); } } - } - } - + + MethodInvocationTree superCall = (MethodInvocationTree) ((ExpressionStatementTree) constructor.getBody().getStatements().get(0)).getExpression(); + List superCallArgs = new ArrayList(superCall.getArguments()); + + MethodTree newConstructor = null; + ExpressionTree outerIdent = (ExpressionTree)make.Identifier(outerVar.getName().toString()); + if (hasVarArgs(constructor)) { + int index = constructor.getParameters().size() - 1; + superCallArgs.add(index, outerIdent); + newConstructor = make.insertMethodParameter(constructor, index, outerVar); + } else { + superCallArgs.add(outerIdent); + newConstructor = make.addMethodParameter(constructor, outerVar); + } + MethodInvocationTree method = make.MethodInvocation( + Collections.emptyList(), + make.Identifier("super"), // NOI18N + superCallArgs); + + BlockTree block = make.insertBlockStatement(constructor.getBody(), 0, make.ExpressionStatement(method)); + block = make.removeBlockStatement(block, 1); + + newConstructor = make.Constructor( + make.Modifiers(newConstructor.getModifiers().getFlags(), newConstructor.getModifiers().getAnnotations()), + newConstructor.getTypeParameters(), + newConstructor.getParameters(), + newConstructor.getThrows(), + block); + + rewrite(constructor, newConstructor); + } + } + if (currentElement == inner) { try { isInInnerClass = true; @@ -529,6 +514,12 @@ return false; } + /** + * Determines whether the given method has variable arguments list (has ... in its declaration). + * + * @param mt method to test + * @return + */ private boolean hasVarArgs(MethodTree mt) { List list = mt.getParameters(); if (list.isEmpty()) { @@ -616,4 +607,39 @@ } return false; } + + /** + * Determines whether the given element and the outer class are the same. + * + * @param el element to test + * @return + */ + private boolean isOuterClass(Element el) { + return el!=null && el == outer; + } + + /** + * Determines whether the given element is a subclass of the inner class. + * + * @param el element to test + * @return {@code true} if the given element is the subclass of the inner class and {@code false} otherwise + */ + private boolean isInnerSubclass(Element el) { + return el!=null && workingCopy.getTypes().isSubtype(el.asType(), inner.asType()) && el!=inner; + } + + private List getConstructors(ClassTree classTree) { + List constructors = new LinkedList(); + + for (Tree member: classTree.getMembers()) { + if (member.getKind() == Tree.Kind.METHOD) { + MethodTree m = (MethodTree) member; + if (m.getReturnType()==null) { + constructors.add(m); + } + } + } + + return constructors; + } }