KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > correction > NewVariableCompletionProposal


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation 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  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.jdt.internal.ui.text.correction;
13
14 import java.util.Arrays JavaDoc;
15 import java.util.Comparator JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19
20 import org.eclipse.core.runtime.Assert;
21 import org.eclipse.core.runtime.CoreException;
22
23 import org.eclipse.swt.graphics.Image;
24
25 import org.eclipse.jdt.core.ICompilationUnit;
26 import org.eclipse.jdt.core.dom.AST;
27 import org.eclipse.jdt.core.dom.ASTNode;
28 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
29 import org.eclipse.jdt.core.dom.Assignment;
30 import org.eclipse.jdt.core.dom.Block;
31 import org.eclipse.jdt.core.dom.BodyDeclaration;
32 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
33 import org.eclipse.jdt.core.dom.CompilationUnit;
34 import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
35 import org.eclipse.jdt.core.dom.EnumDeclaration;
36 import org.eclipse.jdt.core.dom.Expression;
37 import org.eclipse.jdt.core.dom.ExpressionStatement;
38 import org.eclipse.jdt.core.dom.FieldDeclaration;
39 import org.eclipse.jdt.core.dom.ForStatement;
40 import org.eclipse.jdt.core.dom.IBinding;
41 import org.eclipse.jdt.core.dom.ITypeBinding;
42 import org.eclipse.jdt.core.dom.Initializer;
43 import org.eclipse.jdt.core.dom.Javadoc;
44 import org.eclipse.jdt.core.dom.MethodDeclaration;
45 import org.eclipse.jdt.core.dom.MethodInvocation;
46 import org.eclipse.jdt.core.dom.Modifier;
47 import org.eclipse.jdt.core.dom.QualifiedName;
48 import org.eclipse.jdt.core.dom.SimpleName;
49 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
50 import org.eclipse.jdt.core.dom.Statement;
51 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
52 import org.eclipse.jdt.core.dom.TagElement;
53 import org.eclipse.jdt.core.dom.TextElement;
54 import org.eclipse.jdt.core.dom.Type;
55 import org.eclipse.jdt.core.dom.TypeDeclaration;
56 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
57 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
58 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
59 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
60 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
61 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
62
63 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
64 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
65 import org.eclipse.jdt.internal.corext.dom.Bindings;
66 import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder;
67
68 public class NewVariableCompletionProposal extends LinkedCorrectionProposal {
69
70     public static final int LOCAL= 1;
71     public static final int FIELD= 2;
72     public static final int PARAM= 3;
73
74     public static final int CONST_FIELD= 4;
75     public static final int ENUM_CONST= 5;
76
77     private static final String JavaDoc KEY_NAME= "name"; //$NON-NLS-1$
78
private static final String JavaDoc KEY_TYPE= "type"; //$NON-NLS-1$
79
private static final String JavaDoc KEY_INITIALIZER= "initializer"; //$NON-NLS-1$
80

81     final private int fVariableKind;
82     final private SimpleName fOriginalNode;
83     final private ITypeBinding fSenderBinding;
84
85     public NewVariableCompletionProposal(String JavaDoc label, ICompilationUnit cu, int variableKind, SimpleName node, ITypeBinding senderBinding, int relevance, Image image) {
86         super(label, cu, null, relevance, image);
87         if (senderBinding == null) {
88             Assert.isTrue(variableKind == PARAM || variableKind == LOCAL);
89         } else {
90             Assert.isTrue(Bindings.isDeclarationBinding(senderBinding));
91         }
92
93         fVariableKind= variableKind;
94         fOriginalNode= node;
95         fSenderBinding= senderBinding;
96     }
97
98     protected ASTRewrite getRewrite() throws CoreException {
99         CompilationUnit cu= ASTResolving.findParentCompilationUnit(fOriginalNode);
100         switch (fVariableKind) {
101             case PARAM:
102                 return doAddParam(cu);
103             case FIELD:
104             case CONST_FIELD:
105                 return doAddField(cu);
106             case LOCAL:
107                 return doAddLocal(cu);
108             case ENUM_CONST:
109                 return doAddEnumConst(cu);
110             default:
111                 throw new IllegalArgumentException JavaDoc("Unsupported variable kind: " + fVariableKind); //$NON-NLS-1$
112
}
113     }
114
115     private ASTRewrite doAddParam(CompilationUnit cu) throws CoreException {
116         AST ast= cu.getAST();
117         SimpleName node= fOriginalNode;
118
119         BodyDeclaration decl= ASTResolving.findParentBodyDeclaration(node);
120         if (decl instanceof MethodDeclaration) {
121             MethodDeclaration methodDeclaration= (MethodDeclaration) decl;
122
123             ASTRewrite rewrite= ASTRewrite.create(ast);
124             
125             ImportRewrite imports= createImportRewrite((CompilationUnit) decl.getRoot());
126
127             SingleVariableDeclaration newDecl= ast.newSingleVariableDeclaration();
128             newDecl.setType(evaluateVariableType(ast, imports, methodDeclaration.resolveBinding()));
129             newDecl.setName(ast.newSimpleName(node.getIdentifier()));
130
131             ListRewrite listRewriter= rewrite.getListRewrite(decl, MethodDeclaration.PARAMETERS_PROPERTY);
132             listRewriter.insertLast(newDecl, null);
133
134             addLinkedPosition(rewrite.track(newDecl.getType()), false, KEY_TYPE);
135             addLinkedPosition(rewrite.track(node), true, KEY_NAME);
136             addLinkedPosition(rewrite.track(newDecl.getName()), false, KEY_NAME);
137
138             // add javadoc tag
139
Javadoc javadoc= methodDeclaration.getJavadoc();
140             if (javadoc != null) {
141                 HashSet JavaDoc leadingNames= new HashSet JavaDoc();
142                 for (Iterator JavaDoc iter= methodDeclaration.parameters().iterator(); iter.hasNext();) {
143                     SingleVariableDeclaration curr= (SingleVariableDeclaration) iter.next();
144                     leadingNames.add(curr.getName().getIdentifier());
145                 }
146                 SimpleName newTagRef= ast.newSimpleName(node.getIdentifier());
147
148                 TagElement newTagElement= ast.newTagElement();
149                 newTagElement.setTagName(TagElement.TAG_PARAM);
150                 newTagElement.fragments().add(newTagRef);
151                 TextElement commentStart= ast.newTextElement();
152                 newTagElement.fragments().add(commentStart);
153
154                 addLinkedPosition(rewrite.track(newTagRef), true, KEY_NAME);
155                 addLinkedPosition(rewrite.track(commentStart), false, "comment_start"); //$NON-NLS-1$
156

157                 ListRewrite tagsRewriter= rewrite.getListRewrite(javadoc, Javadoc.TAGS_PROPERTY);
158                 JavadocTagsSubProcessor.insertTag(tagsRewriter, newTagElement, leadingNames);
159             }
160
161             return rewrite;
162         }
163         return null;
164     }
165
166     private boolean isAssigned(Statement statement, SimpleName name) {
167         if (statement instanceof ExpressionStatement) {
168             ExpressionStatement exstat= (ExpressionStatement) statement;
169             if (exstat.getExpression() instanceof Assignment) {
170                 Assignment assignment= (Assignment) exstat.getExpression();
171                 return assignment.getLeftHandSide() == name;
172             }
173         }
174         return false;
175     }
176
177     private boolean isForStatementInit(Statement statement, SimpleName name) {
178         if (statement instanceof ForStatement) {
179             ForStatement forStatement= (ForStatement) statement;
180             List JavaDoc list = forStatement.initializers();
181             if (list.size() == 1 && list.get(0) instanceof Assignment) {
182                 Assignment assignment= (Assignment) list.get(0);
183                 return assignment.getLeftHandSide() == name;
184             }
185         }
186         return false;
187     }
188
189
190     private ASTRewrite doAddLocal(CompilationUnit cu) throws CoreException {
191         AST ast= cu.getAST();
192
193         Block body;
194         BodyDeclaration decl= ASTResolving.findParentBodyDeclaration(fOriginalNode);
195         IBinding targetContext= null;
196         if (decl instanceof MethodDeclaration) {
197             body= (((MethodDeclaration) decl).getBody());
198             targetContext= ((MethodDeclaration) decl).resolveBinding();
199         } else if (decl instanceof Initializer) {
200             body= (((Initializer) decl).getBody());
201             targetContext= Bindings.getBindingOfParentType(decl);
202         } else {
203             return null;
204         }
205         ASTRewrite rewrite= ASTRewrite.create(ast);
206         
207         ImportRewrite imports= createImportRewrite((CompilationUnit) decl.getRoot());
208
209         SimpleName[] names= getAllReferences(body);
210         ASTNode dominant= getDominantNode(names);
211
212         Statement dominantStatement= ASTResolving.findParentStatement(dominant);
213         if (ASTNodes.isControlStatementBody(dominantStatement.getLocationInParent())) {
214             dominantStatement= (Statement) dominantStatement.getParent();
215         }
216
217         SimpleName node= names[0];
218
219         if (isAssigned(dominantStatement, node)) {
220             // x = 1; -> int x = 1;
221
Assignment assignment= (Assignment) node.getParent();
222
223             // trick to avoid comment removal around the statement: keep the expression statement
224
// and replace the assignment with an VariableDeclarationExpression
225
VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment();
226             VariableDeclarationExpression newDecl= ast.newVariableDeclarationExpression(newDeclFrag);
227             newDecl.setType(evaluateVariableType(ast, imports, targetContext));
228
229             Expression placeholder= (Expression) rewrite.createCopyTarget(assignment.getRightHandSide());
230             newDeclFrag.setInitializer(placeholder);
231             newDeclFrag.setName(ast.newSimpleName(node.getIdentifier()));
232             rewrite.replace(assignment, newDecl, null);
233
234             addLinkedPosition(rewrite.track(newDecl.getType()), false, KEY_TYPE);
235             addLinkedPosition(rewrite.track(newDeclFrag.getName()), true, KEY_NAME);
236
237             setEndPosition(rewrite.track(assignment.getParent()));
238
239             return rewrite;
240         } else if ((dominant != dominantStatement) && isForStatementInit(dominantStatement, node)) {
241             // for (x = 1;;) ->for (int x = 1;;)
242

243             Assignment assignment= (Assignment) node.getParent();
244
245             VariableDeclarationFragment frag= ast.newVariableDeclarationFragment();
246             VariableDeclarationExpression expression= ast.newVariableDeclarationExpression(frag);
247             frag.setName(ast.newSimpleName(node.getIdentifier()));
248             Expression placeholder= (Expression) rewrite.createCopyTarget(assignment.getRightHandSide());
249             frag.setInitializer(placeholder);
250             expression.setType(evaluateVariableType(ast, imports, targetContext));
251
252             rewrite.replace(assignment, expression, null);
253
254             addLinkedPosition(rewrite.track(expression.getType()), false, KEY_TYPE);
255             addLinkedPosition(rewrite.track(frag.getName()), true, KEY_NAME);
256
257             setEndPosition(rewrite.track(expression));
258
259             return rewrite;
260         }
261         // foo(x) -> int x; foo(x)
262

263         VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment();
264         VariableDeclarationStatement newDecl= ast.newVariableDeclarationStatement(newDeclFrag);
265
266         newDeclFrag.setName(ast.newSimpleName(node.getIdentifier()));
267         newDecl.setType(evaluateVariableType(ast, imports, targetContext));
268 // newDeclFrag.setInitializer(ASTNodeFactory.newDefaultExpression(ast, newDecl.getType(), 0));
269

270         addLinkedPosition(rewrite.track(newDecl.getType()), false, KEY_TYPE);
271         addLinkedPosition(rewrite.track(node), true, KEY_NAME);
272         addLinkedPosition(rewrite.track(newDeclFrag.getName()), false, KEY_NAME);
273
274         Statement statement= dominantStatement;
275         List JavaDoc list= ASTNodes.getContainingList(statement);
276         while (list == null && statement.getParent() instanceof Statement) { // parent must be if, for or while
277
statement= (Statement) statement.getParent();
278             list= ASTNodes.getContainingList(statement);
279         }
280         if (list != null) {
281             ASTNode parent= statement.getParent();
282             StructuralPropertyDescriptor childProperty= statement.getLocationInParent();
283             if (childProperty.isChildListProperty()) {
284                 rewrite.getListRewrite(parent, (ChildListPropertyDescriptor) childProperty).insertBefore(newDecl, statement, null);
285                 return rewrite;
286             } else {
287                 return null;
288             }
289         }
290         return rewrite;
291     }
292
293     private SimpleName[] getAllReferences(Block body) {
294         SimpleName[] names= LinkedNodeFinder.findByProblems(body, fOriginalNode);
295         if (names == null) {
296             return new SimpleName[] { fOriginalNode };
297         }
298         if (names.length > 1) {
299             Arrays.sort(names, new Comparator JavaDoc() {
300                 public int compare(Object JavaDoc o1, Object JavaDoc o2) {
301                     return ((SimpleName) o1).getStartPosition() - ((SimpleName) o2).getStartPosition();
302                 }
303             });
304         }
305         return names;
306     }
307
308
309     private ASTNode getDominantNode(SimpleName[] names) {
310         ASTNode dominator= names[0]; //ASTResolving.findParentStatement(names[0]);
311
for (int i= 1; i < names.length; i++) {
312             ASTNode curr= names[i];// ASTResolving.findParentStatement(names[i]);
313
if (curr != dominator) {
314                 ASTNode parent= getCommonParent(curr, dominator);
315
316                 if (curr.getStartPosition() < dominator.getStartPosition()) {
317                     dominator= curr;
318                 }
319                 while (dominator.getParent() != parent) {
320                     dominator= dominator.getParent();
321                 }
322             }
323         }
324         int parentKind= dominator.getParent().getNodeType();
325         if (parentKind != ASTNode.BLOCK && parentKind != ASTNode.FOR_STATEMENT) {
326             return dominator.getParent();
327         }
328         return dominator;
329     }
330
331     private ASTNode getCommonParent(ASTNode node1, ASTNode node2) {
332         ASTNode parent= node1.getParent();
333         while (parent != null && !ASTNodes.isParent(node2, parent)) {
334             parent= parent.getParent();
335         }
336         return parent;
337     }
338
339     private ASTRewrite doAddField(CompilationUnit astRoot) throws CoreException {
340         SimpleName node= fOriginalNode;
341         boolean isInDifferentCU= false;
342
343         ASTNode newTypeDecl= astRoot.findDeclaringNode(fSenderBinding);
344         if (newTypeDecl == null) {
345             astRoot= ASTResolving.createQuickFixAST(getCompilationUnit(), null);
346             newTypeDecl= astRoot.findDeclaringNode(fSenderBinding.getKey());
347             isInDifferentCU= true;
348         }
349         ImportRewrite imports= createImportRewrite(astRoot);
350
351         if (newTypeDecl != null) {
352             AST ast= newTypeDecl.getAST();
353
354             ASTRewrite rewrite= ASTRewrite.create(ast);
355
356             VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
357             fragment.setName(ast.newSimpleName(node.getIdentifier()));
358
359             Type type= evaluateVariableType(ast, imports, fSenderBinding);
360
361             FieldDeclaration newDecl= ast.newFieldDeclaration(fragment);
362             newDecl.setType(type);
363             newDecl.modifiers().addAll(ASTNodeFactory.newModifiers(ast, evaluateFieldModifiers(newTypeDecl)));
364
365             if (fSenderBinding.isInterface() || fVariableKind == CONST_FIELD) {
366                 fragment.setInitializer(ASTNodeFactory.newDefaultExpression(ast, type, 0));
367             }
368
369             ChildListPropertyDescriptor property= ASTNodes.getBodyDeclarationsProperty(newTypeDecl);
370             List JavaDoc decls= (List JavaDoc) newTypeDecl.getStructuralProperty(property);
371
372             int maxOffset= isInDifferentCU ? -1 : node.getStartPosition();
373             
374             int insertIndex= findFieldInsertIndex(decls, newDecl, maxOffset);
375
376             ListRewrite listRewriter= rewrite.getListRewrite(newTypeDecl, property);
377             listRewriter.insertAt(newDecl, insertIndex, null);
378
379             ModifierCorrectionSubProcessor.installLinkedVisibilityProposals(getLinkedProposalModel(), rewrite, newDecl.modifiers(), fSenderBinding.isInterface());
380             
381             addLinkedPosition(rewrite.track(newDecl.getType()), false, KEY_TYPE);
382             if (!isInDifferentCU) {
383                 addLinkedPosition(rewrite.track(node), true, KEY_NAME);
384             }
385             addLinkedPosition(rewrite.track(fragment.getName()), false, KEY_NAME);
386
387             if (fragment.getInitializer() != null) {
388                 addLinkedPosition(rewrite.track(fragment.getInitializer()), false, KEY_INITIALIZER);
389             }
390             return rewrite;
391         }
392         return null;
393     }
394
395     private int findFieldInsertIndex(List JavaDoc decls, FieldDeclaration newDecl, int maxOffset) {
396         if (maxOffset != -1) {
397             for (int i= decls.size() - 1; i >= 0; i--) {
398                 ASTNode curr= (ASTNode) decls.get(i);
399                 if (maxOffset > curr.getStartPosition() + curr.getLength()) {
400                     return ASTNodes.getInsertionIndex(newDecl, decls.subList(0, i + 1));
401                 }
402             }
403             return 0;
404         }
405         return ASTNodes.getInsertionIndex(newDecl, decls);
406     }
407
408     private Type evaluateVariableType(AST ast, ImportRewrite imports, IBinding targetContext) throws CoreException {
409         if (fOriginalNode.getParent() instanceof MethodInvocation) {
410             MethodInvocation parent= (MethodInvocation) fOriginalNode.getParent();
411             if (parent.getExpression() == fOriginalNode) {
412                 // _x_.foo() -> guess qualifier type by looking for a type with method 'foo'
413
ITypeBinding[] bindings= ASTResolving.getQualifierGuess(fOriginalNode.getRoot(), parent.getName().getIdentifier(), parent.arguments(), targetContext);
414                 if (bindings.length > 0) {
415                     for (int i= 0; i < bindings.length; i++) {
416                         addLinkedPositionProposal(KEY_TYPE, bindings[i]);
417                     }
418                     return imports.addImport(bindings[0], ast);
419                 }
420             }
421         }
422
423         ITypeBinding binding= ASTResolving.guessBindingForReference(fOriginalNode);
424         if (binding != null) {
425             if (binding.isWildcardType()) {
426                 binding= ASTResolving.normalizeWildcardType(binding, isVariableAssigned(), ast);
427                 if (binding == null) {
428                     // only null binding applies
429
binding= ast.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
430
}
431             }
432             
433             if (isVariableAssigned()) {
434                 ITypeBinding[] typeProposals= ASTResolving.getRelaxingTypes(ast, binding);
435                 for (int i= 0; i < typeProposals.length; i++) {
436                     addLinkedPositionProposal(KEY_TYPE, typeProposals[i]);
437                 }
438             }
439             return imports.addImport(binding, ast);
440         }
441         // no binding, find type AST node instead -> ABC a= x-> use 'ABC' as is
442
Type type= ASTResolving.guessTypeForReference(ast, fOriginalNode);
443         if (type != null) {
444             return type;
445         }
446         if (fVariableKind == CONST_FIELD) {
447             return ast.newSimpleType(ast.newSimpleName("String")); //$NON-NLS-1$
448
}
449         return ast.newSimpleType(ast.newSimpleName("Object")); //$NON-NLS-1$
450
}
451
452     private boolean isVariableAssigned() {
453         ASTNode parent= fOriginalNode.getParent();
454         return (parent instanceof Assignment) && (fOriginalNode == ((Assignment) parent).getLeftHandSide());
455     }
456
457
458     private int evaluateFieldModifiers(ASTNode newTypeDecl) {
459         if (fSenderBinding.isAnnotation()) {
460             return 0;
461         }
462         if (fSenderBinding.isInterface()) {
463             // for interface members copy the modifiers from an existing field
464
FieldDeclaration[] fieldDecls= ((TypeDeclaration) newTypeDecl).getFields();
465             if (fieldDecls.length > 0) {
466                 return fieldDecls[0].getModifiers();
467             }
468             return 0;
469         }
470         int modifiers= 0;
471
472         if (fVariableKind == CONST_FIELD) {
473             modifiers |= Modifier.FINAL | Modifier.STATIC;
474         } else {
475             ASTNode parent= fOriginalNode.getParent();
476             if (parent instanceof QualifiedName) {
477                 IBinding qualifierBinding= ((QualifiedName)parent).getQualifier().resolveBinding();
478                 if (qualifierBinding instanceof ITypeBinding) {
479                     modifiers |= Modifier.STATIC;
480                 }
481             } else if (ASTResolving.isInStaticContext(fOriginalNode)) {
482                 modifiers |= Modifier.STATIC;
483             }
484         }
485         ASTNode node= ASTResolving.findParentType(fOriginalNode, true);
486         if (newTypeDecl.equals(node)) {
487             modifiers |= Modifier.PRIVATE;
488         } else if (node instanceof AnonymousClassDeclaration) {
489             modifiers |= Modifier.PROTECTED;
490         } else {
491             modifiers |= Modifier.PUBLIC;
492         }
493
494         return modifiers;
495     }
496
497     private ASTRewrite doAddEnumConst(CompilationUnit astRoot) throws CoreException {
498         SimpleName node= fOriginalNode;
499
500         ASTNode newTypeDecl= astRoot.findDeclaringNode(fSenderBinding);
501         if (newTypeDecl == null) {
502             astRoot= ASTResolving.createQuickFixAST(getCompilationUnit(), null);
503             newTypeDecl= astRoot.findDeclaringNode(fSenderBinding.getKey());
504         }
505
506         if (newTypeDecl != null) {
507             AST ast= newTypeDecl.getAST();
508
509             ASTRewrite rewrite= ASTRewrite.create(ast);
510
511             EnumConstantDeclaration constDecl= ast.newEnumConstantDeclaration();
512             constDecl.setName(ast.newSimpleName(node.getIdentifier()));
513
514             ListRewrite listRewriter= rewrite.getListRewrite(newTypeDecl, EnumDeclaration.ENUM_CONSTANTS_PROPERTY);
515             listRewriter.insertLast(constDecl, null);
516
517             addLinkedPosition(rewrite.track(constDecl.getName()), false, KEY_NAME);
518
519             return rewrite;
520         }
521         return null;
522     }
523
524
525
526     /**
527      * Returns the variable kind.
528      * @return int
529      */

530     public int getVariableKind() {
531         return fVariableKind;
532     }
533
534 }
535
Popular Tags