diff --git a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXRenameTest.java b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXRenameTest.java index 00ab88ec6225..592432817f71 100644 --- a/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXRenameTest.java +++ b/plugins/javaFX/javaFX-CE/testSrc/org/jetbrains/plugins/javaFX/fxml/JavaFXRenameTest.java @@ -190,6 +190,19 @@ public class JavaFXRenameTest extends AbstractJavaFXRenameTest { assertMethodExists(psiClass, "getNewName"); } + public void testNestedControllerIdFromFxml() throws Exception { + doTestHandler("newName", getTestName(false) + "Internal"); + myFixture.checkResultByFile(getTestName(false) + ".java", getTestName(false) + "_after.java", false); + } + + public void testNestedControllerIdFromJava() throws Exception { + myFixture.configureByFiles(getTestName(false) + ".java", getTestName(false) + "Internal.java", getTestName(true) + ".fxml"); + final PsiElement elementAtCaret = myFixture.getElementAtCaret(); + new RenameProcessor(getProject(), elementAtCaret, "newName", false, false).run(); + myFixture.checkResultByFile(getTestName(true) + ".fxml", getTestName(true) + "_after.fxml", false); + myFixture.checkResultByFile(getTestName(false) + ".java", getTestName(false) + "_after.java", false); + } + public void testControllerStringProperty() throws Exception { doTestProperty("newName", false); } diff --git a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml index 73735cf560b4..5f88e12bf2e5 100644 --- a/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml +++ b/plugins/javaFX/src/META-INF/common-javaFX-plugin.xml @@ -15,6 +15,7 @@ + diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/refactoring/JavaFxPropertyRenameHandler.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/refactoring/JavaFxPropertyRenameHandler.java index ff39fd2e385e..ade73e296eab 100644 --- a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/refactoring/JavaFxPropertyRenameHandler.java +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/refactoring/JavaFxPropertyRenameHandler.java @@ -6,14 +6,14 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiReference; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference; import com.intellij.psi.util.PropertyUtil; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttributeValue; import com.intellij.psi.xml.XmlFile; +import com.intellij.psi.xml.XmlTag; import com.intellij.refactoring.RenameRefactoring; import com.intellij.refactoring.openapi.impl.JavaRenameRefactoringImpl; import com.intellij.refactoring.rename.PsiElementRenameHandler; @@ -25,8 +25,10 @@ import gnu.trove.THashMap; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames; import org.jetbrains.plugins.javaFX.fxml.JavaFxFileTypeFactory; +import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil; import org.jetbrains.plugins.javaFX.fxml.refs.JavaFxComponentIdReferenceProvider; import org.jetbrains.plugins.javaFX.fxml.refs.JavaFxFieldIdReferenceProvider; import org.jetbrains.plugins.javaFX.fxml.refs.JavaFxPropertyReference; @@ -87,23 +89,26 @@ public class JavaFxPropertyRenameHandler implements RenameHandler { } } if (reference instanceof JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef) { + final XmlAttributeValue fxIdValueElement = + ((JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef)reference).getXmlAttributeValue(); final Set elementsToRename = new THashSet<>(); JavaFxRenameAttributeProcessor.visitReferencedElements(references, psiElement -> { if (psiElement != null) { elementsToRename.add(psiElement); } }); + ContainerUtil.addIfNotNull(elementsToRename, getNestedControllerField(fxIdValueElement)); + if (!canRename(project, editor, elementsToRename)) { return; } - final XmlAttributeValue fxIdElement = ((JavaFxFieldIdReferenceProvider.JavaFxControllerFieldRef)reference).getXmlAttributeValue(); if (ApplicationManager.getApplication().isUnitTestMode()) { final String newName = PsiElementRenameHandler.DEFAULT_NAME.getData(dataContext); assert newName != null : "Rename property"; - new RenameDialog(project, fxIdElement, null, editor).performRename(newName); + doRenameFxId(fxIdValueElement, newName, false, false); } else { - new RenameDialog(project, fxIdElement, null, editor).show(); + new RenameFxIdDialog(fxIdValueElement, editor).show(); } } } @@ -180,6 +185,33 @@ public class JavaFxPropertyRenameHandler implements RenameHandler { if (key != null) map.put(key, value); } + public static void doRenameFxId(XmlAttributeValue fxIdValueElement, String newName, boolean searchInComments, boolean previewUsages) { + final RenameRefactoring rename = + new JavaRenameRefactoringImpl(fxIdValueElement.getProject(), fxIdValueElement, newName, searchInComments, false); + rename.setPreviewUsages(previewUsages); + + final PsiField nestedControllerField = getNestedControllerField(fxIdValueElement); + if (nestedControllerField != null) { + rename.addElement(nestedControllerField, newName + FxmlConstants.CONTROLLER_SUFFIX); + } + rename.run(); + } + + @Nullable + private static PsiField getNestedControllerField(@NotNull XmlAttributeValue fxIdValueElement) { + final String fxId = fxIdValueElement.getValue(); + if (!StringUtil.isEmpty(fxId)) { + final XmlTag tag = PsiTreeUtil.getParentOfType(fxIdValueElement, XmlTag.class); + if (tag != null && FxmlConstants.FX_INCLUDE.equals(tag.getName())) { + final PsiClass controllerClass = JavaFxPsiUtil.getControllerClass(tag.getContainingFile()); + if (controllerClass != null) { + return controllerClass.findFieldByName(fxId + FxmlConstants.CONTROLLER_SUFFIX, true); + } + } + } + return null; + } + private static class PropertyRenameDialog extends RenameDialog { private final JavaFxPropertyReference myPropertyReference; @@ -199,4 +231,19 @@ public class JavaFxPropertyRenameHandler implements RenameHandler { close(DialogWrapper.OK_EXIT_CODE); } } + + private static class RenameFxIdDialog extends RenameDialog { + public RenameFxIdDialog(@NotNull XmlAttributeValue fxIdValueElement, Editor editor) { + super(fxIdValueElement.getProject(), fxIdValueElement, null, editor); + } + + @Override + protected void doAction() { + final String newName = getNewName(); + final PsiElement element = getPsiElement(); + assert element instanceof XmlAttributeValue; + doRenameFxId(((XmlAttributeValue)element), newName, isSearchInComments(), isPreviewUsages()); + close(DialogWrapper.OK_EXIT_CODE); + } + } } diff --git a/plugins/javaFX/src/org/jetbrains/plugins/javaFX/refactoring/JavaFxRenameFxIdFieldProcessor.java b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/refactoring/JavaFxRenameFxIdFieldProcessor.java new file mode 100644 index 000000000000..562ec99b2d2c --- /dev/null +++ b/plugins/javaFX/src/org/jetbrains/plugins/javaFX/refactoring/JavaFxRenameFxIdFieldProcessor.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2013 JetBrains s.r.o. + * + * 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. + */ +package org.jetbrains.plugins.javaFX.refactoring; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.search.SearchScope; +import com.intellij.psi.xml.XmlTag; +import com.intellij.refactoring.rename.RenamePsiElementProcessor; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.javaFX.fxml.FxmlConstants; +import org.jetbrains.plugins.javaFX.indexing.JavaFxControllerClassIndex; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +/** + * @author Pavel.Dolgov + */ +public class JavaFxRenameFxIdFieldProcessor extends RenamePsiElementProcessor { + @Override + public boolean canProcessElement(@NotNull PsiElement element) { + final NestedControllerCandidate nestedControllerCandidate = findNestedControllerCandidate(element); + final Collection fxmls = findFxmlWithController(nestedControllerCandidate); + return !fxmls.isEmpty(); + } + + @Override + public boolean isInplaceRenameSupported() { + return false; + } + + @Override + public void prepareRenaming(PsiElement element, String newName, Map allRenames, SearchScope scope) { + final NestedControllerCandidate nestedControllerCandidate = findNestedControllerCandidate(element); + if (nestedControllerCandidate != null) { + final Collection fxmls = findFxmlWithController(nestedControllerCandidate); + for (PsiFile fxml : fxmls) { + final Ref found = new Ref<>(false); + fxml.accept(new XmlRecursiveElementVisitor() { + @Override + public void visitXmlTag(XmlTag tag) { + super.visitXmlTag(tag); + if (found.get()) return; + if (FxmlConstants.FX_INCLUDE.equals(tag.getName())) { + final String value = tag.getAttributeValue(FxmlConstants.FX_ID); + if (StringUtil.equals(nestedControllerCandidate.fxId, value)) { + found.set(true); + } + } + } + }); + if (found.get()) { + allRenames.put(nestedControllerCandidate.nestedControllerField, newName + FxmlConstants.CONTROLLER_SUFFIX); + return; + } + } + } + } + + @Nullable + private static NestedControllerCandidate findNestedControllerCandidate(@NotNull PsiElement element) { + if (element instanceof PsiField) { + final PsiField field = (PsiField)element; + final String fxId = field.getName(); + if (!StringUtil.isEmpty(fxId)) { + final PsiClass containingClass = field.getContainingClass(); + if (containingClass != null) { + final PsiField nestedControllerField = containingClass.findFieldByName(fxId + FxmlConstants.CONTROLLER_SUFFIX, true); + if (nestedControllerField != null) { + final PsiType psiType = nestedControllerField.getType(); + if (!(psiType instanceof PsiPrimitiveType) && !(psiType instanceof PsiArrayType)) { // optimization + return new NestedControllerCandidate(fxId, nestedControllerField, containingClass); + } + } + } + } + } + return null; + } + + @NotNull + private static Collection findFxmlWithController(@Nullable NestedControllerCandidate nestedControllerCandidate) { + if (nestedControllerCandidate != null) { + final String qualifiedName = nestedControllerCandidate.controllerClass.getQualifiedName(); + if (qualifiedName != null) { + final Project project = nestedControllerCandidate.controllerClass.getProject(); + return JavaFxControllerClassIndex.findFxmlWithController(project, qualifiedName); + } + } + return Collections.emptyList(); + } + + private static class NestedControllerCandidate { + private final String fxId; + private final PsiField nestedControllerField; + private final PsiClass controllerClass; + + private NestedControllerCandidate(@NotNull String fxId, @NotNull PsiField nestedControllerField, @NotNull PsiClass controllerClass) { + this.fxId = fxId; + this.nestedControllerField = nestedControllerField; + this.controllerClass = controllerClass; + } + } +} diff --git a/plugins/javaFX/testData/rename/NestedControllerIdFromFxml.java b/plugins/javaFX/testData/rename/NestedControllerIdFromFxml.java new file mode 100644 index 000000000000..893cfafc4878 --- /dev/null +++ b/plugins/javaFX/testData/rename/NestedControllerIdFromFxml.java @@ -0,0 +1,5 @@ +import javafx.fxml.FXML; + +class NestedControllerIdFromFxml { + @FXML NestedControllerIdFromFxmlInternal internalController; +} \ No newline at end of file diff --git a/plugins/javaFX/testData/rename/NestedControllerIdFromFxmlInternal.java b/plugins/javaFX/testData/rename/NestedControllerIdFromFxmlInternal.java new file mode 100644 index 000000000000..272e78809e45 --- /dev/null +++ b/plugins/javaFX/testData/rename/NestedControllerIdFromFxmlInternal.java @@ -0,0 +1,2 @@ +class NestedControllerIdFromFxmlInternal { +} \ No newline at end of file diff --git a/plugins/javaFX/testData/rename/NestedControllerIdFromFxml_after.java b/plugins/javaFX/testData/rename/NestedControllerIdFromFxml_after.java new file mode 100644 index 000000000000..150b4cf39dba --- /dev/null +++ b/plugins/javaFX/testData/rename/NestedControllerIdFromFxml_after.java @@ -0,0 +1,5 @@ +import javafx.fxml.FXML; + +class NestedControllerIdFromFxml { + @FXML NestedControllerIdFromFxmlInternal newNameController; +} \ No newline at end of file diff --git a/plugins/javaFX/testData/rename/NestedControllerIdFromJava.java b/plugins/javaFX/testData/rename/NestedControllerIdFromJava.java new file mode 100644 index 000000000000..ae21b99732b4 --- /dev/null +++ b/plugins/javaFX/testData/rename/NestedControllerIdFromJava.java @@ -0,0 +1,8 @@ +import javafx.fxml.FXML; +import javafx.scene.layout.VBox; + +class NestedControllerIdFromJava { + @FXML + private VBox internal; + @FXML NestedControllerIdFromJavaInternal internalController; +} \ No newline at end of file diff --git a/plugins/javaFX/testData/rename/NestedControllerIdFromJavaInternal.java b/plugins/javaFX/testData/rename/NestedControllerIdFromJavaInternal.java new file mode 100644 index 000000000000..327fb485706a --- /dev/null +++ b/plugins/javaFX/testData/rename/NestedControllerIdFromJavaInternal.java @@ -0,0 +1,2 @@ +class NestedControllerIdFromJavaInternal { +} \ No newline at end of file diff --git a/plugins/javaFX/testData/rename/NestedControllerIdFromJava_after.java b/plugins/javaFX/testData/rename/NestedControllerIdFromJava_after.java new file mode 100644 index 000000000000..796133317dc2 --- /dev/null +++ b/plugins/javaFX/testData/rename/NestedControllerIdFromJava_after.java @@ -0,0 +1,8 @@ +import javafx.fxml.FXML; +import javafx.scene.layout.VBox; + +class NestedControllerIdFromJava { + @FXML + private VBox newName; + @FXML NestedControllerIdFromJavaInternal newNameController; +} \ No newline at end of file diff --git a/plugins/javaFX/testData/rename/nestedControllerIdFromFxml.fxml b/plugins/javaFX/testData/rename/nestedControllerIdFromFxml.fxml new file mode 100644 index 000000000000..fa1b7ad93447 --- /dev/null +++ b/plugins/javaFX/testData/rename/nestedControllerIdFromFxml.fxml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/plugins/javaFX/testData/rename/nestedControllerIdFromFxmlInternal.fxml b/plugins/javaFX/testData/rename/nestedControllerIdFromFxmlInternal.fxml new file mode 100644 index 000000000000..cf287a6a296a --- /dev/null +++ b/plugins/javaFX/testData/rename/nestedControllerIdFromFxmlInternal.fxml @@ -0,0 +1,5 @@ + + + +