Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2010 Joe Handzik, Joe Gonzales, Marc Celani, and Jason Patel. |
3 |
* All rights reserved. This program and the accompanying materials |
4 |
* are made available under the terms of the Eclipse Public License v1.0 |
5 |
* which accompanies this distribution, and is available at |
6 |
* http://www.eclipse.org/legal/epl-v10.html |
7 |
* |
8 |
* Contributors: |
9 |
* Joe Handzik, Joe Gonzales, Marc Celani, and Jason Patel - Initial API and implementation |
10 |
*******************************************************************************/ |
11 |
package org.eclipse.photran.internal.core.refactoring; |
12 |
|
13 |
import java.util.ArrayList; |
14 |
import java.util.Collection; |
15 |
import java.util.Collections; |
16 |
import java.util.HashSet; |
17 |
import java.util.List; |
18 |
import java.util.Set; |
19 |
|
20 |
import org.eclipse.core.runtime.CoreException; |
21 |
import org.eclipse.core.runtime.IProgressMonitor; |
22 |
import org.eclipse.core.runtime.OperationCanceledException; |
23 |
import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
24 |
import org.eclipse.ltk.core.refactoring.RefactoringStatusContext; |
25 |
import org.eclipse.photran.internal.core.analysis.binding.Definition; |
26 |
import org.eclipse.photran.internal.core.analysis.binding.ScopingNode; |
27 |
import org.eclipse.photran.internal.core.lexer.Terminal; |
28 |
import org.eclipse.photran.internal.core.lexer.Token; |
29 |
import org.eclipse.photran.internal.core.parser.ASTCallStmtNode; |
30 |
import org.eclipse.photran.internal.core.parser.ASTEntityDeclNode; |
31 |
import org.eclipse.photran.internal.core.parser.ASTIntConstNode; |
32 |
import org.eclipse.photran.internal.core.parser.ASTListNode; |
33 |
import org.eclipse.photran.internal.core.parser.ASTSeparatedListNode; |
34 |
import org.eclipse.photran.internal.core.parser.ASTSubroutineArgNode; |
35 |
import org.eclipse.photran.internal.core.parser.ASTSubroutineParNode; |
36 |
import org.eclipse.photran.internal.core.parser.ASTSubroutineStmtNode; |
37 |
import org.eclipse.photran.internal.core.parser.ASTSubroutineSubprogramNode; |
38 |
import org.eclipse.photran.internal.core.parser.ASTTypeDeclarationStmtNode; |
39 |
import org.eclipse.photran.internal.core.parser.IASTListNode; |
40 |
import org.eclipse.photran.internal.core.parser.IASTNode; |
41 |
import org.eclipse.photran.internal.core.parser.IBodyConstruct; |
42 |
import org.eclipse.photran.internal.core.refactoring.infrastructure.FortranEditorRefactoring; |
43 |
import org.eclipse.photran.internal.core.reindenter.Reindenter; |
44 |
import org.eclipse.photran.internal.core.vpg.PhotranTokenRef; |
45 |
import org.eclipse.photran.internal.core.vpg.PhotranVPG; |
46 |
|
47 |
/** |
48 |
* |
49 |
* |
50 |
* This refactoring allows a user to select a subroutine and to add a new parameter to the list. The |
51 |
* refactoring will ask for a declaration line for the parameter, a default value with which to |
52 |
* update all callers of the subroutine, and a position in the list at which to add the new |
53 |
* parameter. The refactoring ensures that the declaration line is valid, contains some logic to |
54 |
* ensure that the default value matches the appropriate type, and ensures that the position is in |
55 |
* bounds. It then updates the subroutine signature and updates the callers of the subroutine. If |
56 |
* the callers specify the variable name in the call list, the refactoring will match this pattern. |
57 |
* |
58 |
* @author Joe Handzik, Joe Gonzales, Marc Celani, Jason Patel |
59 |
*/ |
60 |
public class AddSubroutineParameterRefactoring extends FortranEditorRefactoring |
61 |
{ |
62 |
private ASTSubroutineStmtNode selectedSubroutine; |
63 |
|
64 |
private List<ASTSubroutineParNode> oldParameterList; |
65 |
|
66 |
private List<ASTSubroutineParNode> newParameterList; |
67 |
|
68 |
private int position = 0; |
69 |
|
70 |
private String parameterName = null; |
71 |
|
72 |
private String declaration = "integer, intent(in) :: newName"; //$NON-NLS-1$ |
73 |
|
74 |
private String defaultValue = "0"; //$NON-NLS-1$ |
75 |
|
76 |
private ASTTypeDeclarationStmtNode declStmt = null; |
77 |
|
78 |
private String type = "integer"; //$NON-NLS-1$ |
79 |
|
80 |
public List<ASTSubroutineParNode> getOldParameterList() |
81 |
{ |
82 |
return oldParameterList; |
83 |
} |
84 |
|
85 |
public String getDeclaration() |
86 |
{ |
87 |
assert declaration != null; |
88 |
return this.declaration; |
89 |
} |
90 |
|
91 |
public int getPosition() |
92 |
{ |
93 |
return this.position; |
94 |
} |
95 |
|
96 |
public String getDefault() |
97 |
{ |
98 |
assert defaultValue != null; |
99 |
return this.defaultValue; |
100 |
} |
101 |
|
102 |
public void setPosition(int position) |
103 |
{ |
104 |
this.position = position; |
105 |
} |
106 |
|
107 |
/* |
108 |
* Sets the declaration member, but first checks that an appropriate type is at the beggining of |
109 |
* the declaration. If not, it assumes that "real" should be prepended. The type member is then |
110 |
* set to this type. |
111 |
* |
112 |
* @param declaration The declaration to be set. |
113 |
*/ |
114 |
public void setDeclaration(String declaration) |
115 |
{ |
116 |
// Add "real" to all declaration lines that do not specify a type to avoid parser errors. |
117 |
|
118 |
String[] declArgs = declaration.split(","); //$NON-NLS-1$ |
119 |
String[] validTypes = { "integer", "real", "logical", "double", "character" }; //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ |
120 |
boolean hasTypeDefined = false; |
121 |
for (int i = 0; i < validTypes.length; i++) |
122 |
{ |
123 |
if (validTypes[i].equals(declArgs[0])) |
124 |
{ |
125 |
hasTypeDefined = true; |
126 |
type = declArgs[0]; |
127 |
break; |
128 |
} |
129 |
} |
130 |
|
131 |
if (!hasTypeDefined) |
132 |
{ |
133 |
type = "real"; //$NON-NLS-1$ |
134 |
if (declArgs.length == 1) |
135 |
declaration = "real, " + declaration; //$NON-NLS-1$ |
136 |
else |
137 |
declaration = "real" + " :: " + declaration; //$NON-NLS-1$ //$NON-NLS-2$ |
138 |
} |
139 |
|
140 |
this.declaration = declaration; |
141 |
} |
142 |
|
143 |
public void setDefaultValue(String defValue) |
144 |
{ |
145 |
defaultValue = defValue; |
146 |
} |
147 |
|
148 |
/* |
149 |
* (non-Javadoc) |
150 |
* |
151 |
* @see |
152 |
* org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring#doCheckInitialConditions( |
153 |
* org.eclipse.ltk.core.refactoring.RefactoringStatus, |
154 |
* org.eclipse.core.runtime.IProgressMonitor) |
155 |
*/ |
156 |
@Override |
157 |
protected void doCheckInitialConditions(RefactoringStatus status, IProgressMonitor pm) |
158 |
throws org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring.PreconditionFailure |
159 |
{ |
160 |
ensureProjectHasRefactoringEnabled(status); |
161 |
|
162 |
ensureSubroutineIsSelected(); |
163 |
|
164 |
if (!matchingDeclarationsInInterfacesUniquelyBind()) |
165 |
status |
166 |
.addWarning(Messages.AddSubroutineParameterRefactoring_matchingDeclarationsDoNotUniquelyBind); |
167 |
|
168 |
oldParameterList = getSubroutineParameters(); |
169 |
} |
170 |
|
171 |
/* |
172 |
* By looking at the AST tree, starting at the node supplied to the refactoring as the selected |
173 |
* node, this method determines if a subroutine node has been selected or not. |
174 |
*/ |
175 |
private void ensureSubroutineIsSelected() |
176 |
throws org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring.PreconditionFailure |
177 |
{ |
178 |
IASTNode temporaryNode = findEnclosingNode(astOfFileInEditor, selectedRegionInEditor); |
179 |
|
180 |
if (temporaryNode == null) |
181 |
fail(Messages.AddSubroutineParameterRefactoring_selectSubroutineError); |
182 |
|
183 |
if (temporaryNode instanceof ASTSubroutineSubprogramNode) |
184 |
selectedSubroutine = ((ASTSubroutineSubprogramNode)temporaryNode).getSubroutineStmt(); |
185 |
else if (temporaryNode instanceof ASTSubroutineStmtNode) |
186 |
{ |
187 |
if (temporaryNode.findNearestAncestor(ASTSubroutineSubprogramNode.class) == null) |
188 |
fail(Messages.AddSubroutineParameterRefactoring_selectSubroutineError); |
189 |
selectedSubroutine = (ASTSubroutineStmtNode)temporaryNode; |
190 |
} |
191 |
else |
192 |
fail(Messages.AddSubroutineParameterRefactoring_selectSubroutineError); |
193 |
} |
194 |
|
195 |
/* |
196 |
* This method determines if a matching declaration already exists in scope, and if so, will |
197 |
* fail the refactoring. |
198 |
*/ |
199 |
private boolean matchingDeclarationsInInterfacesUniquelyBind() |
200 |
{ |
201 |
for (Definition declaration : getInterfaceDeclarations()) |
202 |
if (declaration.resolveInterfaceBinding().size() != 1) return false; |
203 |
|
204 |
return true; |
205 |
} |
206 |
|
207 |
/* |
208 |
* (non-Javadoc) |
209 |
* |
210 |
* @see |
211 |
* org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring#doCheckFinalConditions(org |
212 |
* .eclipse.ltk.core.refactoring.RefactoringStatus, org.eclipse.core.runtime.IProgressMonitor) |
213 |
*/ |
214 |
@Override |
215 |
protected void doCheckFinalConditions(RefactoringStatus status, IProgressMonitor pm) |
216 |
throws org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring.PreconditionFailure |
217 |
{ |
218 |
ensureDeclarationIsValid(); |
219 |
|
220 |
parameterName = declStmt.getEntityDeclList().get(0).getObjectName().getObjectName() |
221 |
.getText(); |
222 |
|
223 |
ensurePositionIsValid(); |
224 |
|
225 |
ensureDefaultValueIsValid(); |
226 |
|
227 |
checkForConflictingBindings(pm, status); |
228 |
} |
229 |
|
230 |
/* |
231 |
* This method ensures that the default value supplied is valid by applying logic that tests |
232 |
* whether or not the default type supplied matches the type supplied in the declaration line. |
233 |
* For example, .true. and .false. are reserved for logical types. This method also ensures that |
234 |
* the default value is not a variable name beginning with a number. |
235 |
*/ |
236 |
private void ensureDefaultValueIsValid() |
237 |
throws org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring.PreconditionFailure |
238 |
{ |
239 |
if (defaultValue == null || defaultValue.equals("") || //$NON-NLS-1$ |
240 |
isWhiteSpace(defaultValue) || isVariableNameBeginningWithNumber(defaultValue) |
241 |
|| (isTrueOrFalse(defaultValue) && !type.equals("logical")) || //$NON-NLS-1$ |
242 |
(isANumber(defaultValue) && (!type.equals("integer") && !type.equals("real"))) || //$NON-NLS-1$//$NON-NLS-2$ |
243 |
(isRealAndNotInteger(defaultValue) && type.equals("integer")) || //$NON-NLS-1$ |
244 |
(defaultValue.equals("null") && !declaration.contains("pointer"))) //$NON-NLS-1$ //$NON-NLS-2$ |
245 |
fail(Messages.AddSubroutineParameterRefactoring_InvalidDefaultValue); |
246 |
} |
247 |
|
248 |
/* |
249 |
* @param str A string to be tested |
250 |
* |
251 |
* @return <code> true </code> if the selected string is a real number, and <code> false </code> |
252 |
* if the selected string is ann integer or not a number. |
253 |
*/ |
254 |
private boolean isRealAndNotInteger(String str) |
255 |
{ |
256 |
if (isANumber(str)) |
257 |
{ |
258 |
try |
259 |
{ |
260 |
Integer.parseInt(str); |
261 |
} |
262 |
catch (NumberFormatException e) |
263 |
{ |
264 |
return true; |
265 |
} |
266 |
} |
267 |
return false; |
268 |
} |
269 |
|
270 |
/* |
271 |
* @param str A string to be tested |
272 |
* |
273 |
* @return <code> true </code> if the selected string is ".true." or ".false.", and <code> false |
274 |
* </code> if the selected string is anything else. |
275 |
*/ |
276 |
private boolean isTrueOrFalse(String str) |
277 |
{ |
278 |
if (str == null) return false; |
279 |
return str.equals(".true.") || str.equals(".false."); //$NON-NLS-1$ //$NON-NLS-2$ |
280 |
} |
281 |
|
282 |
/* |
283 |
* @param str A string to be tested |
284 |
* |
285 |
* @return <code> true </code> if the string begins with a number but is not a number (hence, a |
286 |
* variable name beginning with a number) and <code> false </code> otherwise. |
287 |
*/ |
288 |
private boolean isVariableNameBeginningWithNumber(String str) |
289 |
{ |
290 |
if (str != null) |
291 |
{ |
292 |
if (str.length() != 0) |
293 |
{ |
294 |
if (isANumber(str.substring(0, 1))) |
295 |
{ |
296 |
if (!isANumber(str)) return true; |
297 |
} |
298 |
} |
299 |
} |
300 |
|
301 |
return false; |
302 |
} |
303 |
|
304 |
/* |
305 |
* @param str A string |
306 |
* |
307 |
* @return <code> true </code> if str is a number. |
308 |
*/ |
309 |
private boolean isANumber(String str) |
310 |
{ |
311 |
try |
312 |
{ |
313 |
Double.parseDouble(str); |
314 |
} |
315 |
catch (NumberFormatException e) |
316 |
{ |
317 |
return false; |
318 |
} |
319 |
return true; |
320 |
} |
321 |
|
322 |
/* |
323 |
* This function ensures that the position given to the refactoring is in bounds for the current |
324 |
* size of the list, and if not, fails the refactoring. |
325 |
*/ |
326 |
private void ensurePositionIsValid() |
327 |
throws org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring.PreconditionFailure |
328 |
{ |
329 |
if (position > oldParameterList.size() || position < 0) |
330 |
fail(Messages.AddSubroutineParameterRefactoring_InvalidParameterPosition); |
331 |
} |
332 |
|
333 |
/* |
334 |
* This function attempts to produce a declaration node by passing the declaration line on to a |
335 |
* parser. If this node is returned as an error node, the refactoring fails. |
336 |
*/ |
337 |
private void ensureDeclarationIsValid() |
338 |
throws org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring.PreconditionFailure |
339 |
{ |
340 |
IBodyConstruct decl = parseLiteralStatementNoFail(declaration); |
341 |
if (decl == null || !(decl instanceof ASTTypeDeclarationStmtNode)) |
342 |
fail(Messages.AddSubroutineParameterRefactoring_InvalidDeclaration); |
343 |
declStmt = (ASTTypeDeclarationStmtNode)decl; |
344 |
IASTListNode<ASTEntityDeclNode> entityDeclList = declStmt.getEntityDeclList(); |
345 |
if (entityDeclList == null) |
346 |
{ |
347 |
fail(Messages.AddSubroutineParameterRefactoring_InvalidDeclaration); |
348 |
} |
349 |
} |
350 |
|
351 |
/* |
352 |
* This function checks to see whether or not the variable name supplied to the refactoring is |
353 |
* already in scope in the subroutine. |
354 |
*/ |
355 |
private void checkForConflictingBindings(IProgressMonitor pm, RefactoringStatus status) |
356 |
{ |
357 |
Definition def = arbitraryDefinitionInScope(); |
358 |
if (def == null) return; // No declarations in scope, so the new one can't conflict |
359 |
|
360 |
checkForConflictingBindings(pm, new ConflictingBindingErrorHandler(status), def, |
361 |
Collections.<PhotranTokenRef> emptyList(), parameterName); |
362 |
} |
363 |
|
364 |
/* |
365 |
* This function returns an arbitrary definition line in scope of the current node. It is used |
366 |
* to iterate through the declarations to see if there are any conflicts. |
367 |
*/ |
368 |
private Definition arbitraryDefinitionInScope() |
369 |
{ |
370 |
ScopingNode enclosingScope = selectedSubroutine.findNearestAncestor(ScopingNode.class); |
371 |
List<Definition> allDefs = enclosingScope.getAllDefinitions(); |
372 |
if (allDefs.isEmpty()) |
373 |
return null; |
374 |
else |
375 |
return allDefs.get(0); |
376 |
} |
377 |
|
378 |
/* |
379 |
* @param str A string |
380 |
* |
381 |
* @return <code> true </code> if str is only white space. This is used to test if default |
382 |
* values are nothing but white space. |
383 |
*/ |
384 |
private boolean isWhiteSpace(String str) |
385 |
{ |
386 |
return str.replace(" ", "").replace("\t", "").equals(""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ |
387 |
} |
388 |
|
389 |
/* |
390 |
* (non-Javadoc) |
391 |
* |
392 |
* @see |
393 |
* org.eclipse.rephraserengine.core.vpg.refactoring.VPGRefactoring#doCreateChange(org.eclipse |
394 |
* .core.runtime.IProgressMonitor) |
395 |
*/ |
396 |
@Override |
397 |
protected void doCreateChange(IProgressMonitor pm) throws CoreException, |
398 |
OperationCanceledException |
399 |
{ |
400 |
|
401 |
buildNewParameterListWithNewParameter(); |
402 |
|
403 |
// Change the arguments list to the new list |
404 |
permuteArgumentList(selectedSubroutine); |
405 |
|
406 |
addArgumentDeclaration(selectedSubroutine); |
407 |
|
408 |
permuteCallSites(); |
409 |
|
410 |
addChangeFromModifiedAST(fileInEditor, pm); |
411 |
vpg.releaseAST(fileInEditor); |
412 |
|
413 |
} |
414 |
|
415 |
/* |
416 |
* This function adds the declaration line to the subroutine. |
417 |
*/ |
418 |
private void addArgumentDeclaration(ASTSubroutineStmtNode subroutineStatement) |
419 |
{ |
420 |
ASTSubroutineSubprogramNode subroutine = (ASTSubroutineSubprogramNode)subroutineStatement |
421 |
.getParent(); |
422 |
|
423 |
IASTListNode<IBodyConstruct> statements = subroutine.getBody(); |
424 |
if (statements == null) |
425 |
{ |
426 |
statements = new ASTListNode<IBodyConstruct>(); |
427 |
subroutine.setBody(statements); |
428 |
} |
429 |
|
430 |
statements.add(0, declStmt); |
431 |
Reindenter.reindent(declStmt, astOfFileInEditor); |
432 |
|
433 |
} |
434 |
|
435 |
/* |
436 |
* (non-Javadoc) |
437 |
* |
438 |
* @see org.eclipse.ltk.core.refactoring.Refactoring#getName() |
439 |
*/ |
440 |
@Override |
441 |
public String getName() |
442 |
{ |
443 |
return Messages.AddSubroutineParameterRefactoring_Name; |
444 |
} |
445 |
|
446 |
/* |
447 |
* This function returns the list of subroutine parameters from the selected subroutine node. |
448 |
*/ |
449 |
public List<ASTSubroutineParNode> getSubroutineParameters() |
450 |
{ |
451 |
if (selectedSubroutine.getSubroutinePars() != null) |
452 |
return selectedSubroutine.getSubroutinePars(); |
453 |
|
454 |
return new ArrayList<ASTSubroutineParNode>(); |
455 |
} |
456 |
|
457 |
/* |
458 |
* This function returns a collection of interface declarations. |
459 |
*/ |
460 |
private Collection<Definition> getInterfaceDeclarations() |
461 |
{ |
462 |
List<Definition> subroutineDefinitions = selectedSubroutine.getSubroutineName() |
463 |
.getSubroutineName().resolveBinding(); |
464 |
|
465 |
if (subroutineDefinitions.size() != 1) return new ArrayList<Definition>(); |
466 |
|
467 |
return subroutineDefinitions.get(0).findMatchingDeclarationsInInterfaces(); |
468 |
} |
469 |
|
470 |
/* |
471 |
* This function builds the new parameter list to be supplied to the subroutine node by adding |
472 |
* the new parameter to the list in the appropriate position. |
473 |
*/ |
474 |
public void buildNewParameterListWithNewParameter() |
475 |
{ |
476 |
// Create new variable |
477 |
ASTSubroutineParNode newParameter = new ASTSubroutineParNode(); |
478 |
Token variableName = generateVariableName(); |
479 |
newParameter.setVariableName(variableName); |
480 |
|
481 |
// Create new list |
482 |
newParameterList = new ArrayList<ASTSubroutineParNode>(oldParameterList); |
483 |
newParameterList.add(position, newParameter); |
484 |
|
485 |
} |
486 |
|
487 |
/* |
488 |
* This function returns a token for a variable with the name of the new parameter name. |
489 |
*/ |
490 |
private Token generateVariableName() |
491 |
{ |
492 |
Token variableName = new Token(Terminal.T_IDENT, parameterName); |
493 |
return variableName; |
494 |
} |
495 |
|
496 |
/* |
497 |
* This function changes the argument list of the subroutine statement node to the new list |
498 |
* generated in buildNewParameterListWithNewParameter() |
499 |
*/ |
500 |
protected void permuteArgumentList(ASTSubroutineStmtNode node) |
501 |
{ |
502 |
ASTSeparatedListNode<ASTSubroutineParNode> newParameterList = new ASTSeparatedListNode<ASTSubroutineParNode>( |
503 |
new Token(Terminal.T_COMMA, ","), this.newParameterList); //$NON-NLS-1$ |
504 |
node.setSubroutinePars(newParameterList); |
505 |
} |
506 |
|
507 |
/* |
508 |
* This function changes all call sites to be updated to have the new argument in place, and |
509 |
* will match any calling pattern currently used. |
510 |
*/ |
511 |
private void permuteCallSites() |
512 |
{ |
513 |
for (ASTCallStmtNode callStmt : getCallSites()) |
514 |
{ |
515 |
int previousArgumentListSize = 0; |
516 |
|
517 |
if (callStmt.getArgList() != null) |
518 |
{ |
519 |
previousArgumentListSize = callStmt.getArgList().size(); |
520 |
} |
521 |
|
522 |
// Generate new IExpression Node for the default value |
523 |
ASTIntConstNode expr = new ASTIntConstNode(); |
524 |
expr.setIntConst(new Token(Terminal.T_ICON, defaultValue)); |
525 |
ASTSubroutineArgNode addedParArg = new ASTSubroutineArgNode(); |
526 |
addedParArg.setExpr(expr); |
527 |
|
528 |
// Test to see if the call site is using the |
529 |
// "(variableName = value, variablename = value)" pattern, or simply the |
530 |
// "(value, value)" pattern |
531 |
// The new parameter should follow this pattern at the call site, and should assume the |
532 |
// (value) pattern if the list was previously empty. |
533 |
if (previousArgumentListSize > 0) |
534 |
{ |
535 |
int positionToCompareTo = Math.min(position, previousArgumentListSize - 1); |
536 |
ASTSubroutineParNode firstParameter = oldParameterList.get(positionToCompareTo); |
537 |
ASTSubroutineArgNode firstParameterArgument = getActualArgFromCallStmt(callStmt, |
538 |
firstParameter.getVariableName(), positionToCompareTo); |
539 |
if (firstParameterArgument.getName() != null) |
540 |
addedParArg.setName(new Token(Terminal.T_IDENT, parameterName)); |
541 |
} |
542 |
|
543 |
ArrayList<ASTSubroutineArgNode> newParameterListForCallSite = new ArrayList<ASTSubroutineArgNode>(); |
544 |
|
545 |
for (int i = 0; i < previousArgumentListSize; i++) |
546 |
{ |
547 |
ASTSubroutineParNode desiredPar = oldParameterList.get(i); |
548 |
ASTSubroutineArgNode desiredParArgument = getActualArgFromCallStmt(callStmt, |
549 |
desiredPar.getVariableName(), i); |
550 |
newParameterListForCallSite.add(desiredParArgument); |
551 |
} |
552 |
|
553 |
newParameterListForCallSite.add(position, addedParArg); |
554 |
|
555 |
ASTSeparatedListNode<ASTSubroutineArgNode> newArgList = new ASTSeparatedListNode<ASTSubroutineArgNode>( |
556 |
new Token(Terminal.T_COMMA, ","), newParameterListForCallSite); //$NON-NLS-1$ |
557 |
callStmt.setArgList(newArgList); |
558 |
} |
559 |
} |
560 |
|
561 |
/* |
562 |
* This function returns the set of call sites for the subroutine that was selected. |
563 |
*/ |
564 |
private Set<ASTCallStmtNode> getCallSites() |
565 |
{ |
566 |
List<Definition> subroutineDefinitions = selectedSubroutine.getSubroutineName() |
567 |
.getSubroutineName().resolveBinding(); |
568 |
HashSet<ASTCallStmtNode> result = new HashSet<ASTCallStmtNode>(); |
569 |
|
570 |
if (subroutineDefinitions.size() != 1) return result; |
571 |
|
572 |
for (PhotranTokenRef tokenRef : subroutineDefinitions.get(0).findAllReferences(true)) |
573 |
{ |
574 |
Token token = tokenRef.findToken(); |
575 |
|
576 |
ASTCallStmtNode callStmtNode = token.findNearestAncestor(ASTCallStmtNode.class); |
577 |
|
578 |
if (callStmtNode != null) result.add(callStmtNode); |
579 |
} |
580 |
|
581 |
return result; |
582 |
} |
583 |
|
584 |
/* |
585 |
* This function gets an argument from a call statement, in order to check if it follows the |
586 |
* pattern of "Variablename = Value". |
587 |
*/ |
588 |
private ASTSubroutineArgNode getActualArgFromCallStmt(ASTCallStmtNode callStmt, |
589 |
Token desiredParName, int desiredParIndex) |
590 |
{ |
591 |
for (int i = 0; i < callStmt.getArgList().size(); i++) |
592 |
{ |
593 |
ASTSubroutineArgNode argument = callStmt.getArgList().get(i); |
594 |
if (argument.getName() == null || desiredParName == null) |
595 |
{ |
596 |
if (i == desiredParIndex) return argument; |
597 |
} |
598 |
else |
599 |
{ |
600 |
String argumentName = PhotranVPG.canonicalizeIdentifier(argument.getName() |
601 |
.getText()); |
602 |
String parameterName = PhotranVPG.canonicalizeIdentifier(desiredParName.getText()); |
603 |
if (argumentName.equals(parameterName)) return argument; |
604 |
} |
605 |
} |
606 |
return null; |
607 |
} |
608 |
|
609 |
/* |
610 |
* This class handles all error cases for conflicting variable names or bindings. |
611 |
*/ |
612 |
private final class ConflictingBindingErrorHandler implements IConflictingBindingCallback |
613 |
{ |
614 |
private final RefactoringStatus status; |
615 |
|
616 |
private ConflictingBindingErrorHandler(RefactoringStatus status) |
617 |
{ |
618 |
this.status = status; |
619 |
} |
620 |
|
621 |
public void addConflictError(List<Conflict> conflictingDef) |
622 |
{ |
623 |
Conflict conflict = conflictingDef.get(0); |
624 |
|
625 |
String msg = Messages.bind( |
626 |
Messages.AddSubroutineParameterRefactoring_NameConflictsWith, conflict.name, |
627 |
vpg.getDefinitionFor(conflict.tokenRef)); |
628 |
RefactoringStatusContext context = createContext(conflict.tokenRef); // Highlights |
629 |
// problematic |
630 |
// definition |
631 |
status.addError(msg, context); |
632 |
} |
633 |
|
634 |
public void addConflictWarning(List<Conflict> conflictingDef) |
635 |
{ |
636 |
Conflict conflict = conflictingDef.get(0); |
637 |
|
638 |
String msg = Messages.bind( |
639 |
Messages.AddSubroutineParameterRefactoring_NameMightConflictWithSubprogram, |
640 |
conflict.name); |
641 |
RefactoringStatusContext context = createContext(conflict.tokenRef); // Highlights |
642 |
// problematic |
643 |
// definition |
644 |
status.addWarning(msg, context); |
645 |
} |
646 |
|
647 |
public void addReferenceWillChangeError(String newName, Token reference) |
648 |
{ |
649 |
throw new IllegalStateException(); |
650 |
} |
651 |
} |
652 |
|
653 |
} |