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