001: package core;
002:
003: import java.util.Iterator;
004: import java.util.List;
005:
006: import org.eclipse.jdt.core.dom.AST;
007: import org.eclipse.jdt.core.dom.ASTVisitor;
008: import org.eclipse.jdt.core.dom.Assignment;
009: import org.eclipse.jdt.core.dom.CompilationUnit;
010: import org.eclipse.jdt.core.dom.Expression;
011: import org.eclipse.jdt.core.dom.ExpressionStatement;
012: import org.eclipse.jdt.core.dom.FieldAccess;
013: import org.eclipse.jdt.core.dom.FieldDeclaration;
014: import org.eclipse.jdt.core.dom.ITypeBinding;
015: import org.eclipse.jdt.core.dom.MethodDeclaration;
016: import org.eclipse.jdt.core.dom.PrimitiveType;
017: import org.eclipse.jdt.core.dom.ReturnStatement;
018: import org.eclipse.jdt.core.dom.SimpleName;
019: import org.eclipse.jdt.core.dom.SimpleType;
020: import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
021: import org.eclipse.jdt.core.dom.Statement;
022: import org.eclipse.jdt.core.dom.Type;
023: import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
024:
025: /**
026: * @author sh
027: *
028: */
029: public class FieldDeclarationVisitor extends ASTVisitor {
030: private String oldName = null;
031: private String newName = null;
032: private String oldVarName = null;
033: private String newVarName = null;
034:
035: /**
036: * Looks for field declarations.
037: * For every occurence matching the renamed field
038: * the field type would be updated
039: * e.g. private int a; --> private String a;
040: * If the use set a new variable name the new name
041: * will be set.
042: *
043: * @param node the node to visit
044: */
045: @Override
046: public boolean visit(FieldDeclaration node) {
047: // iterate over all variable declarations
048: List<VariableDeclarationFragment> fragments = node.fragments();
049: for (Iterator<VariableDeclarationFragment> iterator = fragments
050: .iterator(); iterator.hasNext();) {
051: VariableDeclarationFragment name = (VariableDeclarationFragment) iterator
052: .next();
053: String varName = name.getName().toString();
054:
055: // if the right variable type and name was found
056: // do the refactoring
057: Type type = node.getType();
058: if (varName.equals(this .oldVarName)
059: && type.toString().equals(this .oldName)) {
060:
061: // if the user set a primitive datatype
062: AST ast = type.getAST();
063: ITypeBinding binding = ast
064: .resolveWellKnownType(this .newName);
065: if (binding != null) {
066: // set the new type in the declaration
067: PrimitiveType.Code code = PrimitiveType
068: .toCode(binding.getName());
069: PrimitiveType primType = ast.newPrimitiveType(code);
070: node.setType(primType);
071: }
072: // if the user set a complex datatype
073: else {
074: SimpleType newType = ast.newSimpleType(ast
075: .newName(this .newName));
076: node.setType(newType);
077:
078: // resolve the package of the type for the import statement
079: }
080:
081: // set the new variable name
082: name.setName(ast.newSimpleName(this .newVarName));
083: }
084: }
085: return super .visit(node);
086: }
087:
088: /**
089: * Looks for method declarations.
090: * For each method declarations at first the return statement
091: * in the method body is searched. For an occurency it checks
092: * if the return value is the same type as the field declarations.
093: * If so, the methods return value, method name and method
094: * parameter types will be adapted.
095: *
096: * @param node the node to visit
097: */
098: @Override
099: public boolean visit(MethodDeclaration node) {
100: // iterate over the whole method body
101: List<Statement> statements = node.getBody().statements();
102: for (Iterator<Statement> iterator = statements.iterator(); iterator
103: .hasNext();) {
104: Statement statement = (Statement) iterator.next();
105:
106: // handle the return value type
107: // according to the variable declaration type,
108: // adapt the return value for a getter method.
109: handleReturnValue(node, statement);
110:
111: // handle the parameters
112: // according to the variable declaration type,
113: // adapt the variable type in the parameter list for
114: // a setter method. This algorithm investigates the method body
115: // and searches for the return statement. If the field to refactor
116: // has been found, we know that we have to adapt the return value
117: // and the variable declaration type in the method parameters.
118: handleParameters(node, statement);
119:
120: // handles the getter methods name
121: handleGetterMethodName(node, statement);
122:
123: // handles the setter methods name
124: handleSetterMethodName(node, statement);
125:
126: // handle return statement
127: // if the variable name should be renamed
128: // update the return statement in a getter method
129: handleReturnStatement(node, statement);
130: }
131: return super .visit(node);
132: }
133:
134: /**
135: * A helper method to adapt the getter method name.
136: *
137: * @param node the node to visit
138: * @param statement the current statement to investigate
139: */
140: private void handleGetterMethodName(MethodDeclaration node,
141: Statement statement) {
142: // handle the return value type
143: // according to the variable declaration type,
144: // adapt the method name for a getter method.
145: if (statement instanceof ReturnStatement == false)
146: return;
147:
148: Expression expr = ((ReturnStatement) statement).getExpression();
149: if (expr == null || expr instanceof FieldAccess == false)
150: return;
151: String var = ((FieldAccess) expr).getName().toString();
152:
153: if (var.equals(this .oldVarName)) {
154: // set the new name
155: AST ast = node.getAST();
156: StringBuilder sb = new StringBuilder(newVarName.substring(
157: 0, 1).toUpperCase());
158: sb.append(newVarName.substring(1, newVarName.length())
159: .toLowerCase());
160: String newMethodName = "get" + sb.toString();
161: node.setName(ast.newSimpleName(newMethodName));
162: }
163: }
164:
165: /**
166: * A helper method to adapt the setter method name
167: *
168: * @param node the node to visit
169: * @param statement the current statement to investigate
170: */
171: private void handleSetterMethodName(MethodDeclaration node,
172: Statement statement) {
173: // handle the method name of the setter.
174: // According to the variable declaration type,
175: // adapt the method name a setter method. This algorithm investigates
176: // the method body and searches for the return statement. If the field
177: //to refactor has been found, we know that we have to adapt the method name
178:
179: if (statement instanceof ExpressionStatement == false)
180: return;
181:
182: Expression expr = ((ExpressionStatement) statement)
183: .getExpression();
184:
185: // step over if it is not an assignment
186: if (expr instanceof Assignment == false)
187: return;
188: Assignment assign = (Assignment) expr;
189:
190: // investigate the assignment
191: Expression lhs = assign.getLeftHandSide();
192:
193: // step over if the left hand site of the expression is not a field access
194: if (lhs instanceof FieldAccess == false)
195: return;
196:
197: // investigate the field access, continue if
198: // the field to refactor isn't used
199: FieldAccess lhsf = (FieldAccess) lhs;
200: if (!lhsf.getName().toString().equals(this .oldVarName))
201: return;
202:
203: // get the the right hand site of the expression to know which
204: // variable declaration we have to adapt in a setter method
205: Expression rhs = assign.getRightHandSide();
206: String bodyVarName = ((SimpleName) rhs).getFullyQualifiedName()
207: .toString();
208:
209: // iterate over all method variable declarations
210: // to find the correct parameter
211: List<SingleVariableDeclaration> parameters = node.parameters();
212: for (Iterator<SingleVariableDeclaration> varIter = parameters
213: .iterator(); varIter.hasNext();) {
214: SingleVariableDeclaration varDecl = (SingleVariableDeclaration) varIter
215: .next();
216: String varDeclName = varDecl.getName()
217: .getFullyQualifiedName().toString();
218:
219: // check if it is the correct parameter
220: if (!varDeclName.equals(bodyVarName))
221: continue;
222:
223: // adapt the parameter name
224: AST ast = node.getAST();
225: varDecl.setName(ast.newSimpleName(newVarName));
226:
227: // adapt the method name
228: StringBuilder sb = new StringBuilder(newVarName.substring(
229: 0, 1).toUpperCase());
230: sb.append(newVarName.substring(1, newVarName.length())
231: .toLowerCase());
232: String newMethodName = "set" + sb.toString();
233: node.setName(ast.newSimpleName(newMethodName));
234:
235: // adapt the body variable name
236: assign.setRightHandSide(ast.newSimpleName(newVarName));
237: }
238: }
239:
240: /**
241: * A helper method to adapt the methods return type.
242: *
243: * @param node the node to visit
244: * @param statement the current statement to investigate
245: */
246: private void handleReturnValue(MethodDeclaration node,
247: Statement statement) {
248: // handle the return value type
249: // according to the variable declaration type,
250: // adapt the return value for a getter method.
251: if (statement instanceof ReturnStatement == false)
252: return;
253:
254: Expression expr = ((ReturnStatement) statement).getExpression();
255: if (expr == null || expr instanceof FieldAccess == false)
256: return;
257: String var = ((FieldAccess) expr).getName().toString();
258:
259: if (var.equals(this .oldVarName)) {
260: // resolve the type stored in the info object
261: AST ast = node.getAST();
262: ITypeBinding binding = ast
263: .resolveWellKnownType(this .newName);
264: // if the user set a primitive datatype
265: if (binding != null) {
266: PrimitiveType.Code code = PrimitiveType.toCode(binding
267: .getName());
268: PrimitiveType primType = ast.newPrimitiveType(code);
269: node.setReturnType2(primType);
270: }
271: // if the user set a complex datatype
272: else {
273: SimpleType newType = ast.newSimpleType(ast
274: .newName(this .newName));
275: node.setReturnType2(newType);
276: }
277: }
278: }
279:
280: /**
281: * A helper method to adapt the methods parameter type.
282: *
283: * @param node the node to visit
284: * @param statement the current statement to investigate
285: */
286: private void handleParameters(MethodDeclaration node,
287: Statement statement) {
288: // handle the parameters
289: // according to the variable declaration type,
290: // adapt the variable type in the parameter list for
291: // a setter method. This algorithm investigates the method body
292: // and searches for the return statement. If the field to refactor
293: // has been found, we know that we have to adapt the return value
294: // and the variable declaration type in the method parameters.
295: if (statement instanceof ExpressionStatement == false)
296: return;
297:
298: Expression expr = ((ExpressionStatement) statement)
299: .getExpression();
300:
301: // step over if it is not an assignment
302: if (expr instanceof Assignment == false)
303: return;
304: Assignment assign = (Assignment) expr;
305:
306: // investigate the assignment
307: Expression lhs = assign.getLeftHandSide();
308:
309: // step over if the left hand site of the expression is not a field access
310: if (lhs instanceof FieldAccess == false)
311: return;
312:
313: // investigate the field access, continue if
314: // the field to refactor isn't used
315: FieldAccess lhsf = (FieldAccess) lhs;
316: if (!lhsf.getName().toString().equals(this .oldVarName))
317: return;
318:
319: // get the the right hand site of the expression to know which
320: // variable declaration we have to adapt in a setter method
321: Expression rhs = assign.getRightHandSide();
322: String bodyVarName = ((SimpleName) rhs).getFullyQualifiedName()
323: .toString();
324:
325: // iterate over all method variable declarations
326: // to find the correct parameter
327: List<SingleVariableDeclaration> parameters = node.parameters();
328: for (Iterator<SingleVariableDeclaration> varIter = parameters
329: .iterator(); varIter.hasNext();) {
330: SingleVariableDeclaration varDecl = (SingleVariableDeclaration) varIter
331: .next();
332: // get the variable type
333: Type type = varDecl.getType();
334: String varDeclName = varDecl.getName()
335: .getFullyQualifiedName().toString();
336:
337: // check if it is the correct parameter
338: if (!varDeclName.equals(bodyVarName))
339: continue;
340: ITypeBinding binding = type.getAST().resolveWellKnownType(
341: this .newName);
342: // if the user set a primitive datatype
343: if (binding != null) {
344: PrimitiveType.Code code = PrimitiveType.toCode(binding
345: .getName());
346: PrimitiveType primType = type.getAST()
347: .newPrimitiveType(code);
348: varDecl.setType(primType);
349: }
350: // if the user set a complex datatype
351: else {
352: SimpleType newType = type.getAST().newSimpleType(
353: type.getAST().newName(this .newName));
354: varDecl.setType(newType);
355: }
356: }
357: }
358:
359: /**
360: * A helper method to adapt the return statement
361: * in a getter method body, if the variable name
362: * should be renamed.
363: *
364: * @param node
365: * @param statement
366: */
367: private void handleReturnStatement(MethodDeclaration node,
368: Statement statement) {
369: if (statement instanceof ReturnStatement == false)
370: return;
371:
372: ReturnStatement retStatement = (ReturnStatement) statement;
373: Expression expr = retStatement.getExpression();
374: if (expr == null)
375: return;
376: if (expr.toString().equals(this .oldVarName)) {
377: AST ast = retStatement.getAST();
378: retStatement.setExpression(ast
379: .newSimpleName(this .newVarName));
380: }
381: }
382:
383: /**
384: * Looks for field acess.
385: * For every occurence matching the renamed field
386: * the field statement would be updated.
387: *
388: * @param node the node to visit
389: */
390: @Override
391: public boolean visit(FieldAccess node) {
392: String fqn = node.getName().getFullyQualifiedName();
393: if (fqn.contains(this .oldVarName)) {
394: AST ast = node.getAST();
395: SimpleName name = ast.newSimpleName(fqn.replace(
396: this .oldVarName, this .newVarName));
397: node.setName(name);
398: }
399: return super .visit(node);
400: }
401:
402: /**
403: * Starts the process.
404: *
405: * @param unit the AST root node. Bindings have to been resolved.
406: */
407: public void process(CompilationUnit unit, RefactorInfo info) {
408: this.oldName = info.getOldName();
409: this.newName = info.getNewName();
410: this.oldVarName = info.getOldVarName();
411: this.newVarName = info.getNewVarName();
412: unit.accept(this);
413: }
414: }
|