]>
Commit | Line | Data |
---|---|---|
1b2798f6 EK |
1 | /******************************************************************************* |
2 | * Copyright (c) 2000, 2011 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 | * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for | |
11 | * bug "inline method - doesn't handle implicit cast" (see | |
12 | * https://bugs.eclipse.org/bugs/show_bug.cgi?id=24941). | |
13 | * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fix for | |
14 | * bug Encapsulate field can fail when two variables in one variable declaration (see | |
15 | * https://bugs.eclipse.org/bugs/show_bug.cgi?id=51540). | |
16 | *******************************************************************************/ | |
17 | package org.eclipse.jdt.internal.corext.dom; | |
18 | ||
19 | import java.util.ArrayList; | |
20 | import java.util.List; | |
21 | import java.util.Map; | |
22 | ||
23 | import org.eclipse.core.runtime.Assert; | |
24 | ||
25 | import org.eclipse.text.edits.TextEdit; | |
26 | ||
27 | import org.eclipse.jface.text.BadLocationException; | |
28 | import org.eclipse.jface.text.Document; | |
29 | ||
30 | import org.eclipse.jdt.core.Flags; | |
31 | import org.eclipse.jdt.core.IBuffer; | |
32 | import org.eclipse.jdt.core.IField; | |
33 | import org.eclipse.jdt.core.IJavaElement; | |
34 | import org.eclipse.jdt.core.IJavaProject; | |
35 | import org.eclipse.jdt.core.IMember; | |
36 | import org.eclipse.jdt.core.ISourceReference; | |
37 | import org.eclipse.jdt.core.IType; | |
38 | import org.eclipse.jdt.core.ITypeRoot; | |
39 | import org.eclipse.jdt.core.JavaModelException; | |
40 | import org.eclipse.jdt.core.compiler.IProblem; | |
41 | import org.eclipse.jdt.core.dom.AST; | |
42 | import org.eclipse.jdt.core.dom.ASTNode; | |
43 | import org.eclipse.jdt.core.dom.ASTVisitor; | |
44 | import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; | |
45 | import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; | |
46 | import org.eclipse.jdt.core.dom.ArrayType; | |
47 | import org.eclipse.jdt.core.dom.Assignment; | |
48 | import org.eclipse.jdt.core.dom.BodyDeclaration; | |
49 | import org.eclipse.jdt.core.dom.CharacterLiteral; | |
50 | import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; | |
51 | import org.eclipse.jdt.core.dom.ClassInstanceCreation; | |
52 | import org.eclipse.jdt.core.dom.CompilationUnit; | |
53 | import org.eclipse.jdt.core.dom.DoStatement; | |
54 | import org.eclipse.jdt.core.dom.EnhancedForStatement; | |
55 | import org.eclipse.jdt.core.dom.EnumConstantDeclaration; | |
56 | import org.eclipse.jdt.core.dom.Expression; | |
57 | import org.eclipse.jdt.core.dom.FieldAccess; | |
58 | import org.eclipse.jdt.core.dom.FieldDeclaration; | |
59 | import org.eclipse.jdt.core.dom.ForStatement; | |
60 | import org.eclipse.jdt.core.dom.IBinding; | |
61 | import org.eclipse.jdt.core.dom.IExtendedModifier; | |
62 | import org.eclipse.jdt.core.dom.IMethodBinding; | |
63 | import org.eclipse.jdt.core.dom.ITypeBinding; | |
64 | import org.eclipse.jdt.core.dom.IVariableBinding; | |
65 | import org.eclipse.jdt.core.dom.IfStatement; | |
66 | import org.eclipse.jdt.core.dom.InfixExpression; | |
67 | import org.eclipse.jdt.core.dom.Message; | |
68 | import org.eclipse.jdt.core.dom.MethodDeclaration; | |
69 | import org.eclipse.jdt.core.dom.MethodInvocation; | |
70 | import org.eclipse.jdt.core.dom.Modifier; | |
71 | import org.eclipse.jdt.core.dom.Name; | |
72 | import org.eclipse.jdt.core.dom.NodeFinder; | |
73 | import org.eclipse.jdt.core.dom.ParameterizedType; | |
74 | import org.eclipse.jdt.core.dom.ParenthesizedExpression; | |
75 | import org.eclipse.jdt.core.dom.PrimitiveType; | |
76 | import org.eclipse.jdt.core.dom.QualifiedName; | |
77 | import org.eclipse.jdt.core.dom.QualifiedType; | |
78 | import org.eclipse.jdt.core.dom.SimpleName; | |
79 | import org.eclipse.jdt.core.dom.SimpleType; | |
80 | import org.eclipse.jdt.core.dom.SingleVariableDeclaration; | |
81 | import org.eclipse.jdt.core.dom.StringLiteral; | |
82 | import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor; | |
83 | import org.eclipse.jdt.core.dom.Type; | |
84 | import org.eclipse.jdt.core.dom.VariableDeclaration; | |
85 | import org.eclipse.jdt.core.dom.VariableDeclarationExpression; | |
86 | import org.eclipse.jdt.core.dom.VariableDeclarationFragment; | |
87 | import org.eclipse.jdt.core.dom.VariableDeclarationStatement; | |
88 | import org.eclipse.jdt.core.dom.WhileStatement; | |
89 | ||
90 | import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; | |
91 | import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil; | |
92 | import org.eclipse.jdt.internal.corext.util.Strings; | |
93 | ||
94 | import org.eclipse.jdt.internal.ui.JavaPlugin; | |
95 | import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; | |
96 | import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache; | |
97 | import org.eclipse.jdt.internal.ui.text.correction.ASTResolving; | |
98 | ||
99 | public class ASTNodes { | |
100 | ||
101 | public static final int NODE_ONLY= 0; | |
102 | public static final int INCLUDE_FIRST_PARENT= 1; | |
103 | public static final int INCLUDE_ALL_PARENTS= 2; | |
104 | ||
105 | public static final int WARNING= 1 << 0; | |
106 | public static final int ERROR= 1 << 1; | |
107 | public static final int PROBLEMS= WARNING | ERROR; | |
108 | ||
109 | private static final Message[] EMPTY_MESSAGES= new Message[0]; | |
110 | private static final IProblem[] EMPTY_PROBLEMS= new IProblem[0]; | |
111 | ||
112 | private static final int CLEAR_VISIBILITY= ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE); | |
113 | ||
114 | ||
115 | private ASTNodes() { | |
116 | // no instance; | |
117 | } | |
118 | ||
119 | public static String asString(ASTNode node) { | |
120 | ASTFlattener flattener= new ASTFlattener(); | |
121 | node.accept(flattener); | |
122 | return flattener.getResult(); | |
123 | } | |
124 | ||
125 | public static String asFormattedString(ASTNode node, int indent, String lineDelim, Map<String, String> options) { | |
126 | String unformatted= asString(node); | |
127 | TextEdit edit= CodeFormatterUtil.format2(node, unformatted, indent, lineDelim, options); | |
128 | if (edit != null) { | |
129 | Document document= new Document(unformatted); | |
130 | try { | |
131 | edit.apply(document, TextEdit.NONE); | |
132 | } catch (BadLocationException e) { | |
133 | JavaPlugin.log(e); | |
134 | } | |
135 | return document.get(); | |
136 | } | |
137 | return unformatted; // unknown node | |
138 | } | |
139 | ||
140 | ||
141 | /** | |
142 | * Returns the source of the given node from the location where it was parsed. | |
143 | * @param node the node to get the source from | |
144 | * @param extendedRange if set, the extended ranges of the nodes should ne used | |
145 | * @param removeIndent if set, the indentation is removed. | |
146 | * @return return the source for the given node or null if accessing the source failed. | |
147 | */ | |
148 | public static String getNodeSource(ASTNode node, boolean extendedRange, boolean removeIndent) { | |
149 | ASTNode root= node.getRoot(); | |
150 | if (root instanceof CompilationUnit) { | |
151 | CompilationUnit astRoot= (CompilationUnit) root; | |
152 | ITypeRoot typeRoot= astRoot.getTypeRoot(); | |
153 | try { | |
154 | if (typeRoot != null && typeRoot.getBuffer() != null) { | |
155 | IBuffer buffer= typeRoot.getBuffer(); | |
156 | int offset= extendedRange ? astRoot.getExtendedStartPosition(node) : node.getStartPosition(); | |
157 | int length= extendedRange ? astRoot.getExtendedLength(node) : node.getLength(); | |
158 | String str= buffer.getText(offset, length); | |
159 | if (removeIndent) { | |
160 | IJavaProject project= typeRoot.getJavaProject(); | |
161 | int indent= StubUtility.getIndentUsed(buffer, node.getStartPosition(), project); | |
162 | str= Strings.changeIndent(str, indent, project, new String(), typeRoot.findRecommendedLineSeparator()); | |
163 | } | |
164 | return str; | |
165 | } | |
166 | } catch (JavaModelException e) { | |
167 | // ignore | |
168 | } | |
169 | } | |
170 | return null; | |
171 | } | |
172 | ||
173 | /** | |
174 | * Returns the list that contains the given ASTNode. If the node | |
175 | * isn't part of any list, <code>null</code> is returned. | |
176 | * | |
177 | * @param node the node in question | |
178 | * @return the list that contains the node or <code>null</code> | |
179 | */ | |
180 | public static List<? extends ASTNode> getContainingList(ASTNode node) { | |
181 | StructuralPropertyDescriptor locationInParent= node.getLocationInParent(); | |
182 | if (locationInParent != null && locationInParent.isChildListProperty()) { | |
183 | return (List<? extends ASTNode>) node.getParent().getStructuralProperty(locationInParent); | |
184 | } | |
185 | return null; | |
186 | } | |
187 | ||
188 | /** | |
189 | * Returns a list of the direct children of a node. The siblings are ordered by start offset. | |
190 | * @param node the node to get the children for | |
191 | * @return the children | |
192 | */ | |
193 | public static List<ASTNode> getChildren(ASTNode node) { | |
194 | ChildrenCollector visitor= new ChildrenCollector(); | |
195 | node.accept(visitor); | |
196 | return visitor.result; | |
197 | } | |
198 | ||
199 | private static class ChildrenCollector extends GenericVisitor { | |
200 | public List<ASTNode> result; | |
201 | ||
202 | public ChildrenCollector() { | |
203 | super(true); | |
204 | result= null; | |
205 | } | |
206 | @Override | |
207 | protected boolean visitNode(ASTNode node) { | |
208 | if (result == null) { // first visitNode: on the node's parent: do nothing, return true | |
209 | result= new ArrayList<ASTNode>(); | |
210 | return true; | |
211 | } | |
212 | result.add(node); | |
213 | return false; | |
214 | } | |
215 | } | |
216 | ||
217 | /** | |
218 | * Returns true if this is an existing node, i.e. it was created as part of | |
219 | * a parsing process of a source code file. Returns false if this is a newly | |
220 | * created node which has not yet been given a source position. | |
221 | * | |
222 | * @param node the node to be tested. | |
223 | * @return true if this is an existing node, false if not. | |
224 | */ | |
225 | public static boolean isExistingNode(ASTNode node) { | |
226 | return node.getStartPosition() != -1; | |
227 | } | |
228 | ||
229 | /** | |
230 | * Returns the element type. This is a convenience method that returns its | |
231 | * argument if it is a simple type and the element type if the parameter is an array type. | |
232 | * @param type The type to get the element type from. | |
233 | * @return The element type of the type or the type itself. | |
234 | */ | |
235 | public static Type getElementType(Type type) { | |
236 | if (! type.isArrayType()) | |
237 | return type; | |
238 | return ((ArrayType)type).getElementType(); | |
239 | } | |
240 | ||
241 | public static ASTNode findDeclaration(IBinding binding, ASTNode root) { | |
242 | root= root.getRoot(); | |
243 | if (root instanceof CompilationUnit) { | |
244 | return ((CompilationUnit)root).findDeclaringNode(binding); | |
245 | } | |
246 | return null; | |
247 | } | |
248 | ||
249 | public static VariableDeclaration findVariableDeclaration(IVariableBinding binding, ASTNode root) { | |
250 | if (binding.isField()) | |
251 | return null; | |
252 | ASTNode result= findDeclaration(binding, root); | |
253 | if (result instanceof VariableDeclaration) | |
254 | return (VariableDeclaration)result; | |
255 | ||
256 | return null; | |
257 | } | |
258 | ||
259 | /** | |
260 | * Returns the type node for the given declaration. | |
261 | * @param declaration the declaration | |
262 | * @return the type node | |
263 | */ | |
264 | public static Type getType(VariableDeclaration declaration) { | |
265 | if (declaration instanceof SingleVariableDeclaration) { | |
266 | return ((SingleVariableDeclaration)declaration).getType(); | |
267 | } else if (declaration instanceof VariableDeclarationFragment) { | |
268 | ASTNode parent= ((VariableDeclarationFragment)declaration).getParent(); | |
269 | if (parent instanceof VariableDeclarationExpression) | |
270 | return ((VariableDeclarationExpression)parent).getType(); | |
271 | else if (parent instanceof VariableDeclarationStatement) | |
272 | return ((VariableDeclarationStatement)parent).getType(); | |
273 | else if (parent instanceof FieldDeclaration) | |
274 | return ((FieldDeclaration)parent).getType(); | |
275 | } | |
276 | Assert.isTrue(false, "Unknown VariableDeclaration"); //$NON-NLS-1$ | |
277 | return null; | |
278 | } | |
279 | ||
280 | public static int getDimensions(VariableDeclaration declaration) { | |
281 | int dim= declaration.getExtraDimensions(); | |
282 | Type type= getType(declaration); | |
283 | if (type instanceof ArrayType) { | |
284 | dim += ((ArrayType) type).getDimensions(); | |
285 | } | |
286 | return dim; | |
287 | } | |
288 | ||
289 | public static List<IExtendedModifier> getModifiers(VariableDeclaration declaration) { | |
290 | Assert.isNotNull(declaration); | |
291 | if (declaration instanceof SingleVariableDeclaration) { | |
292 | return ((SingleVariableDeclaration)declaration).modifiers(); | |
293 | } else if (declaration instanceof VariableDeclarationFragment) { | |
294 | ASTNode parent= declaration.getParent(); | |
295 | if (parent instanceof VariableDeclarationExpression) | |
296 | return ((VariableDeclarationExpression)parent).modifiers(); | |
297 | else if (parent instanceof VariableDeclarationStatement) | |
298 | return ((VariableDeclarationStatement)parent).modifiers(); | |
299 | } | |
300 | return new ArrayList<IExtendedModifier>(0); | |
301 | } | |
302 | ||
303 | public static boolean isSingleDeclaration(VariableDeclaration declaration) { | |
304 | Assert.isNotNull(declaration); | |
305 | if (declaration instanceof SingleVariableDeclaration) { | |
306 | return true; | |
307 | } else if (declaration instanceof VariableDeclarationFragment) { | |
308 | ASTNode parent= declaration.getParent(); | |
309 | if (parent instanceof VariableDeclarationExpression) | |
310 | return ((VariableDeclarationExpression)parent).fragments().size() == 1; | |
311 | else if (parent instanceof VariableDeclarationStatement) | |
312 | return ((VariableDeclarationStatement)parent).fragments().size() == 1; | |
313 | } | |
314 | return false; | |
315 | } | |
316 | ||
317 | public static boolean isLiteral(Expression expression) { | |
318 | int type= expression.getNodeType(); | |
319 | return type == ASTNode.BOOLEAN_LITERAL || type == ASTNode.CHARACTER_LITERAL || type == ASTNode.NULL_LITERAL || | |
320 | type == ASTNode.NUMBER_LITERAL || type == ASTNode.STRING_LITERAL || type == ASTNode.TYPE_LITERAL; | |
321 | } | |
322 | ||
323 | public static boolean isLabel(SimpleName name) { | |
324 | int parentType= name.getParent().getNodeType(); | |
325 | return parentType == ASTNode.LABELED_STATEMENT || | |
326 | parentType == ASTNode.BREAK_STATEMENT || parentType != ASTNode.CONTINUE_STATEMENT; | |
327 | } | |
328 | ||
329 | public static boolean isStatic(BodyDeclaration declaration) { | |
330 | return Modifier.isStatic(declaration.getModifiers()); | |
331 | } | |
332 | ||
333 | public static List<BodyDeclaration> getBodyDeclarations(ASTNode node) { | |
334 | if (node instanceof AbstractTypeDeclaration) { | |
335 | return ((AbstractTypeDeclaration)node).bodyDeclarations(); | |
336 | } else if (node instanceof AnonymousClassDeclaration) { | |
337 | return ((AnonymousClassDeclaration)node).bodyDeclarations(); | |
338 | } | |
339 | // should not happen. | |
340 | Assert.isTrue(false); | |
341 | return null; | |
342 | } | |
343 | ||
344 | /** | |
345 | * Returns the structural property descriptor for the "bodyDeclarations" property | |
346 | * of this node (element type: {@link BodyDeclaration}). | |
347 | * | |
348 | * @param node the node, either an {@link AbstractTypeDeclaration} or an {@link AnonymousClassDeclaration} | |
349 | * @return the property descriptor | |
350 | */ | |
351 | public static ChildListPropertyDescriptor getBodyDeclarationsProperty(ASTNode node) { | |
352 | if (node instanceof AbstractTypeDeclaration) { | |
353 | return ((AbstractTypeDeclaration)node).getBodyDeclarationsProperty(); | |
354 | } else if (node instanceof AnonymousClassDeclaration) { | |
355 | return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY; | |
356 | } | |
357 | // should not happen. | |
358 | Assert.isTrue(false); | |
359 | return null; | |
360 | } | |
361 | ||
362 | public static String getTypeName(Type type) { | |
363 | final StringBuffer buffer= new StringBuffer(); | |
364 | ASTVisitor visitor= new ASTVisitor() { | |
365 | @Override | |
366 | public boolean visit(PrimitiveType node) { | |
367 | buffer.append(node.getPrimitiveTypeCode().toString()); | |
368 | return false; | |
369 | } | |
370 | @Override | |
371 | public boolean visit(SimpleName node) { | |
372 | buffer.append(node.getIdentifier()); | |
373 | return false; | |
374 | } | |
375 | @Override | |
376 | public boolean visit(QualifiedName node) { | |
377 | buffer.append(node.getName().getIdentifier()); | |
378 | return false; | |
379 | } | |
380 | @Override | |
381 | public void endVisit(ArrayType node) { | |
382 | buffer.append("[]"); //$NON-NLS-1$ | |
383 | } | |
384 | }; | |
385 | type.accept(visitor); | |
386 | return buffer.toString(); | |
387 | } | |
388 | ||
389 | public static InfixExpression.Operator convertToInfixOperator(Assignment.Operator operator) { | |
390 | if (operator.equals(Assignment.Operator.PLUS_ASSIGN)) | |
391 | return InfixExpression.Operator.PLUS; | |
392 | ||
393 | if (operator.equals(Assignment.Operator.MINUS_ASSIGN)) | |
394 | return InfixExpression.Operator.MINUS; | |
395 | ||
396 | if (operator.equals(Assignment.Operator.TIMES_ASSIGN)) | |
397 | return InfixExpression.Operator.TIMES; | |
398 | ||
399 | if (operator.equals(Assignment.Operator.DIVIDE_ASSIGN)) | |
400 | return InfixExpression.Operator.DIVIDE; | |
401 | ||
402 | if (operator.equals(Assignment.Operator.BIT_AND_ASSIGN)) | |
403 | return InfixExpression.Operator.AND; | |
404 | ||
405 | if (operator.equals(Assignment.Operator.BIT_OR_ASSIGN)) | |
406 | return InfixExpression.Operator.OR; | |
407 | ||
408 | if (operator.equals(Assignment.Operator.BIT_XOR_ASSIGN)) | |
409 | return InfixExpression.Operator.XOR; | |
410 | ||
411 | if (operator.equals(Assignment.Operator.REMAINDER_ASSIGN)) | |
412 | return InfixExpression.Operator.REMAINDER; | |
413 | ||
414 | if (operator.equals(Assignment.Operator.LEFT_SHIFT_ASSIGN)) | |
415 | return InfixExpression.Operator.LEFT_SHIFT; | |
416 | ||
417 | if (operator.equals(Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN)) | |
418 | return InfixExpression.Operator.RIGHT_SHIFT_SIGNED; | |
419 | ||
420 | if (operator.equals(Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN)) | |
421 | return InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED; | |
422 | ||
423 | Assert.isTrue(false, "Cannot convert assignment operator"); //$NON-NLS-1$ | |
424 | return null; | |
425 | } | |
426 | ||
427 | /** | |
428 | * Returns true if a node at a given location is a body of a control statement. Such body nodes are | |
429 | * interesting as when replacing them, it has to be evaluates if a Block is needed instead. | |
430 | * E.g. <code> if (x) do(); -> if (x) { do1(); do2() } </code> | |
431 | * | |
432 | * @param locationInParent Location of the body node | |
433 | * @return Returns true if the location is a body node location of a control statement. | |
434 | */ | |
435 | public static boolean isControlStatementBody(StructuralPropertyDescriptor locationInParent) { | |
436 | return locationInParent == IfStatement.THEN_STATEMENT_PROPERTY | |
437 | || locationInParent == IfStatement.ELSE_STATEMENT_PROPERTY | |
438 | || locationInParent == ForStatement.BODY_PROPERTY | |
439 | || locationInParent == EnhancedForStatement.BODY_PROPERTY | |
440 | || locationInParent == WhileStatement.BODY_PROPERTY | |
441 | || locationInParent == DoStatement.BODY_PROPERTY; | |
442 | } | |
443 | ||
444 | /** | |
445 | * Returns the type to which an inlined variable initializer should be cast, or | |
446 | * <code>null</code> if no cast is necessary. | |
447 | * | |
448 | * @param initializer the initializer expression of the variable to inline | |
449 | * @param reference the reference to the variable (which is to be inlined) | |
450 | * @return a type binding to which the initializer should be cast, or <code>null</code> iff no cast is necessary | |
451 | * @since 3.6 | |
452 | */ | |
453 | public static ITypeBinding getExplicitCast(Expression initializer, Expression reference) { | |
454 | ITypeBinding initializerType= initializer.resolveTypeBinding(); | |
455 | ITypeBinding referenceType= reference.resolveTypeBinding(); | |
456 | if (initializerType == null || referenceType == null) | |
457 | return null; | |
458 | ||
459 | if (initializerType.isPrimitive() && referenceType.isPrimitive() && ! referenceType.isEqualTo(initializerType)) { | |
460 | return referenceType; | |
461 | ||
462 | } else if (initializerType.isPrimitive() && ! referenceType.isPrimitive()) { // initializer is autoboxed | |
463 | ITypeBinding unboxedReferenceType= Bindings.getUnboxedTypeBinding(referenceType, reference.getAST()); | |
464 | if (!unboxedReferenceType.isEqualTo(initializerType)) | |
465 | return unboxedReferenceType; | |
466 | else if (needsExplicitBoxing(reference)) | |
467 | return referenceType; | |
468 | ||
469 | } else if (! initializerType.isPrimitive() && referenceType.isPrimitive()) { // initializer is autounboxed | |
470 | ITypeBinding unboxedInitializerType= Bindings.getUnboxedTypeBinding(initializerType, reference.getAST()); | |
471 | if (!unboxedInitializerType.isEqualTo(referenceType)) | |
472 | return referenceType; | |
473 | ||
474 | } else if (initializerType.isRawType() && referenceType.isParameterizedType()) { | |
475 | return referenceType; // don't lose the unchecked conversion | |
476 | ||
477 | } else if (! TypeRules.canAssign(initializerType, referenceType)) { | |
478 | if (!Bindings.containsTypeVariables(referenceType)) | |
479 | return referenceType; | |
480 | } | |
481 | ||
482 | return null; | |
483 | } | |
484 | ||
485 | /** | |
486 | * Returns whether an expression at the given location needs explicit boxing. | |
487 | * | |
488 | * @param expression the expression | |
489 | * @return <code>true</code> iff an expression at the given location needs explicit boxing | |
490 | * @since 3.6 | |
491 | */ | |
492 | private static boolean needsExplicitBoxing(Expression expression) { | |
493 | StructuralPropertyDescriptor locationInParent= expression.getLocationInParent(); | |
494 | if (locationInParent == ParenthesizedExpression.EXPRESSION_PROPERTY) | |
495 | return needsExplicitBoxing((ParenthesizedExpression) expression.getParent()); | |
496 | ||
497 | if (locationInParent == ClassInstanceCreation.EXPRESSION_PROPERTY | |
498 | || locationInParent == FieldAccess.EXPRESSION_PROPERTY | |
499 | || locationInParent == MethodInvocation.EXPRESSION_PROPERTY) | |
500 | return true; | |
501 | ||
502 | return false; | |
503 | } | |
504 | ||
505 | /** | |
506 | * Returns the closest ancestor of <code>node</code> that is an instance of <code>parentClass</code>, or <code>null</code> if none. | |
507 | * <p> | |
508 | * <b>Warning:</b> This method does not stop at any boundaries like parentheses, statements, body declarations, etc. | |
509 | * The resulting node may be in a totally different scope than the given node. | |
510 | * Consider using one of the {@link ASTResolving}<code>.find(..)</code> methods instead. | |
511 | * </p> | |
512 | * @param node the node | |
513 | * @param parentClass the class of the sought ancestor node | |
514 | * @return the closest ancestor of <code>node</code> that is an instance of <code>parentClass</code>, or <code>null</code> if none | |
515 | */ | |
516 | public static ASTNode getParent(ASTNode node, Class<? extends ASTNode> parentClass) { | |
517 | do { | |
518 | node= node.getParent(); | |
519 | } while (node != null && !parentClass.isInstance(node)); | |
520 | return node; | |
521 | } | |
522 | ||
523 | /** | |
524 | * Returns the closest ancestor of <code>node</code> whose type is <code>nodeType</code>, or <code>null</code> if none. | |
525 | * <p> | |
526 | * <b>Warning:</b> This method does not stop at any boundaries like parentheses, statements, body declarations, etc. | |
527 | * The resulting node may be in a totally different scope than the given node. | |
528 | * Consider using one of the {@link ASTResolving}<code>.find(..)</code> methods instead. | |
529 | * </p> | |
530 | * @param node the node | |
531 | * @param nodeType the node type constant from {@link ASTNode} | |
532 | * @return the closest ancestor of <code>node</code> whose type is <code>nodeType</code>, or <code>null</code> if none | |
533 | */ | |
534 | public static ASTNode getParent(ASTNode node, int nodeType) { | |
535 | do { | |
536 | node= node.getParent(); | |
537 | } while (node != null && node.getNodeType() != nodeType); | |
538 | return node; | |
539 | } | |
540 | ||
541 | public static ASTNode findParent(ASTNode node, StructuralPropertyDescriptor[][] pathes) { | |
542 | for (int p= 0; p < pathes.length; p++) { | |
543 | StructuralPropertyDescriptor[] path= pathes[p]; | |
544 | ASTNode current= node; | |
545 | int d= path.length - 1; | |
546 | for (; d >= 0 && current != null; d--) { | |
547 | StructuralPropertyDescriptor descriptor= path[d]; | |
548 | if (!descriptor.equals(current.getLocationInParent())) | |
549 | break; | |
550 | current= current.getParent(); | |
551 | } | |
552 | if (d < 0) | |
553 | return current; | |
554 | } | |
555 | return null; | |
556 | } | |
557 | ||
558 | public static ASTNode getNormalizedNode(ASTNode node) { | |
559 | ASTNode current= node; | |
560 | // normalize name | |
561 | if (QualifiedName.NAME_PROPERTY.equals(current.getLocationInParent())) { | |
562 | current= current.getParent(); | |
563 | } | |
564 | // normalize type | |
565 | if (QualifiedType.NAME_PROPERTY.equals(current.getLocationInParent()) || | |
566 | SimpleType.NAME_PROPERTY.equals(current.getLocationInParent())) { | |
567 | current= current.getParent(); | |
568 | } | |
569 | // normalize parameterized types | |
570 | if (ParameterizedType.TYPE_PROPERTY.equals(current.getLocationInParent())) { | |
571 | current= current.getParent(); | |
572 | } | |
573 | return current; | |
574 | } | |
575 | ||
576 | /** | |
577 | * Returns <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code> | |
578 | * (i.e. returns <code>false</code> if <code>parent == node</code>). | |
579 | * | |
580 | * @param node node to test | |
581 | * @param parent assumed parent | |
582 | * @return <code>true</code> iff <code>parent</code> is a true ancestor of <code>node</code> | |
583 | */ | |
584 | public static boolean isParent(ASTNode node, ASTNode parent) { | |
585 | Assert.isNotNull(parent); | |
586 | do { | |
587 | node= node.getParent(); | |
588 | if (node == parent) | |
589 | return true; | |
590 | } while (node != null); | |
591 | return false; | |
592 | } | |
593 | ||
594 | public static int getExclusiveEnd(ASTNode node){ | |
595 | return node.getStartPosition() + node.getLength(); | |
596 | } | |
597 | ||
598 | public static int getInclusiveEnd(ASTNode node){ | |
599 | return node.getStartPosition() + node.getLength() - 1; | |
600 | } | |
601 | ||
602 | public static IMethodBinding getMethodBinding(Name node) { | |
603 | IBinding binding= node.resolveBinding(); | |
604 | if (binding instanceof IMethodBinding) | |
605 | return (IMethodBinding)binding; | |
606 | return null; | |
607 | } | |
608 | ||
609 | public static IVariableBinding getVariableBinding(Name node) { | |
610 | IBinding binding= node.resolveBinding(); | |
611 | if (binding instanceof IVariableBinding) | |
612 | return (IVariableBinding)binding; | |
613 | return null; | |
614 | } | |
615 | ||
616 | public static IVariableBinding getLocalVariableBinding(Name node) { | |
617 | IVariableBinding result= getVariableBinding(node); | |
618 | if (result == null || result.isField()) | |
619 | return null; | |
620 | ||
621 | return result; | |
622 | } | |
623 | ||
624 | public static IVariableBinding getFieldBinding(Name node) { | |
625 | IVariableBinding result= getVariableBinding(node); | |
626 | if (result == null || !result.isField()) | |
627 | return null; | |
628 | ||
629 | return result; | |
630 | } | |
631 | ||
632 | public static ITypeBinding getTypeBinding(Name node) { | |
633 | IBinding binding= node.resolveBinding(); | |
634 | if (binding instanceof ITypeBinding) | |
635 | return (ITypeBinding)binding; | |
636 | return null; | |
637 | } | |
638 | ||
639 | /** | |
640 | * Returns the receiver's type binding of the given method invocation. | |
641 | * | |
642 | * @param invocation method invocation to resolve type of | |
643 | * @return the type binding of the receiver | |
644 | */ | |
645 | public static ITypeBinding getReceiverTypeBinding(MethodInvocation invocation) { | |
646 | ITypeBinding result= null; | |
647 | Expression exp= invocation.getExpression(); | |
648 | if(exp != null) { | |
649 | return exp.resolveTypeBinding(); | |
650 | } | |
651 | else { | |
652 | AbstractTypeDeclaration type= (AbstractTypeDeclaration)getParent(invocation, AbstractTypeDeclaration.class); | |
653 | if (type != null) | |
654 | return type.resolveBinding(); | |
655 | } | |
656 | return result; | |
657 | } | |
658 | ||
659 | public static ITypeBinding getEnclosingType(ASTNode node) { | |
660 | while(node != null) { | |
661 | if (node instanceof AbstractTypeDeclaration) { | |
662 | return ((AbstractTypeDeclaration)node).resolveBinding(); | |
663 | } else if (node instanceof AnonymousClassDeclaration) { | |
664 | return ((AnonymousClassDeclaration)node).resolveBinding(); | |
665 | } | |
666 | node= node.getParent(); | |
667 | } | |
668 | return null; | |
669 | } | |
670 | ||
671 | public static IProblem[] getProblems(ASTNode node, int scope, int severity) { | |
672 | ASTNode root= node.getRoot(); | |
673 | if (!(root instanceof CompilationUnit)) | |
674 | return EMPTY_PROBLEMS; | |
675 | IProblem[] problems= ((CompilationUnit)root).getProblems(); | |
676 | if (root == node) | |
677 | return problems; | |
678 | final int iterations= computeIterations(scope); | |
679 | List<IProblem> result= new ArrayList<IProblem>(5); | |
680 | for (int i= 0; i < problems.length; i++) { | |
681 | IProblem problem= problems[i]; | |
682 | boolean consider= false; | |
683 | if ((severity & PROBLEMS) == PROBLEMS) | |
684 | consider= true; | |
685 | else if ((severity & WARNING) != 0) | |
686 | consider= problem.isWarning(); | |
687 | else if ((severity & ERROR) != 0) | |
688 | consider= problem.isError(); | |
689 | if (consider) { | |
690 | ASTNode temp= node; | |
691 | int count= iterations; | |
692 | do { | |
693 | int nodeOffset= temp.getStartPosition(); | |
694 | int problemOffset= problem.getSourceStart(); | |
695 | if (nodeOffset <= problemOffset && problemOffset < nodeOffset + temp.getLength()) { | |
696 | result.add(problem); | |
697 | count= 0; | |
698 | } else { | |
699 | count--; | |
700 | } | |
701 | } while ((temp= temp.getParent()) != null && count > 0); | |
702 | } | |
703 | } | |
704 | return result.toArray(new IProblem[result.size()]); | |
705 | } | |
706 | ||
707 | public static Message[] getMessages(ASTNode node, int flags) { | |
708 | ASTNode root= node.getRoot(); | |
709 | if (!(root instanceof CompilationUnit)) | |
710 | return EMPTY_MESSAGES; | |
711 | Message[] messages= ((CompilationUnit)root).getMessages(); | |
712 | if (root == node) | |
713 | return messages; | |
714 | final int iterations= computeIterations(flags); | |
715 | List<Message> result= new ArrayList<Message>(5); | |
716 | for (int i= 0; i < messages.length; i++) { | |
717 | Message message= messages[i]; | |
718 | ASTNode temp= node; | |
719 | int count= iterations; | |
720 | do { | |
721 | int nodeOffset= temp.getStartPosition(); | |
722 | int messageOffset= message.getStartPosition(); | |
723 | if (nodeOffset <= messageOffset && messageOffset < nodeOffset + temp.getLength()) { | |
724 | result.add(message); | |
725 | count= 0; | |
726 | } else { | |
727 | count--; | |
728 | } | |
729 | } while ((temp= temp.getParent()) != null && count > 0); | |
730 | } | |
731 | return result.toArray(new Message[result.size()]); | |
732 | } | |
733 | ||
734 | private static int computeIterations(int flags) { | |
735 | switch (flags) { | |
736 | case NODE_ONLY: | |
737 | return 1; | |
738 | case INCLUDE_ALL_PARENTS: | |
739 | return Integer.MAX_VALUE; | |
740 | case INCLUDE_FIRST_PARENT: | |
741 | return 2; | |
742 | default: | |
743 | return 1; | |
744 | } | |
745 | } | |
746 | ||
747 | ||
748 | private static int getOrderPreference(BodyDeclaration member, MembersOrderPreferenceCache store) { | |
749 | int memberType= member.getNodeType(); | |
750 | int modifiers= member.getModifiers(); | |
751 | ||
752 | switch (memberType) { | |
753 | case ASTNode.TYPE_DECLARATION: | |
754 | case ASTNode.ENUM_DECLARATION : | |
755 | case ASTNode.ANNOTATION_TYPE_DECLARATION : | |
756 | return store.getCategoryIndex(MembersOrderPreferenceCache.TYPE_INDEX) * 2; | |
757 | case ASTNode.FIELD_DECLARATION: | |
758 | if (Modifier.isStatic(modifiers)) { | |
759 | int index= store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_FIELDS_INDEX) * 2; | |
760 | if (Modifier.isFinal(modifiers)) { | |
761 | return index; // first final static, then static | |
762 | } | |
763 | return index + 1; | |
764 | } | |
765 | return store.getCategoryIndex(MembersOrderPreferenceCache.FIELDS_INDEX) * 2; | |
766 | case ASTNode.INITIALIZER: | |
767 | if (Modifier.isStatic(modifiers)) { | |
768 | return store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_INIT_INDEX) * 2; | |
769 | } | |
770 | return store.getCategoryIndex(MembersOrderPreferenceCache.INIT_INDEX) * 2; | |
771 | case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION: | |
772 | return store.getCategoryIndex(MembersOrderPreferenceCache.METHOD_INDEX) * 2; | |
773 | case ASTNode.METHOD_DECLARATION: | |
774 | if (Modifier.isStatic(modifiers)) { | |
775 | return store.getCategoryIndex(MembersOrderPreferenceCache.STATIC_METHODS_INDEX) * 2; | |
776 | } | |
777 | if (((MethodDeclaration) member).isConstructor()) { | |
778 | return store.getCategoryIndex(MembersOrderPreferenceCache.CONSTRUCTORS_INDEX) * 2; | |
779 | } | |
780 | return store.getCategoryIndex(MembersOrderPreferenceCache.METHOD_INDEX) * 2; | |
781 | default: | |
782 | return 100; | |
783 | } | |
784 | } | |
785 | ||
786 | /** | |
787 | * Computes the insertion index to be used to add the given member to the | |
788 | * the list <code>container</code>. | |
789 | * @param member the member to add | |
790 | * @param container a list containing objects of type <code>BodyDeclaration</code> | |
791 | * @return the insertion index to be used | |
792 | */ | |
793 | public static int getInsertionIndex(BodyDeclaration member, List<? extends BodyDeclaration> container) { | |
794 | int containerSize= container.size(); | |
795 | ||
796 | MembersOrderPreferenceCache orderStore= JavaPlugin.getDefault().getMemberOrderPreferenceCache(); | |
797 | ||
798 | int orderIndex= getOrderPreference(member, orderStore); | |
799 | ||
800 | int insertPos= containerSize; | |
801 | int insertPosOrderIndex= -1; | |
802 | ||
803 | for (int i= containerSize - 1; i >= 0; i--) { | |
804 | int currOrderIndex= getOrderPreference(container.get(i), orderStore); | |
805 | if (orderIndex == currOrderIndex) { | |
806 | if (insertPosOrderIndex != orderIndex) { // no perfect match yet | |
807 | insertPos= i + 1; // after a same kind | |
808 | insertPosOrderIndex= orderIndex; // perfect match | |
809 | } | |
810 | } else if (insertPosOrderIndex != orderIndex) { // not yet a perfect match | |
811 | if (currOrderIndex < orderIndex) { // we are bigger | |
812 | if (insertPosOrderIndex == -1) { | |
813 | insertPos= i + 1; // after | |
814 | insertPosOrderIndex= currOrderIndex; | |
815 | } | |
816 | } else { | |
817 | insertPos= i; // before | |
818 | insertPosOrderIndex= currOrderIndex; | |
819 | } | |
820 | } | |
821 | } | |
822 | return insertPos; | |
823 | } | |
824 | ||
825 | public static SimpleName getLeftMostSimpleName(Name name) { | |
826 | if (name instanceof SimpleName) { | |
827 | return (SimpleName)name; | |
828 | } else { | |
829 | final SimpleName[] result= new SimpleName[1]; | |
830 | ASTVisitor visitor= new ASTVisitor() { | |
831 | @Override | |
832 | public boolean visit(QualifiedName qualifiedName) { | |
833 | Name left= qualifiedName.getQualifier(); | |
834 | if (left instanceof SimpleName) | |
835 | result[0]= (SimpleName)left; | |
836 | else | |
837 | left.accept(this); | |
838 | return false; | |
839 | } | |
840 | }; | |
841 | name.accept(visitor); | |
842 | return result[0]; | |
843 | } | |
844 | } | |
845 | ||
846 | public static SimpleType getLeftMostSimpleType(QualifiedType type) { | |
847 | final SimpleType[] result= new SimpleType[1]; | |
848 | ASTVisitor visitor= new ASTVisitor() { | |
849 | @Override | |
850 | public boolean visit(QualifiedType qualifiedType) { | |
851 | Type left= qualifiedType.getQualifier(); | |
852 | if (left instanceof SimpleType) | |
853 | result[0]= (SimpleType)left; | |
854 | else | |
855 | left.accept(this); | |
856 | return false; | |
857 | } | |
858 | }; | |
859 | type.accept(visitor); | |
860 | return result[0]; | |
861 | } | |
862 | ||
863 | public static Name getTopMostName(Name name) { | |
864 | Name result= name; | |
865 | while(result.getParent() instanceof Name) { | |
866 | result= (Name)result.getParent(); | |
867 | } | |
868 | return result; | |
869 | } | |
870 | ||
871 | public static Type getTopMostType(Type type) { | |
872 | Type result= type; | |
873 | while(result.getParent() instanceof Type) { | |
874 | result= (Type)result.getParent(); | |
875 | } | |
876 | return result; | |
877 | } | |
878 | ||
879 | public static int changeVisibility(int modifiers, int visibility) { | |
880 | return (modifiers & CLEAR_VISIBILITY) | visibility; | |
881 | } | |
882 | ||
883 | /** | |
884 | * Adds flags to the given node and all its descendants. | |
885 | * @param root The root node | |
886 | * @param flags The flags to set | |
887 | */ | |
888 | public static void setFlagsToAST(ASTNode root, final int flags) { | |
889 | root.accept(new GenericVisitor(true) { | |
890 | @Override | |
891 | protected boolean visitNode(ASTNode node) { | |
892 | node.setFlags(node.getFlags() | flags); | |
893 | return true; | |
894 | } | |
895 | }); | |
896 | } | |
897 | ||
898 | public static String getQualifier(Name name) { | |
899 | if (name.isQualifiedName()) { | |
900 | return ((QualifiedName) name).getQualifier().getFullyQualifiedName(); | |
901 | } | |
902 | return ""; //$NON-NLS-1$ | |
903 | } | |
904 | ||
905 | public static String getSimpleNameIdentifier(Name name) { | |
906 | if (name.isQualifiedName()) { | |
907 | return ((QualifiedName) name).getName().getIdentifier(); | |
908 | } else { | |
909 | return ((SimpleName) name).getIdentifier(); | |
910 | } | |
911 | } | |
912 | ||
913 | public static boolean isDeclaration(Name name) { | |
914 | if (name.isQualifiedName()) { | |
915 | return ((QualifiedName) name).getName().isDeclaration(); | |
916 | } else { | |
917 | return ((SimpleName) name).isDeclaration(); | |
918 | } | |
919 | } | |
920 | ||
921 | public static Modifier findModifierNode(int flag, List<IExtendedModifier> modifiers) { | |
922 | for (int i= 0; i < modifiers.size(); i++) { | |
923 | Object curr= modifiers.get(i); | |
924 | if (curr instanceof Modifier && ((Modifier) curr).getKeyword().toFlagValue() == flag) { | |
925 | return (Modifier) curr; | |
926 | } | |
927 | } | |
928 | return null; | |
929 | } | |
930 | ||
931 | public static ITypeBinding getTypeBinding(CompilationUnit root, IType type) throws JavaModelException { | |
932 | if (type.isAnonymous()) { | |
933 | final IJavaElement parent= type.getParent(); | |
934 | if (parent instanceof IField && Flags.isEnum(((IMember) parent).getFlags())) { | |
935 | final EnumConstantDeclaration constant= (EnumConstantDeclaration) NodeFinder.perform(root, ((ISourceReference) parent).getSourceRange()); | |
936 | if (constant != null) { | |
937 | final AnonymousClassDeclaration declaration= constant.getAnonymousClassDeclaration(); | |
938 | if (declaration != null) | |
939 | return declaration.resolveBinding(); | |
940 | } | |
941 | } else { | |
942 | final ClassInstanceCreation creation= (ClassInstanceCreation) getParent(NodeFinder.perform(root, type.getNameRange()), ClassInstanceCreation.class); | |
943 | if (creation != null) | |
944 | return creation.resolveTypeBinding(); | |
945 | } | |
946 | } else { | |
947 | final AbstractTypeDeclaration declaration= (AbstractTypeDeclaration) getParent(NodeFinder.perform(root, type.getNameRange()), AbstractTypeDeclaration.class); | |
948 | if (declaration != null) | |
949 | return declaration.resolveBinding(); | |
950 | } | |
951 | return null; | |
952 | } | |
953 | ||
954 | /** | |
955 | * Escapes a string value to a literal that can be used in Java source. | |
956 | * | |
957 | * @param stringValue the string value | |
958 | * @return the escaped string | |
959 | * @see StringLiteral#getEscapedValue() | |
960 | */ | |
961 | public static String getEscapedStringLiteral(String stringValue) { | |
962 | StringLiteral stringLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newStringLiteral(); | |
963 | stringLiteral.setLiteralValue(stringValue); | |
964 | return stringLiteral.getEscapedValue(); | |
965 | } | |
966 | ||
967 | /** | |
968 | * Escapes a character value to a literal that can be used in Java source. | |
969 | * | |
970 | * @param ch the character value | |
971 | * @return the escaped string | |
972 | * @see CharacterLiteral#getEscapedValue() | |
973 | */ | |
974 | public static String getEscapedCharacterLiteral(char ch) { | |
975 | CharacterLiteral characterLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newCharacterLiteral(); | |
976 | characterLiteral.setCharValue(ch); | |
977 | return characterLiteral.getEscapedValue(); | |
978 | } | |
979 | ||
980 | } |