extract class: allow to make nested (IDEA-144033)

This commit is contained in:
Anna Kozlova
2015-09-09 11:44:36 +03:00
parent 7484fa8599
commit 07ab234ff3
5 changed files with 114 additions and 10 deletions
@@ -63,6 +63,7 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
private JCheckBox myGenerateAccessorsCb;
private final JavaVisibilityPanel myVisibilityPanel;
private final JCheckBox extractAsEnum;
private final JCheckBox createInner;
private final List<MemberInfo> enumConstants = new ArrayList<MemberInfo>();
ExtractClassDialog(PsiClass sourceClass, PsiMember selectedMember) {
@@ -129,6 +130,7 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
if (!hasConstants) {
extractAsEnum.setVisible(false);
}
createInner = new JCheckBox("Create nested class");
super.init();
validateButtons();
}
@@ -152,9 +154,8 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
false),
newClassName, myVisibilityPanel.getVisibility(),
isGenerateAccessors(),
isExtractAsEnum()
? enumConstants
: Collections.<MemberInfo>emptyList());
isExtractAsEnum() ? enumConstants : Collections.<MemberInfo>emptyList());
processor.setExtractInnerClass(createInner.isSelected());
if (processor.getCreatedClass() == null) {
Messages.showErrorDialog(myVisibilityPanel, "Unable to create class with the given name");
classNameField.requestFocusInWindow();
@@ -243,12 +244,15 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
}
protected JComponent createNorthPanel() {
final JPanel checkboxPanel = new JPanel(new BorderLayout());
checkboxPanel.add(createInner, BorderLayout.WEST);
checkboxPanel.add(extractAsEnum, BorderLayout.EAST);
FormBuilder builder = FormBuilder.createFormBuilder()
.addComponent(
JBLabelDecorator.createJBLabelDecorator(RefactorJBundle.message("extract.class.from.label", sourceClass.getQualifiedName()))
.setBold(true))
.addLabeledComponent(RefactorJBundle.message("name.for.new.class.label"), classNameField, UIUtil.LARGE_VGAP)
.addLabeledComponent(new JLabel(), extractAsEnum)
.addLabeledComponent(new JLabel(), checkboxPanel)
.addLabeledComponent(RefactorJBundle.message("package.for.new.class.label"), packageTextField);
if (JavaProjectRootsUtil.getSuitableDestinationSourceRoots(myProject).size() > 1) {
@@ -349,6 +353,14 @@ class ExtractClassDialog extends RefactoringDialog implements MemberInfoChangeLi
table.repaint();
}
});
createInner.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final boolean isCreateInner = createInner.isSelected();
packageTextField.setEnabled(!isCreateInner);
myDestinationFolderComboBox.setEnabled(!isCreateInner);
}
});
myGenerateAccessorsCb = new JCheckBox("Generate accessors");
myGenerateAccessorsCb.setMnemonic('G');
panel.add(myGenerateAccessorsCb, BorderLayout.SOUTH);
@@ -84,6 +84,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
private boolean delegationRequired = false;
private final ExtractEnumProcessor myExtractEnumProcessor;
private final PsiClass myClass;
private boolean extractInnerClass;
public ExtractClassProcessor(PsiClass sourceClass,
List<PsiField> fields,
@@ -150,6 +151,10 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
return myClass;
}
public void setExtractInnerClass(boolean extractInnerClass) {
this.extractInnerClass = extractInnerClass;
}
@Override
protected boolean preprocessUsages(@NotNull final Ref<UsageInfo[]> refUsages) {
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
@@ -166,7 +171,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
final Project project = sourceClass.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final PsiClass existingClass =
JavaPsiFacade.getInstance(project).findClass(StringUtil.getQualifiedName(newPackageName, newClassName), scope);
JavaPsiFacade.getInstance(project).findClass(getQualifiedName(), scope);
if (existingClass != null) {
conflicts.putValue(existingClass, RefactorJBundle.message("cannot.perform.the.refactoring") +
RefactorJBundle.message("there.already.exists.a.class.with.the.chosen.name"));
@@ -193,6 +198,10 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
return showConflicts(conflicts, refUsages.get());
}
private String getQualifiedName() {
return extractInnerClass ? newClassName : StringUtil.getQualifiedName(newPackageName, newClassName);
}
private void calculateInitializersConflicts(MultiMap<PsiElement, String> conflicts) {
final PsiClassInitializer[] initializers = sourceClass.getInitializers();
@@ -412,7 +421,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
final String delegateVisibility = calculateDelegateVisibility();
if (delegateVisibility.length() > 0) fieldBuffer.append(delegateVisibility).append(' ');
fieldBuffer.append("final ");
final String fullyQualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName);
final String fullyQualifiedName = getQualifiedName();
fieldBuffer.append(fullyQualifiedName);
if (!typeParams.isEmpty()) {
fieldBuffer.append('<');
@@ -497,7 +506,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
assert innerName != null;
final String sourceClassQualifiedName = sourceClass.getQualifiedName();
assert sourceClassQualifiedName != null;
final String newInnerClassName = StringUtil.getQualifiedName(newPackageName, newClassName) + innerName.substring(sourceClassQualifiedName.length());
final String newInnerClassName = getQualifiedName() + innerName.substring(sourceClassQualifiedName.length());
boolean hasExternalReference = false;
for (PsiReference reference : calls) {
final PsiElement referenceElement = reference.getElement();
@@ -552,7 +561,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
final Project project = psiManager.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final Iterable<PsiReference> calls = ReferencesSearch.search(method, scope);
final String fullyQualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName);
final String fullyQualifiedName = getQualifiedName();
for (PsiReference reference : calls) {
final PsiElement referenceElement = reference.getElement();
@@ -594,7 +603,7 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
final Project project = psiManager.getProject();
final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
final String qualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName);
final String qualifiedName = getQualifiedName();
@NonNls String getter = null;
if (myGenerateAccessors) {
getter = GenerateMembersUtil.suggestGetterName(field);
@@ -683,6 +692,19 @@ public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
}
final String classString = extractedClassBuilder.buildBeanClass();
if (extractInnerClass) {
final PsiFileFactory factory = PsiFileFactory.getInstance(project);
final PsiJavaFile newFile = (PsiJavaFile)factory.createFileFromText(newClassName + ".java", JavaFileType.INSTANCE, classString);
final PsiClass psiClass = newFile.getClasses()[0];
if (!psiClass.isEnum()) {
final PsiModifierList modifierList = psiClass.getModifierList();
assert modifierList != null;
modifierList.setModifierProperty(PsiModifier.STATIC, true);
}
final PsiElement addedClass = sourceClass.add(psiClass);
return (PsiClass)CodeStyleManager.getInstance(manager)
.reformat(JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedClass));
}
try {
final PsiFile containingFile = sourceClass.getContainingFile();
@@ -0,0 +1,29 @@
class Test {
final Extracted extracted = new Extracted(this);
void bar(){
System.out.println(extracted.getMyT());
}
String foo() {
return "";
}
void bazz() {
bar();
}
public static class Extracted {
private final Test test;
String myT;
public String getMyT() {
return myT;
}
public Extracted(Test test) {
this.test = test;
this.myT = test.foo();
}
}
}
@@ -0,0 +1,15 @@
class Test {
String myT = foo();
void bar(){
System.out.println(myT);
}
String foo() {
return "";
}
void bazz() {
bar();
}
}
@@ -192,11 +192,37 @@ public class ExtractClassTest extends MultiFileTestCase{
});
}
private static void doTest(final PsiClass aClass, final ArrayList<PsiMethod> methods, final ArrayList<PsiField> fields, final String conflicts,
public void testInnerClass() throws Exception {
doTest((rootDir, rootAfter) -> {
PsiClass aClass = myJavaFacade.findClass("Test", GlobalSearchScope.projectScope(myProject));
assertNotNull("Class Test not found", aClass);
final ArrayList<PsiField> fields = new ArrayList<>();
fields.add(aClass.findFieldByName("myT", false));
doTest(aClass, new ArrayList<>(), fields, null, true, true);
});
}
private static void doTest(final PsiClass aClass,
final ArrayList<PsiMethod> methods,
final ArrayList<PsiField> fields,
final String conflicts,
boolean generateGettersSetters) {
doTest(aClass, methods, fields, conflicts, generateGettersSetters, false);
}
private static void doTest(final PsiClass aClass,
final ArrayList<PsiMethod> methods,
final ArrayList<PsiField> fields,
final String conflicts,
boolean generateGettersSetters,
boolean inner) {
try {
ExtractClassProcessor processor = new ExtractClassProcessor(aClass, fields, methods, new ArrayList<>(), StringUtil.getPackageName(aClass.getQualifiedName()), null,
"Extracted", null, generateGettersSetters, Collections.<MemberInfo>emptyList());
processor.setExtractInnerClass(inner);
processor.run();
LocalFileSystem.getInstance().refresh(false);
FileDocumentManager.getInstance().saveAllDocuments();