package com.intellij.lang.javascript.flex;
import com.intellij.flex.model.bc.OutputType;
import com.intellij.lang.javascript.JavaScriptSupportLoader;
import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfiguration;
import com.intellij.lang.javascript.flex.projectStructure.model.FlexBuildConfigurationManager;
import com.intellij.lang.javascript.flex.projectStructure.model.impl.FlexProjectConfigurationEditor;
import com.intellij.lang.javascript.flex.projectStructure.model.impl.NonStructuralModifiableBuildConfiguration;
import com.intellij.lang.javascript.flex.projectStructure.options.BCUtils;
import com.intellij.lang.javascript.psi.JSFile;
import com.intellij.lang.javascript.psi.ecmal4.*;
import com.intellij.lang.javascript.psi.impl.JSPsiImplUtils;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.impl.DirectoryIndex;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiDirectoryContainer;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.xml.XmlFile;
import com.intellij.refactoring.listeners.RefactoringElementAdapter;
import com.intellij.refactoring.listeners.RefactoringElementListener;
import com.intellij.refactoring.listeners.RefactoringElementListenerProvider;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collection;
public class FlexRefactoringListenerProvider implements RefactoringElementListenerProvider {
@Override
@Nullable
public RefactoringElementListener getListener(final PsiElement element) {
final Module module = ModuleUtilCore.findModuleForPsiElement(element);
if (element instanceof PsiDirectoryContainer || element instanceof JSPackage || element instanceof JSPackageStatement) {
final String packageName = getPackageName(element);
return StringUtil.isEmpty(packageName) ? null : new PackageRefactoringListener(element.getProject(), module, packageName);
}
if (module == null || ModuleType.get(module) != FlexModuleType.getInstance()) return null;
final JSClass jsClass = getJSClass(element);
if (jsClass != null) {
return new JSClassRefactoringListener(module, jsClass.getQualifiedName());
}
final VirtualFile file = element instanceof PsiFile ? element.getContainingFile().getVirtualFile() : null;
if (file != null) {
if ("css".equalsIgnoreCase(file.getExtension())) {
return new CssFileRefactoringListener(module, file.getPath());
}
else if ("xml".equalsIgnoreCase(file.getExtension())) {
return new XmlFileRefactoringListener(module, file.getPath());
}
}
return null;
}
@Nullable
public static JSClass getJSClass(final PsiElement element) {
if (element instanceof JSClass) {
return (JSClass)element;
}
if (element instanceof XmlFile && JavaScriptSupportLoader.isFlexMxmFile((XmlFile)element)) {
return XmlBackedJSClassFactory.getXmlBackedClass((XmlFile)element);
}
if (element instanceof JSFile) {
return JSPsiImplUtils.findClass((JSFile)element);
}
return null;
}
@Nullable
public static String getPackageName(final PsiElement element) {
assert element instanceof PsiDirectoryContainer || element instanceof JSPackage || element instanceof JSPackageStatement;
if (element instanceof PsiDirectoryContainer) {
final PsiDirectory[] directories = ((PsiDirectoryContainer)element).getDirectories();
if (directories.length == 0) return null;
return DirectoryIndex.getInstance(element.getProject()).getPackageName(directories[0].getVirtualFile());
}
return ((JSQualifiedNamedElement)element).getQualifiedName();
}
private static class PackageRefactoringListener extends RefactoringElementAdapter {
private @NotNull final Project myProject;
private @Nullable final Module myModule;
private final String myOldPackageName;
private String myNewPackageName;
public PackageRefactoringListener(final @NotNull Project project, final @Nullable Module module, final String oldPackageName) {
myProject = project;
myModule = module;
myOldPackageName = oldPackageName;
}
@Override
public void elementRenamedOrMoved(@NotNull final PsiElement newElement) {
final String newPackageName = getPackageName(newElement);
if (newPackageName != null) {
myNewPackageName = newPackageName;
updatePackageName(myOldPackageName, myNewPackageName);
}
}
@Override
public void undoElementMovedOrRenamed(@NotNull PsiElement newElement, @NotNull String oldQualifiedName) {
if (myNewPackageName != null) {
updatePackageName(myNewPackageName, myOldPackageName);
}
}
private void updatePackageName(final String oldPackageName, final String newPackageName) {
if (myModule == null) {
for (final Module module : ModuleManager.getInstance(myProject).getModules()) {
if (ModuleType.get(module) == FlexModuleType.getInstance()) {
packageNameChanged(module, oldPackageName, newPackageName);
}
}
}
else {
packageNameChanged(myModule, oldPackageName, newPackageName);
}
}
private static void packageNameChanged(final @NotNull Module module, final String oldPackageName, final String newPackageName) {
final String oldPackageWithDot = oldPackageName + ".";
for (FlexBuildConfiguration bc : FlexBuildConfigurationManager.getInstance(module).getBuildConfigurations()) {
if (bc.getOutputType() == OutputType.Application && bc.getMainClass().startsWith(oldPackageWithDot)) {
final String mainClass = (newPackageName.isEmpty() ? "" : (newPackageName + ".")) +
bc.getMainClass().substring(oldPackageWithDot.length());
FlexProjectConfigurationEditor.makeNonStructuralModification(bc, configuration -> configuration.setMainClass(mainClass));
}
if (BCUtils.canHaveRLMsAndRuntimeStylesheets(bc)) {
final Collection<FlexBuildConfiguration.RLMInfo> oldRLMs = bc.getRLMs();
final Collection<FlexBuildConfiguration.RLMInfo> newRLMs = new ArrayList<>();
boolean changed = false;
for (FlexBuildConfiguration.RLMInfo rlm : oldRLMs) {
if (rlm.MAIN_CLASS.startsWith(oldPackageWithDot)) {
changed = true;
final String mainClass = (newPackageName.isEmpty() ? "" : (newPackageName + ".")) +
rlm.MAIN_CLASS.substring(oldPackageWithDot.length());
final String outputFileName = rlm.OUTPUT_FILE.equals(BCUtils.suggestRLMOutputPath(rlm.MAIN_CLASS))
? BCUtils.suggestRLMOutputPath(mainClass)
: rlm.OUTPUT_FILE;
newRLMs.add(new FlexBuildConfiguration.RLMInfo(mainClass, outputFileName, rlm.OPTIMIZE));
}
else {
newRLMs.add(rlm);
}
}
if (changed) {
FlexProjectConfigurationEditor.makeNonStructuralModification(bc, configuration -> configuration.setRLMs(newRLMs));
}
}
}
}
}
private static class JSClassRefactoringListener extends RefactoringElementAdapter {
private final Module myModule;
private final String myOldClassName;
private String myNewClassName;
public JSClassRefactoringListener(final Module module, final String oldClassName) {
myModule = module;
myOldClassName = oldClassName;
}
@Override
public void elementRenamedOrMoved(@NotNull final PsiElement newElement) {
final JSClass newClass = getJSClass(newElement);
if (newClass != null) {
myNewClassName = newClass.getQualifiedName();
classNameChanged(myOldClassName, myNewClassName);
}
}
@Override
public void undoElementMovedOrRenamed(@NotNull PsiElement newElement, @NotNull String oldQualifiedName) {
if (myNewClassName != null) {
classNameChanged(myNewClassName, myOldClassName);
}
}
private void classNameChanged(final String oldClassName, final String newClassName) {
for (FlexBuildConfiguration bc : FlexBuildConfigurationManager.getInstance(myModule).getBuildConfigurations()) {
if (bc.getOutputType() == OutputType.Application && bc.getMainClass().equals(oldClassName)) {
FlexProjectConfigurationEditor.makeNonStructuralModification(bc, configuration -> configuration.setMainClass(newClassName));
}
if (BCUtils.canHaveRLMsAndRuntimeStylesheets(bc)) {
final Collection<FlexBuildConfiguration.RLMInfo> oldRLMs = bc.getRLMs();
final Collection<FlexBuildConfiguration.RLMInfo> newRLMs = new ArrayList<>();
boolean changed = false;
for (FlexBuildConfiguration.RLMInfo rlm : oldRLMs) {
if (rlm.MAIN_CLASS.equals(oldClassName)) {
changed = true;
final String outputFileName = rlm.OUTPUT_FILE.equals(BCUtils.suggestRLMOutputPath(rlm.MAIN_CLASS))
? BCUtils.suggestRLMOutputPath(newClassName)
: rlm.OUTPUT_FILE;
newRLMs.add(new FlexBuildConfiguration.RLMInfo(newClassName, outputFileName, rlm.OPTIMIZE));
}
else {
newRLMs.add(rlm);
}
}
if (changed) {
FlexProjectConfigurationEditor.makeNonStructuralModification(bc, configuration -> configuration.setRLMs(newRLMs));
}
}
}
}
}
private static abstract class FileRefactoringListener extends RefactoringElementAdapter {
protected final @NotNull Module myModule;
protected final String myOldFilePath;
protected String myNewFilePath;
public FileRefactoringListener(@NotNull final Module module, final String oldFilePath) {
myModule = module;
myOldFilePath = oldFilePath;
}
@Override
public void elementRenamedOrMoved(@NotNull final PsiElement newElement) {
final VirtualFile file = newElement instanceof PsiFile ? ((PsiFile)newElement).getVirtualFile() : null;
if (file != null) {
myNewFilePath = file.getPath();
filePathChanged(myOldFilePath, myNewFilePath);
}
}
@Override
public void undoElementMovedOrRenamed(@NotNull PsiElement newElement, @NotNull String oldFilePath) {
if (myNewFilePath != null) {
filePathChanged(myNewFilePath, myOldFilePath);
}
}
protected abstract void filePathChanged(final String oldFilePath, final String newFilePath);
}
private static class CssFileRefactoringListener extends FileRefactoringListener {
public CssFileRefactoringListener(final Module module, final String oldFilePath) {
super(module, oldFilePath);
}
@Override
protected void filePathChanged(final String oldFilePath, final String newFilePath) {
for (FlexBuildConfiguration bc : FlexBuildConfigurationManager.getInstance(myModule).getBuildConfigurations()) {
if (BCUtils.canHaveRLMsAndRuntimeStylesheets(bc)) {
final Collection<String> cssFiles = bc.getCssFilesToCompile();
if (cssFiles.isEmpty()) continue;
final Collection<String> newCssFiles = new ArrayList<>(cssFiles.size());
boolean changed = false;
for (String cssFile : cssFiles) {
if (cssFile.equals(oldFilePath)) {
newCssFiles.add(newFilePath);
changed = true;
}
else {
newCssFiles.add(cssFile);
}
}
if (changed) {
FlexProjectConfigurationEditor.makeNonStructuralModification(bc,
configuration -> configuration.setCssFilesToCompile(newCssFiles));
}
}
}
}
}
private static class XmlFileRefactoringListener extends FileRefactoringListener {
public XmlFileRefactoringListener(final Module module, final String oldFilePath) {
super(module, oldFilePath);
}
@Override
protected void filePathChanged(final String oldFilePath, final String newFilePath) {
for (FlexBuildConfiguration bc : FlexBuildConfigurationManager.getInstance(myModule).getBuildConfigurations()) {
if (bc.getCompilerOptions().getAdditionalConfigFilePath().equals(oldFilePath)) {
FlexProjectConfigurationEditor.makeNonStructuralModification(bc,
configuration -> configuration.getCompilerOptions().setAdditionalConfigFilePath(newFilePath));
}
// TODO update services-config, manifest files
}
}
}
}