/* * Copyright 2013-2017 consulo.io * * 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 consulo.csharp.ide.actions.generate; import java.awt.BorderLayout; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.List; import javax.swing.Icon; import javax.swing.JCheckBox; import javax.swing.JComponent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import consulo.annotations.RequiredDispatchThread; import consulo.annotations.RequiredReadAction; import consulo.csharp.ide.codeInsight.actions.AddAccessModifierFix; import consulo.csharp.ide.codeStyle.CSharpCodeGenerationSettings; import consulo.csharp.lang.psi.CSharpAccessModifier; import consulo.csharp.lang.psi.CSharpModifier; import consulo.csharp.lang.psi.CSharpReferenceExpression; import consulo.csharp.lang.psi.CSharpTypeDeclaration; import consulo.dotnet.psi.DotNetFieldDeclaration; import consulo.dotnet.psi.DotNetModifier; import com.intellij.codeInsight.CodeInsightActionHandler; import consulo.ide.IconDescriptorUpdaters; import com.intellij.ide.util.ChooseElementsDialog; import com.intellij.ide.util.PropertiesComponentImpl; import com.intellij.openapi.application.Result; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiReference; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Function; import com.intellij.util.Processor; import com.intellij.util.Query; /** * @author VISTALL * @since 24.07.2015 */ public class GeneratePropertyHandler implements CodeInsightActionHandler { private static final String ourReplaceReferencesToProperty = "csharp.replace.references.to.property"; private boolean myReadonly; public GeneratePropertyHandler(boolean readonly) { myReadonly = readonly; } @RequiredDispatchThread @Override public void invoke(@NotNull final Project project, @NotNull final Editor editor, @NotNull final PsiFile file) { final CSharpTypeDeclaration typeDeclaration = CSharpGenerateAction.findTypeDeclaration(editor, file); if(typeDeclaration == null) { return; } List<DotNetFieldDeclaration> fields = GeneratePropertyAction.getFields(typeDeclaration); if(fields.isEmpty()) { return; } ChooseElementsDialog<DotNetFieldDeclaration> dialog = new ChooseElementsDialog<DotNetFieldDeclaration>(project, fields, "Choose field(s)", "Choose field(s) for property generate", true) { @Override protected JComponent createCenterPanel() { JComponent centerPanel = super.createCenterPanel(); assert centerPanel != null; final JCheckBox replaceUsageToProperty = new JCheckBox("Replace references to property?", PropertiesComponentImpl.getInstance().getBoolean(ourReplaceReferencesToProperty, true)); replaceUsageToProperty.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { PropertiesComponentImpl.getInstance().setValue(ourReplaceReferencesToProperty, replaceUsageToProperty.isSelected(), true); } }); centerPanel.add(replaceUsageToProperty, BorderLayout.SOUTH); return centerPanel; } @Override protected String getItemText(DotNetFieldDeclaration item) { return item.getName(); } @Nullable @Override protected Icon getItemIcon(DotNetFieldDeclaration item) { return IconDescriptorUpdaters.getIcon(item, Iconable.ICON_FLAG_VISIBILITY); } }; final List<DotNetFieldDeclaration> fieldDeclarations = dialog.showAndGetResult(); if(fieldDeclarations.isEmpty()) { return; } final int startOffset = editor.getCaretModel().getOffset(); final String lineIndent = CodeStyleManager.getInstance(project).getLineIndent(editor.getDocument(), startOffset); final String allText = StringUtil.join(fieldDeclarations, new Function<DotNetFieldDeclaration, String>() { @Override @RequiredReadAction public String fun(DotNetFieldDeclaration fieldDeclaration) { return generatePropertyTextFromField(lineIndent, fieldDeclaration); } }, "\n\n"); PsiDocumentManager.getInstance(project).commitAllDocuments(); new Task.Backgroundable(project, "Searching references", true) { @Override public void run(@NotNull ProgressIndicator indicator) { new WriteCommandAction(project, "Generate property", file) { @Override @RequiredDispatchThread protected void run(Result result) throws Throwable { if(PropertiesComponentImpl.getInstance().getBoolean(ourReplaceReferencesToProperty, true)) { for(final DotNetFieldDeclaration fieldDeclaration : fieldDeclarations) { new AddAccessModifierFix(CSharpModifier.PRIVATE).invoke(project, editor, fieldDeclaration.getNameIdentifier()); final String propertyName = getPropertyName(project, fieldDeclaration.hasModifier(DotNetModifier.STATIC), fieldDeclaration.getName()); Query<PsiReference> search = ReferencesSearch.search(fieldDeclaration); search.forEach(new Processor<PsiReference>() { @Override public boolean process(PsiReference psiReference) { if(psiReference instanceof CSharpReferenceExpression) { psiReference.handleElementRename(propertyName); } return true; } }); } } PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); editor.getDocument().insertString(startOffset, allText); PsiDocumentManager.getInstance(project).doPostponedOperationsAndUnblockDocument(editor.getDocument()); CodeStyleManager.getInstance(project).reformatText(file, startOffset, startOffset + allText.length()); } }.execute(); } }.queue(); } @NotNull public static String getClearFieldName(@NotNull Project project, boolean isStatic, @NotNull String name) { CSharpCodeGenerationSettings customSettings = CSharpCodeGenerationSettings.getInstance(project); String prefix = isStatic ? customSettings.STATIC_FIELD_PREFIX : customSettings.FIELD_PREFIX; String suffix = isStatic ? customSettings.STATIC_FIELD_SUFFIX : customSettings.FIELD_SUFFIX; if(!prefix.isEmpty()) { if(name.startsWith(prefix)) { name = name.substring(prefix.length(), name.length()); } } if(!suffix.isEmpty()) { if(name.endsWith(suffix)) { name = name.substring(0, name.length() - suffix.length()); } } return name; } public static String getPropertyName(@NotNull Project project, boolean isStatic, @NotNull String fieldName) { CSharpCodeGenerationSettings customSettings = CSharpCodeGenerationSettings.getInstance(project); String prefix = isStatic ? customSettings.STATIC_PROPERTY_PREFIX : customSettings.PROPERTY_PREFIX; String suffix = isStatic ? customSettings.STATIC_PROPERTY_SUFFIX : customSettings.PROPERTY_SUFFIX; return prefix + StringUtil.toTitleCase(getClearFieldName(project, isStatic, fieldName)) + suffix; } @RequiredReadAction private String generatePropertyTextFromField(String lineIndent, DotNetFieldDeclaration fieldDeclaration) { StringBuilder builder = new StringBuilder(); builder.append(lineIndent); CSharpAccessModifier accessModifier = CSharpAccessModifier.findModifier(fieldDeclaration); if(accessModifier != CSharpAccessModifier.NONE) { builder.append(accessModifier.getPresentableText()).append(" "); } if(fieldDeclaration.hasModifier(DotNetModifier.STATIC)) { builder.append("static "); } builder.append(fieldDeclaration.getType().getText()).append(" "); String fieldName = fieldDeclaration.getName(); builder.append(getPropertyName(fieldDeclaration.getProject(), fieldDeclaration.hasModifier(DotNetModifier.STATIC), fieldName)); builder.append("{get { return ").append(fieldName).append("; }"); if(!myReadonly) { builder.append("set { ").append(fieldName).append(" = value; }"); } builder.append("}"); return builder.toString(); } @Override public boolean startInWriteAction() { return false; } }