/
ChangeMethodSignatureDialog.java
327 lines (282 loc) · 14.5 KB
/
ChangeMethodSignatureDialog.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
package jetbrains.mps.java.workbench.actions;
/*Generated by MPS */
import jetbrains.mps.ide.platform.refactoring.RefactoringDialog;
import org.jetbrains.mps.openapi.model.SNodeChangeListener;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.baseLanguage.util.plugin.refactorings.ChangeMethodSignatureParameters;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.ide.embeddableEditor.EmbeddableEditor;
import jetbrains.mps.project.MPSProject;
import java.util.List;
import jetbrains.mps.baseLanguage.util.plugin.refactorings.ChangeMethodSignatureRefactoring;
import jetbrains.mps.checkers.IChecker;
import jetbrains.mps.errors.item.NodeReportItem;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.checkers.ConstraintsChecker;
import javax.swing.JComponent;
import com.intellij.openapi.ui.DialogPanel;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SConceptOperations;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import jetbrains.mps.smodel.tempmodel.TemporaryModels;
import jetbrains.mps.smodel.tempmodel.TempModuleOptions;
import javax.swing.border.TitledBorder;
import com.intellij.openapi.ui.ValidationInfo;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.errors.item.IssueKindReportItem;
import jetbrains.mps.progress.EmptyProgressMonitor;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.annotations.Nullable;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import jetbrains.mps.baseLanguage.closures.runtime.Wrappers;
import org.jetbrains.mps.openapi.language.SConcept;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.progress.Task;
import com.intellij.openapi.progress.ProgressIndicator;
import org.jetbrains.mps.openapi.module.ModelAccess;
import jetbrains.mps.baseLanguage.util.plugin.refactorings.MethodRefactoringUtils;
import jetbrains.mps.progress.ProgressMonitorAdapter;
import com.intellij.openapi.ui.Messages;
import com.intellij.refactoring.RefactoringBundle;
import java.util.Map;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import java.util.Objects;
import org.jetbrains.mps.openapi.event.SPropertyChangeEvent;
import org.jetbrains.mps.openapi.event.SReferenceChangeEvent;
import org.jetbrains.mps.openapi.event.SNodeAddEvent;
import org.jetbrains.mps.openapi.event.SNodeRemoveEvent;
import jetbrains.mps.smodel.builder.SNodeBuilder;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SInterfaceConcept;
/*package*/ class ChangeMethodSignatureDialog extends RefactoringDialog implements SNodeChangeListener {
private SNode myDeclaration;
private ChangeMethodSignatureParameters myParameters;
private SModel myTempModel;
private EmbeddableEditor myEditor;
private MPSProject myProject;
private List<ChangeMethodSignatureRefactoring> myRefactorings = null;
private IChecker.AbstractRootChecker<NodeReportItem> myConstraintChecker = null;
private boolean myTriedStatic;
public ParamDefautValueSectionPanel myDefaultValuePanel;
public ChangeMethodSignatureDialog(@NotNull MPSProject project, SNode node) {
super(project.getProject(), true);
setTitle("Change Method Signature");
setValidationDelay(500);
this.myProject = project;
this.myDeclaration = node;
this.myConstraintChecker = new ConstraintsChecker(null).asRootChecker();
// TODO call this constructor inside read action?
myProject.getModelAccess().runReadAction(() -> ChangeMethodSignatureDialog.this.myParameters = new ChangeMethodSignatureParameters(myDeclaration));
init();
// Initially disable (no change)
initValidation();
}
/**
* Create the panel for the method signature, and affect to the parent panel the component
* to be focused first
*
* @param parentPanel parent on which the focused component will be set on
* @return created component
*/
private JComponent createSignaturePanel(final DialogPanel parentPanel) {
JPanel panel = new JPanel(new BorderLayout());
myProject.getRepository().getModelAccess().executeCommand(() -> {
SNode baseMethodDeclaration = ChangeMethodSignatureDialog.this.myParameters.getDeclaration();
SLinkOperations.setTarget(baseMethodDeclaration, LINKS.body$5xQk, SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x4975dc2bdcfa0c49L, "jetbrains.mps.baseLanguage.structure.StubStatementList")));
myTempModel = TemporaryModels.getInstance().createEditable(true, TempModuleOptions.forDefaultModule());
myTempModel.addRootNode(baseMethodDeclaration);
myTempModel.addChangeListener(myDefaultValuePanel);
myTempModel.addChangeListener(ChangeMethodSignatureDialog.this);
TemporaryModels.getInstance().addMissingImports(myTempModel);
ChangeMethodSignatureDialog.this.myEditor = new SizedEmbeddableEditor(myProject, true, 300);
myEditor.editNode(baseMethodDeclaration);
// Set focus on the editor
if (myEditor.getEditor().getCurrentEditorComponent() instanceof JComponent) {
parentPanel.setPreferredFocusedComponent(as_vatimf_a0a0a0n0a0a1a41(myEditor.getEditor().getCurrentEditorComponent(), JComponent.class));
}
});
panel.setBorder(new TitledBorder("Method signature"));
panel.add(this.myEditor);
return panel;
}
/**
* Return all the refactorings objects, which contains the data on which method refactor
*
* @return list of refactorings
*/
public List<ChangeMethodSignatureRefactoring> getAllRefactorings() {
return myRefactorings;
}
@NotNull
@Override
protected List<ValidationInfo> doValidateAll() {
final List<ValidationInfo> issues = ListSequence.fromList(new ArrayList<ValidationInfo>());
// TODO this manual checking occur because it is quite difficult to suppress errors if validation is enabled. One option is to suppress with the node attribute but it would leave an unwanted visible info
myProject.getRepository().getModelAccess().runReadAction(() -> {
if (!(myParameters.hasChanges())) {
ListSequence.fromList(issues).addElement(new ValidationInfo("no changes", myEditor));
}
// Check constraints
ChangeMethodSignatureDialog.this.myConstraintChecker.check(myParameters.getDeclaration(), myProject.getRepository(), (it) -> {
// Ignore not rootable error
if (!(IssueKindReportItem.CONSTRAINTS.deriveItemKind("not rootable").equals(it.getIssueKind()))) {
ListSequence.fromList(issues).addElement(new ValidationInfo(it.getMessage()));
}
}, new EmptyProgressMonitor());
// No abstract concept
if (ListSequence.fromList(SNodeOperations.getNodeDescendants(myParameters.getDeclaration(), null, false, new SAbstractConcept[]{})).any((it) -> SNodeOperations.getConcept(it).isAbstract())) {
ListSequence.fromList(issues).addElement(new ValidationInfo("Abstract concept instance detected. Use one of sub-concepts instead."));
}
// Type of default values
myDefaultValuePanel.validate(issues);
});
if (myTriedStatic) {
ListSequence.fromList(issues).addElement(new ValidationInfo("Static/non static transformation not allowed in this refactoring.").asWarning().withOKEnabled());
}
return issues;
}
@Nullable
@Override
protected JComponent createCenterPanel() {
// Create default panel first because createSignaturePanel below requires it
myDefaultValuePanel = new ParamDefautValueSectionPanel(myProject, this.myParameters.getDeclaration(), () -> {
// Revalidate when children panel updates
// TODO only revalidate children?
ChangeMethodSignatureDialog.this.initValidation();
});
DialogPanel panel = new DialogPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(3, 3, 3, 3);
c.gridx = 0;
c.weightx = 1;
// Signature
c.fill = GridBagConstraints.BOTH;
c.gridy = 0;
c.weighty = 1;
panel.add(this.createSignaturePanel(panel), c);
// Default values
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = 1;
c.weighty = 0;
panel.add(myDefaultValuePanel, c);
return panel;
}
/**
* This method will be called on pressing "Refactor" button in dialog.
*
* First find overriding methods, then create a refactoring objects for each with the default values
* specified in the default value panel.
*
* @see jetbrains.mps.java.workbench.actions.ChangeMethodSignatureDialog#getAllRefactorings()
*/
@Override
protected void doOKAction() {
final List<SNode> methodsToRefactor = new ArrayList<SNode>();
final Wrappers._T<SConcept> changeVisibilityFor = new Wrappers._T<SConcept>(null);
final Wrappers._boolean isChangeMandatory = new Wrappers._boolean(false);
ProgressManager.getInstance().run(new Task.Modal(getProject(), "Search for overriding methods", true) {
@Override
public void run(@NotNull final ProgressIndicator indicator) {
ModelAccess modelAccess = ChangeMethodSignatureDialog.this.myProject.getRepository().getModelAccess();
modelAccess.runReadAction(() -> {
ListSequence.fromList(methodsToRefactor).addSequence(ListSequence.fromList(MethodRefactoringUtils.findOverridingMethods(ChangeMethodSignatureDialog.this.myProject.new ProjectScope(), ChangeMethodSignatureDialog.this.myDeclaration, new ProgressMonitorAdapter(indicator))));
// Use same read action
if (myParameters.isVisibilityChanged() && ListSequence.fromList(methodsToRefactor).isNotEmpty()) {
changeVisibilityFor.value = SNodeOperations.getConcept(SLinkOperations.getTarget(SNodeOperations.cast(myParameters.getDeclaration(), CONCEPTS.IVisible$zu), LINKS.visibility$Yyua));
isChangeMandatory.value = myParameters.isMandatoryVisibilityChange();
}
});
}
});
boolean changeVisibility = false;
if (changeVisibilityFor.value != null && !(isChangeMandatory.value)) {
// [java-impl.jar] JavaRefactoringBundle "dialog.message.overriding.methods.with.weaken.visibility"
String message = String.format("Do you want to reduce the visibility of overriding methods to '%s' as well?", SConceptOperations.conceptAlias(changeVisibilityFor.value));
changeVisibility = Messages.showYesNoDialog(myProject.getProject(), message, RefactoringBundle.message("changeSignature.refactoring.name"), Messages.getQuestionIcon()) == Messages.YES;
} else if (changeVisibilityFor.value != null) {
// TODO internationalization
boolean cancel = Messages.showYesNoDialog(myProject.getProject(), String.format("Overriding methods visibility will be reduced to '%s'. Proceed?", SConceptOperations.conceptAlias(changeVisibilityFor.value)), RefactoringBundle.message("changeSignature.refactoring.name"), Messages.getQuestionIcon()) == Messages.NO;
if (cancel) {
return;
}
}
ListSequence.fromList(methodsToRefactor).addElement(myDeclaration);
// Get default values for new parameters (if any)
final Wrappers._T<Map<SNode, SNode>> defaultValues = new Wrappers._T<Map<SNode, SNode>>(MapSequence.fromMap(new HashMap<SNode, SNode>()));
myProject.getRepository().getModelAccess().runReadAction(() -> defaultValues.value = myDefaultValuePanel.getDefaultValues());
// Create refactorings
myRefactorings = ListSequence.fromList(new ArrayList<ChangeMethodSignatureRefactoring>());
for (SNode method : ListSequence.fromList(methodsToRefactor)) {
ListSequence.fromList(myRefactorings).addElement(new ChangeMethodSignatureRefactoring(this.myParameters, method, defaultValues.value, changeVisibility || Objects.equals(method, myDeclaration)));
}
super.doOKAction();
}
@Override
protected void dispose() {
if (myEditor != null) {
myEditor.disposeEditor();
myEditor = null;
}
if (myDefaultValuePanel != null) {
myDefaultValuePanel.dispose();
}
if (myTempModel != null) {
myProject.getModelAccess().runWriteAction(() -> {
TemporaryModels.getInstance().dispose(myTempModel);
myTempModel = null;
});
}
super.dispose();
}
@Override
public void propertyChanged(@NotNull SPropertyChangeEvent event) {
initValidation();
}
@Override
public void referenceChanged(@NotNull SReferenceChangeEvent event) {
initValidation();
}
@Override
public void nodeAdded(@NotNull SNodeAddEvent event) {
initValidation();
}
@Override
public void nodeRemoved(@NotNull SNodeRemoveEvent event) {
SNode declaration = myParameters.getDeclaration();
// If the node we were editing was removed
if (event.getChild() == declaration) {
// Add it back
myTempModel.addRootNode(declaration);
// Display warning
myTriedStatic = true;
}
// Ensure the body is a stub statement list
if ((SLinkOperations.getTarget(declaration, LINKS.body$5xQk) == null) || !(SNodeOperations.isInstanceOf(SLinkOperations.getTarget(declaration, LINKS.body$5xQk), CONCEPTS.StubStatementList$v6))) {
SLinkOperations.setTarget(declaration, LINKS.body$5xQk, createStubStatementList_vatimf_a0a0g0db());
}
// Revalidate
initValidation();
}
private static SNode createStubStatementList_vatimf_a0a0g0db() {
SNodeBuilder n0 = new SNodeBuilder().init(CONCEPTS.StubStatementList$v6);
return n0.getResult();
}
private static <T> T as_vatimf_a0a0a0n0a0a1a41(Object o, Class<T> type) {
return (type.isInstance(o) ? (T) o : null);
}
private static final class LINKS {
/*package*/ static final SContainmentLink body$5xQk = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b1fcL, 0xf8cc56b1ffL, "body");
/*package*/ static final SContainmentLink visibility$Yyua = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x112670d273fL, 0x112670d886aL, "visibility");
}
private static final class CONCEPTS {
/*package*/ static final SInterfaceConcept IVisible$zu = MetaAdapterFactory.getInterfaceConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x112670d273fL, "jetbrains.mps.baseLanguage.structure.IVisible");
/*package*/ static final SConcept StubStatementList$v6 = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x4975dc2bdcfa0c49L, "jetbrains.mps.baseLanguage.structure.StubStatementList");
}
}