001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.codemanipulation;
011:
012: import org.eclipse.core.runtime.CoreException;
013:
014: import org.eclipse.jdt.core.Flags;
015: import org.eclipse.jdt.core.IField;
016: import org.eclipse.jdt.core.IJavaProject;
017: import org.eclipse.jdt.core.IMethod;
018: import org.eclipse.jdt.core.IType;
019: import org.eclipse.jdt.core.JavaModelException;
020: import org.eclipse.jdt.core.NamingConventions;
021: import org.eclipse.jdt.core.Signature;
022: import org.eclipse.jdt.core.dom.AST;
023: import org.eclipse.jdt.core.dom.ASTNode;
024: import org.eclipse.jdt.core.dom.Assignment;
025: import org.eclipse.jdt.core.dom.CastExpression;
026: import org.eclipse.jdt.core.dom.Expression;
027: import org.eclipse.jdt.core.dom.ITypeBinding;
028: import org.eclipse.jdt.core.dom.IVariableBinding;
029: import org.eclipse.jdt.core.dom.InfixExpression;
030: import org.eclipse.jdt.core.dom.NumberLiteral;
031: import org.eclipse.jdt.core.dom.ParenthesizedExpression;
032: import org.eclipse.jdt.core.dom.PostfixExpression;
033: import org.eclipse.jdt.core.dom.PrefixExpression;
034: import org.eclipse.jdt.core.dom.PrimitiveType;
035: import org.eclipse.jdt.core.dom.Assignment.Operator;
036: import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
037:
038: import org.eclipse.jdt.internal.corext.dom.ASTNodes;
039: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
040: import org.eclipse.jdt.internal.corext.util.JdtFlags;
041:
042: import org.eclipse.jdt.ui.CodeGeneration;
043:
044: public class GetterSetterUtil {
045:
046: private static final String[] EMPTY = new String[0];
047:
048: //no instances
049: private GetterSetterUtil() {
050: }
051:
052: public static String getGetterName(IField field,
053: String[] excludedNames) throws JavaModelException {
054: boolean useIs = StubUtility.useIsForBooleanGetters(field
055: .getJavaProject());
056: return getGetterName(field, excludedNames, useIs);
057: }
058:
059: private static String getGetterName(IField field,
060: String[] excludedNames, boolean useIsForBoolGetters)
061: throws JavaModelException {
062: if (excludedNames == null) {
063: excludedNames = EMPTY;
064: }
065: return getGetterName(field.getJavaProject(), field
066: .getElementName(), field.getFlags(),
067: useIsForBoolGetters && JavaModelUtil.isBoolean(field),
068: excludedNames);
069: }
070:
071: public static String getGetterName(IVariableBinding variableType,
072: IJavaProject project, String[] excludedNames,
073: boolean isBoolean) {
074: boolean useIs = StubUtility.useIsForBooleanGetters(project)
075: && isBoolean;
076: return getGetterName(project, variableType.getName(),
077: variableType.getModifiers(), useIs, excludedNames);
078: }
079:
080: public static String getGetterName(IJavaProject project,
081: String fieldName, int flags, boolean isBoolean,
082: String[] excludedNames) {
083: return NamingConventions.suggestGetterName(project, fieldName,
084: flags, isBoolean, excludedNames);
085: }
086:
087: public static String getSetterName(IVariableBinding variableType,
088: IJavaProject project, String[] excludedNames,
089: boolean isBoolean) {
090: return getSetterName(project, variableType.getName(),
091: variableType.getModifiers(), isBoolean, excludedNames);
092: }
093:
094: public static String getSetterName(IJavaProject project,
095: String fieldName, int flags, boolean isBoolean,
096: String[] excludedNames) {
097: return NamingConventions.suggestSetterName(project, fieldName,
098: flags, isBoolean, excludedNames);
099: }
100:
101: public static String getSetterName(IField field,
102: String[] excludedNames) throws JavaModelException {
103: if (excludedNames == null) {
104: excludedNames = EMPTY;
105: }
106: return NamingConventions.suggestSetterName(field
107: .getJavaProject(), field.getElementName(), field
108: .getFlags(), JavaModelUtil.isBoolean(field),
109: excludedNames);
110: }
111:
112: public static IMethod getGetter(IField field)
113: throws JavaModelException {
114: String getterName = getGetterName(field, EMPTY, true);
115: IMethod primaryCandidate = JavaModelUtil.findMethod(getterName,
116: new String[0], false, field.getDeclaringType());
117: if (!JavaModelUtil.isBoolean(field)
118: || (primaryCandidate != null && primaryCandidate
119: .exists()))
120: return primaryCandidate;
121: //bug 30906 describes why we need to look for other alternatives here (try with get... for booleans)
122: String secondCandidateName = getGetterName(field, EMPTY, false);
123: return JavaModelUtil.findMethod(secondCandidateName,
124: new String[0], false, field.getDeclaringType());
125: }
126:
127: public static IMethod getSetter(IField field)
128: throws JavaModelException {
129: String[] args = new String[] { field.getTypeSignature() };
130: return JavaModelUtil.findMethod(getSetterName(field, EMPTY),
131: args, false, field.getDeclaringType());
132: }
133:
134: /**
135: * Create a stub for a getter of the given field using getter/setter templates. The resulting code
136: * has to be formatted and indented.
137: * @param field The field to create a getter for
138: * @param setterName The chosen name for the setter
139: * @param addComments If <code>true</code>, comments will be added.
140: * @param flags The flags signaling visibility, if static, synchronized or final
141: * @return Returns the generated stub.
142: * @throws CoreException
143: */
144: public static String getSetterStub(IField field, String setterName,
145: boolean addComments, int flags) throws CoreException {
146:
147: String fieldName = field.getElementName();
148: IType parentType = field.getDeclaringType();
149:
150: String returnSig = field.getTypeSignature();
151: String typeName = Signature.toString(returnSig);
152:
153: IJavaProject project = field.getJavaProject();
154:
155: String accessorName = NamingConventions
156: .removePrefixAndSuffixForFieldName(project, fieldName,
157: field.getFlags());
158: String argname = StubUtility.suggestArgumentName(project,
159: accessorName, EMPTY);
160:
161: boolean isStatic = Flags.isStatic(flags);
162: boolean isSync = Flags.isSynchronized(flags);
163: boolean isFinal = Flags.isFinal(flags);
164:
165: String lineDelim = "\n"; // Use default line delimiter, as generated stub has to be formatted anyway //$NON-NLS-1$
166: StringBuffer buf = new StringBuffer();
167: if (addComments) {
168: String comment = CodeGeneration.getSetterComment(field
169: .getCompilationUnit(), parentType
170: .getTypeQualifiedName('.'), setterName, field
171: .getElementName(), typeName, argname, accessorName,
172: lineDelim);
173: if (comment != null) {
174: buf.append(comment);
175: buf.append(lineDelim);
176: }
177: }
178: buf.append(JdtFlags.getVisibilityString(flags));
179: buf.append(' ');
180: if (isStatic)
181: buf.append("static "); //$NON-NLS-1$
182: if (isSync)
183: buf.append("synchronized "); //$NON-NLS-1$
184: if (isFinal)
185: buf.append("final "); //$NON-NLS-1$
186:
187: buf.append("void "); //$NON-NLS-1$
188: buf.append(setterName);
189: buf.append('(');
190: buf.append(typeName);
191: buf.append(' ');
192: buf.append(argname);
193: buf.append(") {"); //$NON-NLS-1$
194: buf.append(lineDelim);
195:
196: boolean useThis = StubUtility.useThisForFieldAccess(project);
197: if (argname.equals(fieldName) || (useThis && !isStatic)) {
198: if (isStatic)
199: fieldName = parentType.getElementName() + '.'
200: + fieldName;
201: else
202: fieldName = "this." + fieldName; //$NON-NLS-1$
203: }
204: String body = CodeGeneration.getSetterMethodBodyContent(field
205: .getCompilationUnit(), parentType
206: .getTypeQualifiedName('.'), setterName, fieldName,
207: argname, lineDelim);
208: if (body != null) {
209: buf.append(body);
210: }
211: buf.append("}"); //$NON-NLS-1$
212: buf.append(lineDelim);
213: return buf.toString();
214: }
215:
216: /**
217: * Create a stub for a getter of the given field using getter/setter templates. The resulting code
218: * has to be formatted and indented.
219: * @param field The field to create a getter for
220: * @param getterName The chosen name for the getter
221: * @param addComments If <code>true</code>, comments will be added.
222: * @param flags The flags signaling visibility, if static, synchronized or final
223: * @return Returns the generated stub.
224: * @throws CoreException
225: */
226: public static String getGetterStub(IField field, String getterName,
227: boolean addComments, int flags) throws CoreException {
228: String fieldName = field.getElementName();
229: IType parentType = field.getDeclaringType();
230:
231: boolean isStatic = Flags.isStatic(flags);
232: boolean isSync = Flags.isSynchronized(flags);
233: boolean isFinal = Flags.isFinal(flags);
234:
235: String typeName = Signature.toString(field.getTypeSignature());
236: String accessorName = NamingConventions
237: .removePrefixAndSuffixForFieldName(field
238: .getJavaProject(), fieldName, field.getFlags());
239:
240: String lineDelim = "\n"; // Use default line delimiter, as generated stub has to be formatted anyway //$NON-NLS-1$
241: StringBuffer buf = new StringBuffer();
242: if (addComments) {
243: String comment = CodeGeneration.getGetterComment(field
244: .getCompilationUnit(), parentType
245: .getTypeQualifiedName('.'), getterName, field
246: .getElementName(), typeName, accessorName,
247: lineDelim);
248: if (comment != null) {
249: buf.append(comment);
250: buf.append(lineDelim);
251: }
252: }
253:
254: buf.append(JdtFlags.getVisibilityString(flags));
255: buf.append(' ');
256: if (isStatic)
257: buf.append("static "); //$NON-NLS-1$
258: if (isSync)
259: buf.append("synchronized "); //$NON-NLS-1$
260: if (isFinal)
261: buf.append("final "); //$NON-NLS-1$
262:
263: buf.append(typeName);
264: buf.append(' ');
265: buf.append(getterName);
266: buf.append("() {"); //$NON-NLS-1$
267: buf.append(lineDelim);
268:
269: boolean useThis = StubUtility.useThisForFieldAccess(field
270: .getJavaProject());
271: if (useThis && !isStatic) {
272: fieldName = "this." + fieldName; //$NON-NLS-1$
273: }
274:
275: String body = CodeGeneration.getGetterMethodBodyContent(field
276: .getCompilationUnit(), parentType
277: .getTypeQualifiedName('.'), getterName, fieldName,
278: lineDelim);
279: if (body != null) {
280: buf.append(body);
281: }
282: buf.append("}"); //$NON-NLS-1$
283: buf.append(lineDelim);
284: return buf.toString();
285: }
286:
287: /**
288: * Converts an assignment, postfix expression or prefix expression into an assignable equivalent expression using the getter.
289: *
290: * @param node the assignment/prefix/postfix node
291: * @param astRewrite the astRewrite to use
292: * @param getterExpression the expression to insert for read accesses or <code>null</code> if such an expression does not exist
293: * @param variableType the type of the variable that the result will be assigned to
294: * @param is50OrHigher <code>true</code> if a 5.0 or higher environment can be used
295: * @return an expression that can be assigned to the type variableType with node being replaced by a equivalent expression using the getter
296: */
297: public static Expression getAssignedValue(ASTNode node,
298: ASTRewrite astRewrite, Expression getterExpression,
299: ITypeBinding variableType, boolean is50OrHigher) {
300: InfixExpression.Operator op = null;
301: AST ast = astRewrite.getAST();
302: if (isNotInBlock(node))
303: return null;
304: if (node.getNodeType() == ASTNode.ASSIGNMENT) {
305: Assignment assignment = ((Assignment) node);
306: Expression rightHandSide = assignment.getRightHandSide();
307: Expression copiedRightOp = (Expression) astRewrite
308: .createCopyTarget(rightHandSide);
309: if (assignment.getOperator() == Operator.ASSIGN) {
310: ITypeBinding rightHandSideType = rightHandSide
311: .resolveTypeBinding();
312: copiedRightOp = createNarrowCastIfNessecary(
313: copiedRightOp, rightHandSideType, ast,
314: variableType, is50OrHigher);
315: return copiedRightOp;
316: }
317: if (getterExpression != null) {
318: InfixExpression infix = ast.newInfixExpression();
319: infix.setLeftOperand(getterExpression);
320: infix.setOperator(ASTNodes
321: .convertToInfixOperator(assignment
322: .getOperator()));
323: infix.setRightOperand(copiedRightOp);
324: ITypeBinding infixType = infix.resolveTypeBinding();
325: return createNarrowCastIfNessecary(infix, infixType,
326: ast, variableType, is50OrHigher);
327: }
328: } else if (node.getNodeType() == ASTNode.POSTFIX_EXPRESSION) {
329: PostfixExpression po = (PostfixExpression) node;
330: if (po.getOperator() == PostfixExpression.Operator.INCREMENT)
331: op = InfixExpression.Operator.PLUS;
332: if (po.getOperator() == PostfixExpression.Operator.DECREMENT)
333: op = InfixExpression.Operator.MINUS;
334: } else if (node.getNodeType() == ASTNode.PREFIX_EXPRESSION) {
335: PrefixExpression pe = (PrefixExpression) node;
336: if (pe.getOperator() == PrefixExpression.Operator.INCREMENT)
337: op = InfixExpression.Operator.PLUS;
338: if (pe.getOperator() == PrefixExpression.Operator.DECREMENT)
339: op = InfixExpression.Operator.MINUS;
340: }
341: if (op != null && getterExpression != null) {
342: return createInfixInvocationFromPostPrefixExpression(op,
343: getterExpression, ast, variableType, is50OrHigher);
344: }
345: return null;
346: }
347:
348: /*
349: * Check if the node is in a block. We don't want to update declarations
350: */
351: private static boolean isNotInBlock(ASTNode parent) {
352: ASTNode statement = parent.getParent();
353: boolean isStatement = statement.getNodeType() != ASTNode.EXPRESSION_STATEMENT;
354: ASTNode block = statement.getParent();
355: boolean isBlock = block.getNodeType() == ASTNode.BLOCK
356: || block.getNodeType() == ASTNode.SWITCH_STATEMENT;
357: boolean isControlStatemenBody = ASTNodes
358: .isControlStatementBody(statement.getLocationInParent());
359: return isStatement || !(isBlock || isControlStatemenBody);
360: }
361:
362: private static Expression createInfixInvocationFromPostPrefixExpression(
363: InfixExpression.Operator operator,
364: Expression getterExpression, AST ast,
365: ITypeBinding variableType, boolean is50OrHigher) {
366: InfixExpression infix = ast.newInfixExpression();
367: infix.setLeftOperand(getterExpression);
368: infix.setOperator(operator);
369: NumberLiteral number = ast.newNumberLiteral();
370: number.setToken("1"); //$NON-NLS-1$
371: infix.setRightOperand(number);
372: ITypeBinding infixType = infix.resolveTypeBinding();
373: return createNarrowCastIfNessecary(infix, infixType, ast,
374: variableType, is50OrHigher);
375: }
376:
377: /**
378: * Checks if the assignment needs a downcast and inserts it if necessary
379: *
380: * @param expression the right hand-side
381: * @param expressionType the type of the right hand-side. Can be null
382: * @param ast the AST
383: * @param variableType the Type of the variable the expression will be assigned to
384: * @param is50OrHigher if <code>true</code> java 5.0 code will be assumed
385: * @return the casted expression if necessary
386: */
387: private static Expression createNarrowCastIfNessecary(
388: Expression expression, ITypeBinding expressionType,
389: AST ast, ITypeBinding variableType, boolean is50OrHigher) {
390: PrimitiveType castTo = null;
391: if (variableType.isEqualTo(expressionType))
392: return expression; //no cast for same type
393: if (is50OrHigher) {
394: if (ast
395: .resolveWellKnownType("java.lang.Character").isEqualTo(variableType)) //$NON-NLS-1$
396: castTo = ast.newPrimitiveType(PrimitiveType.CHAR);
397: if (ast
398: .resolveWellKnownType("java.lang.Byte").isEqualTo(variableType)) //$NON-NLS-1$
399: castTo = ast.newPrimitiveType(PrimitiveType.BYTE);
400: if (ast
401: .resolveWellKnownType("java.lang.Short").isEqualTo(variableType)) //$NON-NLS-1$
402: castTo = ast.newPrimitiveType(PrimitiveType.SHORT);
403: }
404: if (ast.resolveWellKnownType("char").isEqualTo(variableType)) //$NON-NLS-1$
405: castTo = ast.newPrimitiveType(PrimitiveType.CHAR);
406: if (ast.resolveWellKnownType("byte").isEqualTo(variableType)) //$NON-NLS-1$
407: castTo = ast.newPrimitiveType(PrimitiveType.BYTE);
408: if (ast.resolveWellKnownType("short").isEqualTo(variableType)) //$NON-NLS-1$
409: castTo = ast.newPrimitiveType(PrimitiveType.SHORT);
410: if (castTo != null) {
411: CastExpression cast = ast.newCastExpression();
412: if (ASTNodes.needsParentheses(expression)) {
413: ParenthesizedExpression parenthesized = ast
414: .newParenthesizedExpression();
415: parenthesized.setExpression(expression);
416: cast.setExpression(parenthesized);
417: } else
418: cast.setExpression(expression);
419: cast.setType(castTo);
420: return cast;
421: }
422: return expression;
423: }
424:
425: }
|