diff --git a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassDialog.java b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassDialog.java index 9089af1e0f93..e2a6657c34c4 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassDialog.java @@ -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 enumConstants = new ArrayList(); 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.emptyList()); + isExtractAsEnum() ? enumConstants : Collections.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); diff --git a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java index 03d4f641bf73..37fdab9adee5 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassProcessor.java @@ -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 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 refUsages) { final MultiMap conflicts = new MultiMap(); @@ -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 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 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(); diff --git a/java/java-tests/testData/refactoring/extractClass/innerClass/after/Test.java b/java/java-tests/testData/refactoring/extractClass/innerClass/after/Test.java new file mode 100644 index 000000000000..4ce6c8fff1af --- /dev/null +++ b/java/java-tests/testData/refactoring/extractClass/innerClass/after/Test.java @@ -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(); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/extractClass/innerClass/before/Test.java b/java/java-tests/testData/refactoring/extractClass/innerClass/before/Test.java new file mode 100644 index 000000000000..3311280a1ee7 --- /dev/null +++ b/java/java-tests/testData/refactoring/extractClass/innerClass/before/Test.java @@ -0,0 +1,15 @@ +class Test { + String myT = foo(); + + void bar(){ + System.out.println(myT); + } + + String foo() { + return ""; + } + + void bazz() { + bar(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/refactoring/ExtractClassTest.java b/java/java-tests/testSrc/com/intellij/refactoring/ExtractClassTest.java index a3a87316f1cc..504f85df5aa3 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/ExtractClassTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/ExtractClassTest.java @@ -192,11 +192,37 @@ public class ExtractClassTest extends MultiFileTestCase{ }); } - private static void doTest(final PsiClass aClass, final ArrayList methods, final ArrayList 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 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 methods, + final ArrayList fields, + final String conflicts, boolean generateGettersSetters) { + doTest(aClass, methods, fields, conflicts, generateGettersSetters, false); + } + + private static void doTest(final PsiClass aClass, + final ArrayList methods, + final ArrayList 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.emptyList()); + processor.setExtractInnerClass(inner); processor.run(); LocalFileSystem.getInstance().refresh(false); FileDocumentManager.getInstance().saveAllDocuments(); diff --git a/plugins/eclipse/common-eclipse-util/src/AbstractEclipseClasspathReader.java b/plugins/eclipse/common-eclipse-util/src/AbstractEclipseClasspathReader.java index 161463d697c5..2c5144353427 100644 --- a/plugins/eclipse/common-eclipse-util/src/AbstractEclipseClasspathReader.java +++ b/plugins/eclipse/common-eclipse-util/src/AbstractEclipseClasspathReader.java @@ -71,7 +71,7 @@ public abstract class AbstractEclipseClasspathReader { boolean exported, String libName, String url, - String srcUrl, ExpandMacroToPathMap macroMap); + String srcUrl, String nativeRoot, ExpandMacroToPathMap macroMap); protected abstract String expandEclipsePath2Url(T rootModel, String path); @@ -178,7 +178,12 @@ public abstract class AbstractEclipseClasspathReader { } } - addModuleLibrary(rootModel, element, exported, getPresentableName(path, libs), url, srcUrl, macroMap); + String nativeRoot = getNativeLibraryRoot(element); + if (nativeRoot != null) { + nativeRoot = expandEclipsePath2Url(rootModel, nativeRoot); + } + + addModuleLibrary(rootModel, element, exported, getPresentableName(path, libs), url, srcUrl, nativeRoot, macroMap); } else if (kind.equals(EclipseXml.VAR_KIND)) { int slash = path.indexOf("/"); @@ -201,7 +206,11 @@ public abstract class AbstractEclipseClasspathReader { eclipseModuleManager.registerEclipseSrcVariablePath(srcUrl, srcPathAttr); } } - addModuleLibrary(rootModel, element, exported, libName, url, srcUrl, macroMap); + String nativeRoot = getNativeLibraryRoot(element); + if (nativeRoot != null) { + nativeRoot = expandEclipsePath2Url(rootModel, nativeRoot); + } + addModuleLibrary(rootModel, element, exported, libName, url, srcUrl, nativeRoot, macroMap); } else if (kind.equals(EclipseXml.CON_KIND)) { if (path.equals(EclipseXml.ECLIPSE_PLATFORM)) { @@ -242,6 +251,18 @@ public abstract class AbstractEclipseClasspathReader { } } + private static String getNativeLibraryRoot(Element element) { + final Element attributes = element.getChild(EclipseXml.ATTRIBUTES_TAG); + if (attributes != null) { + for (Element attributeElement : attributes.getChildren(EclipseXml.ATTRIBUTE_TAG)) { + if (EclipseXml.DLL_LINK.equals(attributeElement.getAttributeValue(EclipseXml.NAME_ATTR))) { + return attributeElement.getAttributeValue(EclipseXml.VALUE_ATTR); + } + } + } + return null; + } + protected static int srcVarStart(String srcPath) { return srcPath.startsWith("/") ? 1 : 0; } diff --git a/plugins/eclipse/common-eclipse-util/src/EclipseXml.java b/plugins/eclipse/common-eclipse-util/src/EclipseXml.java index 2c3cbaaddb7d..bf1132dbfd1a 100644 --- a/plugins/eclipse/common-eclipse-util/src/EclipseXml.java +++ b/plugins/eclipse/common-eclipse-util/src/EclipseXml.java @@ -76,4 +76,5 @@ public interface EclipseXml { @NonNls String LINKED_RESOURCES = "linkedResources"; @NonNls String LINK = "link"; @NonNls String JAVADOC_LOCATION = "javadoc_location"; + @NonNls String DLL_LINK = "org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY"; } diff --git a/plugins/eclipse/jps-plugin/src/org/jetbrains/jps/eclipse/model/JpsEclipseClasspathReader.java b/plugins/eclipse/jps-plugin/src/org/jetbrains/jps/eclipse/model/JpsEclipseClasspathReader.java index 07ab144cd401..579b8ed6bb69 100644 --- a/plugins/eclipse/jps-plugin/src/org/jetbrains/jps/eclipse/model/JpsEclipseClasspathReader.java +++ b/plugins/eclipse/jps-plugin/src/org/jetbrains/jps/eclipse/model/JpsEclipseClasspathReader.java @@ -151,6 +151,7 @@ class JpsEclipseClasspathReader extends AbstractEclipseClasspathReader() { + @Override + public String fun(String s) { + return toEclipseJavadocPath(model, s); + } + }, JAVADOC_LOCATION, libraryOrderEntry.getRootUrls(JavadocOrderRootType.getInstance())); + } + + static void setupAttributes(Element orderEntry, + Function fun, + String attributeName, + String[] roots) { final List eclipseUrls = new ArrayList(); - final String[] docUrls = libraryOrderEntry.getRootUrls(JavadocOrderRootType.getInstance()); - if (docUrls.length > 0) { - eclipseUrls.add(toEclipseJavadocPath(model, docUrls[0])); + if (roots.length > 0) { + eclipseUrls.add(fun.fun(roots[0])); } final List children = new ArrayList(orderEntry.getChildren(ATTRIBUTES_TAG)); @@ -214,7 +226,7 @@ public class EJavadocUtil { final ArrayList attTags = new ArrayList(attsElement.getChildren(ATTRIBUTE_TAG)); for (Object a : attTags) { Element attElement = (Element)a; - if (Comparing.strEqual(attElement.getAttributeValue("name"), JAVADOC_LOCATION)) { + if (Comparing.strEqual(attElement.getAttributeValue("name"), attributeName)) { final String javadocPath = attElement.getAttributeValue("value"); if (!eclipseUrls.remove(javadocPath)) { attElement.detach(); @@ -232,7 +244,7 @@ public class EJavadocUtil { final Element attrElement = new Element(ATTRIBUTE_TAG); child.addContent(attrElement); - attrElement.setAttribute("name", JAVADOC_LOCATION); + attrElement.setAttribute("name", attributeName); attrElement.setAttribute("value", docUrl); } } diff --git a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathReader.java b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathReader.java index 5cc1dbd15f23..9e1edbe36352 100644 --- a/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathReader.java +++ b/plugins/eclipse/src/org/jetbrains/idea/eclipse/conversion/EclipseClasspathReader.java @@ -153,13 +153,20 @@ public class EclipseClasspathReader extends AbstractEclipseClasspathReader 0) { + EJavadocUtil.setupAttributes(orderEntry, new Function() { + @Override + public String fun(String nativeRoot) { + return EPathUtil.collapse2EclipsePath(nativeRoot, model); + } + }, EclipseXml.DLL_LINK, nativeRoots); + } setExported(orderEntry, libraryOrderEntry); } } diff --git a/plugins/eclipse/testData/round/nativeLibs/test/.classpath b/plugins/eclipse/testData/round/nativeLibs/test/.classpath new file mode 100644 index 000000000000..e7e908d65fa8 --- /dev/null +++ b/plugins/eclipse/testData/round/nativeLibs/test/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/plugins/eclipse/testData/round/nativeLibs/test/.project b/plugins/eclipse/testData/round/nativeLibs/test/.project new file mode 100644 index 000000000000..4766dbaa2c60 --- /dev/null +++ b/plugins/eclipse/testData/round/nativeLibs/test/.project @@ -0,0 +1,15 @@ + + + test + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.jdt.core.javanature + + diff --git a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseClasspathTest.java b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseClasspathTest.java index bf0340c9522e..03c1c7269ae8 100644 --- a/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseClasspathTest.java +++ b/plugins/eclipse/testSources/org/jetbrains/idea/eclipse/EclipseClasspathTest.java @@ -150,6 +150,10 @@ public class EclipseClasspathTest extends IdeaTestCase { doTest(); } + public void testNativeLibs() throws Exception { + doTest(); + } + public void testAccessrulez() throws Exception { doTest(); }