From c723cbd972ed0227334a1a67bd777afdc2a9f932 Mon Sep 17 00:00:00 2001 From: greg Date: Thu, 8 May 2008 20:34:57 +0400 Subject: [PATCH] parameter injection settings improved --- plugins/IntelliLang/IntelliLangJava.iml | 1 + .../inject/config/MethodParameterInjection.java | 252 +++--- .../inject/config/ui/MethodParameterPanel.form | 24 +- .../inject/config/ui/MethodParameterPanel.java | 846 ++++++++++----------- 4 files changed, 571 insertions(+), 552 deletions(-) rewrite plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java (61%) diff --git a/plugins/IntelliLang/IntelliLangJava.iml b/plugins/IntelliLang/IntelliLangJava.iml index 644eeed788..69349e4513 100644 --- a/plugins/IntelliLang/IntelliLangJava.iml +++ b/plugins/IntelliLang/IntelliLangJava.iml @@ -30,6 +30,7 @@ + diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java index 135c49b643..e6865940af 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/MethodParameterInjection.java @@ -20,31 +20,22 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.util.Function; -import com.intellij.util.containers.ContainerUtil; +import gnu.trove.THashMap; import org.intellij.plugins.intelliLang.util.PsiUtilEx; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; public class MethodParameterInjection extends BaseInjection { @NotNull private String myClassName = ""; + @NotNull - private String myMethodSignature = ""; - @NotNull - private boolean[] mySelection = new boolean[0]; + private final Map myParameterMap = new THashMap(); private boolean myApplyInHierarchy = true; - @NotNull @NonNls - private String myMethodName = ""; - @NotNull public List getInjectedArea(PsiLiteralExpression element) { return Collections.singletonList(TextRange.from(1, element.getTextLength() - 2)); @@ -71,23 +62,23 @@ public class MethodParameterInjection extends BaseInjection newInfos) { + myParameterMap.clear(); + for (MethodInfo methodInfo : newInfos) { + myParameterMap.put(methodInfo.getMethodSignature(), methodInfo); } - else { - myMethodName = ""; - } - } - - @NotNull - public boolean[] getSelection() { - return mySelection; } - public void setSelection(@NotNull boolean[] indices) { - mySelection = indices; + public Collection getMethodInfos() { + return myParameterMap.values(); } public boolean isApplyInHierarchy() { @@ -147,47 +120,52 @@ public class MethodParameterInjection extends BaseInjection map = new THashMap(); + JDOMExternalizer.readMap(e, map, null, "SIGNATURES"); + for (String s : map.keySet()) { + myParameterMap.put(s, new MethodInfo(s, map.get(s))); + } + } + private void readOldFormat(final Element e) throws InvalidDataException { final JDOMExternalizableStringList list = new JDOMExternalizableStringList(); list.readExternal(e); - final Boolean[] booleans = new Boolean[list.size()]; - ContainerUtil.map2Array(list, booleans, new Function() { - public Boolean fun(String s) { - return Boolean.valueOf(s); - } - }); - mySelection = new boolean[booleans.length]; - for (int i = 0; i < mySelection.length; i++) { - mySelection[i] = booleans[i]; + if (list.isEmpty()) return; + final boolean[] selection = new boolean[list.size()]; + for (int i = 0; i < list.size(); i++) { + selection[i] = Boolean.parseBoolean(list.get(i)); } + final String methodSignature = JDOMExternalizer.readString(e, "METHOD"); + myParameterMap.put(methodSignature, new MethodInfo(methodSignature, selection)); } protected void writeExternalImpl(Element e) throws WriteExternalException { JDOMExternalizer.write(e, "CLASS", myClassName); - JDOMExternalizer.write(e, "METHOD", myMethodSignature); JDOMExternalizer.write(e, "APPLY_IN_HIERARCHY", myApplyInHierarchy); - - final Boolean[] booleans = new Boolean[mySelection.length]; - for (int i = 0; i < mySelection.length; i++) { - booleans[i] = mySelection[i]; + final THashMap map = new THashMap(); + for (String s : myParameterMap.keySet()) { + map.put(s, myParameterMap.get(s).getFlagsString()); } - //noinspection MismatchedQueryAndUpdateOfCollection - final JDOMExternalizableStringList list = new JDOMExternalizableStringList(); - list.addAll(ContainerUtil.map2List(booleans, new Function() { - public String fun(Boolean s) { - return s.toString(); - } - })); - list.writeExternal(e); + JDOMExternalizer.writeMap(e, map, null, "SIGNATURES"); + } + + + @Override + public MethodParameterInjection copy() { + final MethodParameterInjection result = new MethodParameterInjection(); + result.copyFrom(this); + return result; } @SuppressWarnings({"RedundantIfStatement"}) @@ -199,9 +177,8 @@ public class MethodParameterInjection extends BaseInjection"; + MethodInfo singleInfo = null; + main : for (MethodInfo info : myParameterMap.values()) { + for (boolean b : info.paramFlags) { + if (b) { + if (singleInfo == null) { + singleInfo = info; + break; + } + else { + singleInfo = null; + break main; + } + } + } + } + final String name = singleInfo != null + ? StringUtil.getShortName(className) + "." + singleInfo.methodName + : StringUtil.getShortName(className); + return name + " ("+StringUtil.getPackageName(className)+")"; } - public String getDisplayName() { - final String name = getMethodName(); - final String clazz = StringUtil.getShortName(getClassName()); - return clazz.length() > 0 ? clazz + "." + name : name; + public static class MethodInfo { + @NotNull + final String methodSignature; + @NotNull + final String methodName; + @NotNull + final boolean[] paramFlags; + + public MethodInfo(@NotNull final String methodSignature, @NotNull final boolean[] paramFlags) { + this.methodSignature = methodSignature; + this.paramFlags = paramFlags; + methodName = calcMethodName(methodSignature); + } + + public MethodInfo(@NotNull final String methodSignature, @NotNull final String paramFlags) { + this.methodSignature = methodSignature; + this.paramFlags = calcParamFlags(paramFlags); + methodName = calcMethodName(methodSignature); + } + + @NotNull + public String getMethodSignature() { + return methodSignature; + } + + @NotNull + public boolean[] getParamFlags() { + return paramFlags; + } + + public boolean isEnabled() { + for (boolean b : paramFlags) { + if (b) return true; + } + return false; + } + + private static boolean[] calcParamFlags(final String string) { + final StringTokenizer st = new StringTokenizer(string, ","); + final boolean[] result = new boolean[st.countTokens()]; + for (int i = 0; i < result.length; i++) { + result[i] = Boolean.parseBoolean(st.nextToken()); + } + return result; + } + + private static String calcMethodName(final String methodSignature) { + final String s = methodSignature.split("\\(", 2)[0]; + return s.length() != 0 ? s : ""; + } + + public String getFlagsString() { + final StringBuilder result = new StringBuilder(); + boolean first = true; + for (boolean b : paramFlags) { + if (first) first = false; + else result.append(','); + result.append(b); + } + return result.toString(); + } + + public MethodInfo copy() { + return new MethodInfo(methodSignature, paramFlags.clone()); + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + final MethodInfo that = (MethodInfo)o; + + if (!methodName.equals(that.methodName)) return false; + if (!methodSignature.equals(that.methodSignature)) return false; + if (!Arrays.equals(paramFlags, that.paramFlags)) return false; + + return true; + } + + public int hashCode() { + int result; + result = methodSignature.hashCode(); + result = 31 * result + methodName.hashCode(); + result = 31 * result + Arrays.hashCode(paramFlags); + return result; + } } } diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form index d559601806..d484bbf8fc 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.form @@ -1,6 +1,6 @@
- + @@ -40,28 +40,10 @@ - - - - - - - - - - - - - - - - - - - + @@ -73,7 +55,7 @@ - + diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java dissimilarity index 61% index e3ac5cc9cf..2350242822 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/inject/config/ui/MethodParameterPanel.java @@ -1,443 +1,403 @@ -/* - * Copyright 2006 Sascha Weinreuter - * - * 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.intellij.plugins.intelliLang.inject.config.ui; - -import com.intellij.codeInsight.generation.ClassMember; -import com.intellij.codeInsight.generation.PsiMethodMember; -import com.intellij.ide.util.MemberChooser; -import com.intellij.ide.util.TreeClassChooser; -import com.intellij.ide.util.TreeClassChooserFactory; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; -import com.intellij.openapi.util.Condition; -import com.intellij.psi.*; -import com.intellij.psi.impl.compiled.ClsParameterListImpl; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.MethodSignature; -import com.intellij.psi.util.PsiFormatUtil; -import com.intellij.ui.BooleanTableCellRenderer; -import com.intellij.ui.ReferenceEditorWithBrowseButton; -import com.intellij.ui.table.TableView; -import com.intellij.util.Function; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.containers.ContainerUtil; -import com.intellij.util.ui.ColumnInfo; -import com.intellij.util.ui.ListTableModel; -import org.intellij.plugins.intelliLang.inject.config.MethodParameterInjection; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import javax.swing.table.TableCellEditor; -import javax.swing.table.TableCellRenderer; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Arrays; -import java.util.Collections; - -public class MethodParameterPanel extends AbstractInjectionPanel { - private final ParamModel myParamModel; - - LanguagePanel myLanguagePanel; // read by reflection - - private JPanel myRoot; - private JPanel myClassPanel; - private JCheckBox myHierarchy; - - private JPanel myMethodPanel; - - private ReferenceEditorWithBrowseButton myClassField; - private ReferenceEditorWithBrowseButton myMethodField; - - private JTable myParamsTable; - - private PsiMethod myMethod; - - public MethodParameterPanel(MethodParameterInjection injection, final Project project) { - super(injection, project); - $$$setupUI$$$(); // see IDEA-9987 - - myParamModel = (ParamModel)myParamsTable.getModel(); - myParamModel.setSortable(false); - myParamModel.setItems(Collections.emptyList()); - - final TreeUpdateListener updateListener = new TreeUpdateListener(); - myClassField = new ReferenceEditorWithBrowseButton(new BrowseClassListener(project), project, new Function() { - public Document fun(String s) { - final Document document = ReferenceEditorWithBrowseButton.createTypeDocument(s, PsiManager.getInstance(project)); - document.addDocumentListener(updateListener); - return document; - } - }, ""); - myClassPanel.add(myClassField, BorderLayout.CENTER); - - myMethodField = new ReferenceEditorWithBrowseButton(new BrowseMethodListener(project), project, new Function() { - public Document fun(String s) { - final Document document = EditorFactory.getInstance().createDocument(s); - document.setReadOnly(true); - document.addDocumentListener(updateListener); - return document; - } - }, ""); - myMethodPanel.add(myMethodField, BorderLayout.CENTER); - - init(injection.copy()); - } - - @Nullable - private PsiType getClassType() { - final PsiDocumentManager dm = PsiDocumentManager.getInstance(myProject); - final PsiFile psiFile = dm.getPsiFile(myClassField.getEditorTextField().getDocument()); - try { - assert psiFile != null; - return ((PsiTypeCodeFragment)psiFile).getType(); - } - catch (PsiTypeCodeFragment.TypeSyntaxException e1) { - return null; - } - catch (PsiTypeCodeFragment.NoTypeException e1) { - return null; - } - } - - @Nullable - private PsiClass findClass(PsiType type) { - if (type instanceof PsiClassType) { - final JavaPsiFacade facade = JavaPsiFacade.getInstance(myProject); - return facade.findClass(type.getCanonicalText(), GlobalSearchScope.allScope(myProject)); - } - return null; - } - - private void setMethodName(String s) { - myMethodField.setText(s); - updateParameters(); - } - - private void setClassName(String name) { - myClassField.setText(name); - myMethod = null; - setMethodName(""); - } - - public String getClassName() { - final PsiType type = getClassType(); - if (type == null) { - return myClassField.getText(); - } - return type.getCanonicalText(); - } - - @Nullable - private PsiMethod makeMethod(String signature) { - try { - if (signature.trim().length() > 0) { - final JavaPsiFacade facade = JavaPsiFacade.getInstance(myProject); - final PsiElementFactory ef = facade.getElementFactory(); - return ef.createMethodFromText("void " + signature + "{}", null); - } - } - catch (IncorrectOperationException e) { - // signature is not in form NAME(TYPE NAME) - } - return null; - } - - private void setSelection(boolean[] selectedIndices) { - final java.util.List list = myParamModel.getItems(); - for (int i = 0; i < selectedIndices.length; i++) { - if (list.size() > i) { - final Param param = list.get(i); - if (param.isEditable()) { - param.selected = selectedIndices[i]; - } - } - } - } - - private boolean[] getSelection() { - final java.util.List list = myParamModel.getItems(); - final boolean[] indices = new boolean[list.size()]; - - for (int i = 0; i < indices.length; i++) { - final Param param = list.get(i); - indices[i] = param.selected; - } - return indices; - } - - protected void apply(MethodParameterInjection other) { - other.setClassName(getClassName()); - other.setMethodSignature(buildSignature(myMethod)); - other.setSelection(getSelection()); - other.setApplyInHierarchy(myHierarchy.isSelected()); - } - - protected void resetImpl() { - setClassName(myOrigInjection.getClassName()); - myMethod = makeMethod(myOrigInjection.getMethodSignature()); - if (myMethod != null) { - setMethodName(myMethod.getName()); - } - setSelection(myOrigInjection.getSelection()); - myHierarchy.setSelected(myOrigInjection.isApplyInHierarchy()); - } - - @Nullable - private String buildSignature(@Nullable PsiMethod method) { - if (method == null) { - return null; - } - - final PsiParameterList list = method.getParameterList(); - final PsiParameter[] parameters = list.getParameters(); - final String s; - if (parameters.length > 0) { - // if there are no sources, parameter names are unknown. This trick gives the "decompiled" names - if (list instanceof ClsParameterListImpl && parameters[0].getName() == null) { - s = method.getName() + list.getText(); - } - else { - s = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, - PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_FQ_CLASS_NAMES); - } - } - else { - s = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME, 0) + "()"; - } - return s; - } - - public JPanel getComponent() { - return myRoot; - } - - private void createUIComponents() { - myLanguagePanel = new LanguagePanel(myProject, myOrigInjection); - myParamsTable = new TableView(new ParamModel()); - } - - private void updateParameters() { - if (myMethod != null) { - final PsiParameterList list = myMethod.getParameterList(); - final java.util.List params = ContainerUtil.map(list.getParameters(), new Function() { - public Param fun(PsiParameter s) { - return new Param(list.getParameterIndex(s), s); - } - }); - if (!myParamModel.getItems().equals(params)) { - myParamModel.setItems(params); - } - } - else { - myParamModel.setItems(Collections.emptyList()); - } - } - - static class Param { - private final int myIndex; - final String myName; - final PsiType myType; - - public boolean selected; - - public Param(int index, PsiParameter p) { - myIndex = index; - myName = p.getName(); - myType = p.getType(); - } - - public boolean isEditable() { - return isInjectable(myType); - } - - public static boolean isInjectable(PsiType type) { - return type.equalsToText("java.lang.String") || type.equalsToText("java.lang.String...") || type.equalsToText("java.lang.String[]"); - } - - @SuppressWarnings({"RedundantIfStatement"}) - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - final Param param = (Param)o; - - if (myIndex != param.myIndex) return false; - if (myName != null ? !myName.equals(param.myName) : param.myName != null) return false; - - return true; - } - - public int hashCode() { - int result; - result = myIndex; - result = 31 * result + (myName != null ? myName.hashCode() : 0); - return result; - } - } - - static class ParamModel extends ListTableModel { - - public ParamModel() { - super(new ColumnInfo(" ") { // "" for the first column's name isn't a good idea - final BooleanTableCellRenderer myRenderer = new MyCellRenderer(); - - public Boolean valueOf(Param o) { - return o.selected; - } - - public int getWidth(JTable table) { - return myRenderer.getPreferredSize().width; - } - - public TableCellEditor getEditor(Param o) { - return new DefaultCellEditor(new JCheckBox()); - } - - public TableCellRenderer getRenderer(Param param) { - return myRenderer; - } - - public void setValue(Param param, Boolean value) { - param.selected = value; - } - - public Class getColumnClass() { - return Boolean.class; - } - - public boolean isCellEditable(Param param) { - return param.isEditable(); - } - }, new ColumnInfo("Type") { - public String valueOf(Param o) { - return o.myType.getCanonicalText(); - } - }, new ColumnInfo("Name") { - public String valueOf(Param o) { - return o.myName; - } - }); - } - - @SuppressWarnings({"unchecked"}) - private static class MyCellRenderer extends BooleanTableCellRenderer { - public Component getTableCellRendererComponent(JTable table, - Object value, - boolean isSelected, - boolean hasFocus, - int row, - int column) { - final JComponent component = (JComponent)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - final Param p = ((ListTableModel)table.getModel()).getItems().get(row); - setEnabled(p.isEditable()); - return component; - } - } - } - - private class BrowseClassListener implements ActionListener { - private final Project myProject; - - public BrowseClassListener(Project project) { - myProject = project; - } - - public void actionPerformed(ActionEvent e) { - final TreeClassChooserFactory factory = TreeClassChooserFactory.getInstance(myProject); - final TreeClassChooser chooser = factory.createAllProjectScopeChooser("Select Class"); - chooser.showDialog(); - final PsiClass psiClass = chooser.getSelectedClass(); - if (psiClass != null) { - setClassName(psiClass.getQualifiedName()); - updateTree(); - } - } - } - - private class BrowseMethodListener implements ActionListener { - private final Project myProject; - - public BrowseMethodListener(Project project) { - myProject = project; - } - - public void actionPerformed(ActionEvent e) { - final PsiType type = getClassType(); - - final PsiClass psiClass = findClass(type); - if (psiClass != null) { - final PsiMethod[] psiMethods = psiClass.getMethods(); - final java.util.List methods = ContainerUtil.findAll(psiMethods, new Condition() { - public boolean value(PsiMethod method) { - final PsiModifierList modifiers = method.getModifierList(); - if (modifiers.hasModifierProperty(PsiModifier.PRIVATE) || modifiers.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { - return false; - } - final PsiParameter[] parameters = method.getParameterList().getParameters(); - return ContainerUtil.find(parameters, new Condition() { - public boolean value(PsiParameter p) { - return Param.isInjectable(p.getType()); - } - }) != null; - } - }); - final PsiMethodMember[] members = - ContainerUtil.map2Array(methods, PsiMethodMember.class, new Function() { - public PsiMethodMember fun(PsiMethod psiMethod) { - return new PsiMethodMember(psiMethod); - } - }); - final MemberChooser chooser = new MemberChooser(members, false, false, myProject, false); - if (myMethod != null) { - final PsiMethod selection = ContainerUtil.find(methods, new Condition() { - public boolean value(PsiMethod method) { - if (!method.getName().equals(myMethod.getName())) { - return false; - } - final MethodSignature sig1 = method.getSignature(PsiSubstitutor.EMPTY); - final MethodSignature sig2 = myMethod.getSignature(PsiSubstitutor.EMPTY); - return Arrays.equals(sig1.getParameterTypes(), sig2.getParameterTypes()); - } - }); - if (selection != null) { - chooser.selectElements(new ClassMember[]{new PsiMethodMember(selection)}); - } - } - chooser.setCopyJavadocVisible(false); - chooser.setTitle("Select Method"); - chooser.show(); - - final java.util.List selection = chooser.getSelectedElements(); - if (chooser.isOK() && selection != null && selection.size() > 0) { - final PsiMethod method = selection.get(0).getElement(); - myMethod = makeMethod(buildSignature(method)); - setMethodName(method.getName()); - updateTree(); - } - - return; - } - - Messages.showErrorDialog(myProject, "Please select a valid class first", "Method Selection"); - } - } - - private void $$$setupUI$$$() { - } -} +/* + * Copyright 2006 Sascha Weinreuter + * + * 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.intellij.plugins.intelliLang.inject.config.ui; + +import com.intellij.ide.util.TreeClassChooser; +import com.intellij.ide.util.TreeClassChooserFactory; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.event.DocumentAdapter; +import com.intellij.openapi.editor.event.DocumentEvent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.actionSystem.TypeSafeDataProvider; +import com.intellij.openapi.actionSystem.DataKey; +import com.intellij.openapi.actionSystem.DataSink; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.peer.PeerFactory; +import com.intellij.psi.*; +import com.intellij.psi.impl.compiled.ClsParameterListImpl; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.PsiFormatUtil; +import com.intellij.ui.BooleanTableCellRenderer; +import com.intellij.ui.ColoredTreeCellRenderer; +import com.intellij.ui.ReferenceEditorWithBrowseButton; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.dualView.TreeTableView; +import com.intellij.util.Function; +import com.intellij.util.Icons; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.containers.Convertor; +import com.intellij.util.ui.ColumnInfo; +import com.intellij.util.ui.tree.TreeUtil; +import com.intellij.util.ui.treetable.ListTreeTableModelOnColumns; +import com.intellij.util.ui.treetable.TreeColumnInfo; +import gnu.trove.THashMap; +import org.intellij.plugins.intelliLang.inject.config.MethodParameterInjection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.*; +import java.util.List; + +public class MethodParameterPanel extends AbstractInjectionPanel { + + LanguagePanel myLanguagePanel; // read by reflection + + private JPanel myRoot; + private JPanel myClassPanel; + private JCheckBox myHierarchy; + + private TreeTableView myParamsTable; + + private ReferenceEditorWithBrowseButton myClassField; + private DefaultMutableTreeNode myRootNode; + + private THashMap myData = new THashMap(); + + public MethodParameterPanel(MethodParameterInjection injection, final Project project) { + super(injection, project); + + myClassField = new ReferenceEditorWithBrowseButton(new BrowseClassListener(project), project, new Function() { + public Document fun(String s) { + final Document document = ReferenceEditorWithBrowseButton.createTypeDocument(s, PsiManager.getInstance(project)); + document.addDocumentListener(new DocumentAdapter() { + @Override + public void documentChanged(final DocumentEvent e) { + updateParamTree(); + updateTree(); + } + }); + return document; + } + }, ""); + myClassPanel.add(myClassField, BorderLayout.CENTER); + myParamsTable.getTree().setShowsRootHandles(true); + myParamsTable.getTree().setCellRenderer(new ColoredTreeCellRenderer() { + + public void customizeCellRenderer(final JTree tree, + final Object value, + final boolean selected, + final boolean expanded, + final boolean leaf, + final int row, + final boolean hasFocus) { + + final Object o = ((DefaultMutableTreeNode)value).getUserObject(); + setIcon(o instanceof PsiMethod ? Icons.METHOD_ICON : o instanceof PsiParameter ? Icons.PARAMETER_ICON : null); + final String name = o instanceof PsiMethod + ? PsiFormatUtil.formatMethod((PsiMethod)o, PsiSubstitutor.EMPTY, + PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, PsiFormatUtil.SHOW_NAME) + : o instanceof PsiParameter + ? PsiFormatUtil + .formatVariable((PsiParameter)o, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE, PsiSubstitutor.EMPTY) + : null; + final boolean missing = o instanceof PsiElement && !((PsiElement)o).isPhysical(); + if (name != null) { + append(name, missing? SimpleTextAttributes.ERROR_ATTRIBUTES : SimpleTextAttributes.REGULAR_ATTRIBUTES); + } + } + + }); + init(injection.copy()); + PeerFactory.getInstance().getUIHelper().installTreeTableSpeedSearch(myParamsTable, new Convertor() { + @Nullable + public String convert(final TreePath o) { + final Object userObject = ((DefaultMutableTreeNode)o.getLastPathComponent()).getUserObject(); + return userObject instanceof PsiNamedElement? ((PsiNamedElement)userObject).getName() : null; + } + }); + } + + @Nullable + private PsiType getClassType() { + final Document document = myClassField.getEditorTextField().getDocument(); + final PsiDocumentManager dm = PsiDocumentManager.getInstance(myProject); + dm.commitDocument(document); + final PsiFile psiFile = dm.getPsiFile(document); + try { + assert psiFile != null; + return ((PsiTypeCodeFragment)psiFile).getType(); + } + catch (PsiTypeCodeFragment.TypeSyntaxException e1) { + return null; + } + catch (PsiTypeCodeFragment.NoTypeException e1) { + return null; + } + } + + @Nullable + private PsiClass findClass(PsiType type) { + if (type instanceof PsiClassType) { + final JavaPsiFacade facade = JavaPsiFacade.getInstance(myProject); + return facade.findClass(type.getCanonicalText(), GlobalSearchScope.allScope(myProject)); + } + return null; + } + + private void setPsiClass(String name) { + myClassField.setText(name); + } + + private void updateParamTree() { + rebuildTreeModel(); + refreshTreeStructure(); + } + + private void rebuildTreeModel() { + myData.clear(); + final PsiClass psiClass = findClass(getClassType()); + if (psiClass == null) return; + final List methods = ContainerUtil.findAll(psiClass.getMethods(), new Condition() { + public boolean value(final PsiMethod method) { + final PsiModifierList modifiers = method.getModifierList(); + if (modifiers.hasModifierProperty(PsiModifier.PRIVATE) || modifiers.hasModifierProperty(PsiModifier.PACKAGE_LOCAL)) { + return false; + } + final PsiParameter[] parameters = method.getParameterList().getParameters(); + return null != ContainerUtil.find(parameters, new Condition() { + public boolean value(PsiParameter p) { + return isInjectable(p.getType()); + } + }); + } + }); + for (PsiMethod method : methods) { + final String signature = buildSignature(method); + myData.put(method, new MethodParameterInjection.MethodInfo(signature, new boolean[method.getParameterList().getParametersCount()])); + } + } + + private void refreshTreeStructure() { + myRootNode.removeAllChildren(); + final ArrayList methods = new ArrayList(myData.keySet()); + Collections.sort(methods, new Comparator() { + public int compare(final PsiMethod o1, final PsiMethod o2) { + return o1.getName().compareTo(o2.getName()); + } + }); + for (PsiMethod method : methods) { + final PsiParameter[] params = method.getParameterList().getParameters(); + final DefaultMutableTreeNode methodNode = new DefaultMutableTreeNode(method, true); + myRootNode.add(methodNode); + for (final PsiParameter parameter : params) { + methodNode.add(new DefaultMutableTreeNode(parameter, false)); + } + } + final ListTreeTableModelOnColumns tableModel = (ListTreeTableModelOnColumns)myParamsTable.getTableModel(); + tableModel.reload(); + TreeUtil.expandAll(myParamsTable.getTree()); + myParamsTable.revalidate(); + } + + public String getClassName() { + final PsiType type = getClassType(); + if (type == null) { + return myClassField.getText(); + } + return type.getCanonicalText(); + } + + + protected void apply(MethodParameterInjection other) { + other.setClassName(getClassName()); + other.setApplyInHierarchy(myHierarchy.isSelected()); + if (getClassType() != null) { + other.setMethodInfos(ContainerUtil.findAll(myData.values(), new Condition() { + public boolean value(final MethodParameterInjection.MethodInfo methodInfo) { + return methodInfo.isEnabled(); + } + })); + } + } + + protected void resetImpl() { + setPsiClass(myOrigInjection.getClassName()); + myHierarchy.setSelected(myOrigInjection.isApplyInHierarchy()); + + rebuildTreeModel(); + final THashMap map = new THashMap(); + for (PsiMethod method : myData.keySet()) { + map.put(myData.get(method).getMethodSignature(), method); + } + for (MethodParameterInjection.MethodInfo info : myOrigInjection.getMethodInfos()) { + final PsiMethod method = map.get(info.getMethodSignature()); + if (method != null) { + final MethodParameterInjection.MethodInfo curInfo = myData.get(method); + System.arraycopy(info.getParamFlags(), 0, curInfo.getParamFlags(), 0, Math.min(info.getParamFlags().length, curInfo.getParamFlags().length)); + } + else { + final PsiMethod missingMethod = makeMethod(info.getMethodSignature()); + myData.put(missingMethod, info.copy()); + } + } + refreshTreeStructure(); + final Enumeration enumeration = myRootNode.children(); + while (enumeration.hasMoreElements()) { + PsiMethod method = (PsiMethod)((DefaultMutableTreeNode)enumeration.nextElement()).getUserObject(); + assert myData.containsKey(method); + } + } + + @NotNull + private static String buildSignature(@NotNull PsiMethod method) { + final PsiParameterList list = method.getParameterList(); + final PsiParameter[] parameters = list.getParameters(); + final String s; + if (parameters.length > 0) { + // if there are no sources, parameter names are unknown. This trick gives the "decompiled" names + if (list instanceof ClsParameterListImpl && parameters[0].getName() == null) { + s = method.getName() + list.getText(); + } + else { + s = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, + PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_FQ_CLASS_NAMES); + } + } + else { + s = PsiFormatUtil.formatMethod(method, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME, 0) + "()"; + } + return s; + } + + @Nullable + private PsiMethod makeMethod(String signature) { + try { + if (signature.trim().length() > 0) { + final JavaPsiFacade facade = JavaPsiFacade.getInstance(myProject); + final PsiElementFactory ef = facade.getElementFactory(); + return ef.createMethodFromText("void " + signature + "{}", null); + } + } + catch (IncorrectOperationException e) { + // signature is not in form NAME(TYPE NAME) + } + return null; + } + + public JPanel getComponent() { + return myRoot; + } + + private void createUIComponents() { + myLanguagePanel = new LanguagePanel(myProject, myOrigInjection); + myRootNode = new DefaultMutableTreeNode(null, true); + myParamsTable = new MyView(new ListTreeTableModelOnColumns(myRootNode, createColumnInfos())); + } + + private ColumnInfo[] createColumnInfos() { + return new ColumnInfo[]{ + new ColumnInfo(" ") { // "" for the first column's name isn't a good idea + final BooleanTableCellRenderer myRenderer = new BooleanTableCellRenderer(); + + public Boolean valueOf(DefaultMutableTreeNode o) { + final Object userObject = o.getUserObject(); + if (userObject instanceof PsiParameter) { + final PsiMethod method = getMethod(o); + final int index = method.getParameterList().getParameterIndex((PsiParameter)userObject); + return myData.get(method).getParamFlags()[index]; + } + return null; + } + + public int getWidth(JTable table) { + return myRenderer.getPreferredSize().width; + } + + public TableCellEditor getEditor(DefaultMutableTreeNode o) { + return new DefaultCellEditor(new JCheckBox()); + } + + public TableCellRenderer getRenderer(DefaultMutableTreeNode o) { + myRenderer.setEnabled(isCellEditable(o)); + return myRenderer; + } + + public void setValue(DefaultMutableTreeNode o, Boolean value) { + final Object userObject = o.getUserObject(); + if (userObject instanceof PsiParameter) { + final PsiMethod method = getMethod(o); + final int index = method.getParameterList().getParameterIndex((PsiParameter)userObject); + myData.get(method).getParamFlags()[index] = Boolean.TRUE.equals(value); + } + } + + public Class getColumnClass() { + return Boolean.class; + } + + public boolean isCellEditable(DefaultMutableTreeNode o) { + return o.getUserObject() instanceof PsiParameter && isInjectable(((PsiParameter)o.getUserObject()).getType()); + } + + private PsiMethod getMethod(final DefaultMutableTreeNode o) { + return (PsiMethod)((DefaultMutableTreeNode)o.getParent()).getUserObject(); + } + + + }, new TreeColumnInfo("Method/Parameters") + }; + } + + public static boolean isInjectable(PsiType type) { + return type.equalsToText("java.lang.String") || type.equalsToText("java.lang.String...") || type.equalsToText("java.lang.String[]"); + } + + private class BrowseClassListener implements ActionListener { + private final Project myProject; + + public BrowseClassListener(Project project) { + myProject = project; + } + + public void actionPerformed(ActionEvent e) { + final TreeClassChooserFactory factory = TreeClassChooserFactory.getInstance(myProject); + final TreeClassChooser chooser = factory.createAllProjectScopeChooser("Select Class"); + chooser.showDialog(); + final PsiClass psiClass = chooser.getSelectedClass(); + if (psiClass != null) { + setPsiClass(psiClass.getQualifiedName()); + updateParamTree(); + updateTree(); + } + } + } + + private class MyView extends TreeTableView implements TypeSafeDataProvider { + public MyView(ListTreeTableModelOnColumns treeTableModel) { + super(treeTableModel); + } + + public void calcData(final DataKey key, final DataSink sink) { + if (LangDataKeys.PSI_ELEMENT.equals(key)) { + final Collection selection = getSelection(); + if (!selection.isEmpty()) { + final Object o = ((DefaultMutableTreeNode)selection.iterator().next()).getUserObject(); + if (o instanceof PsiElement) sink.put(LangDataKeys.PSI_ELEMENT, (PsiElement)o); + } + } + } + } +} -- 2.11.4.GIT