001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.java.hints.errors;
043:
044: import com.sun.source.tree.ArrayAccessTree;
045: import com.sun.source.tree.AssignmentTree;
046: import com.sun.source.tree.BinaryTree;
047: import com.sun.source.tree.ConditionalExpressionTree;
048: import com.sun.source.tree.ExpressionTree;
049: import com.sun.source.tree.IdentifierTree;
050: import com.sun.source.tree.LiteralTree;
051: import com.sun.source.tree.MemberSelectTree;
052: import com.sun.source.tree.MethodInvocationTree;
053: import com.sun.source.tree.MethodTree;
054: import com.sun.source.tree.NewClassTree;
055: import com.sun.source.tree.ParameterizedTypeTree;
056: import com.sun.source.tree.ReturnTree;
057: import com.sun.source.tree.Scope;
058: import com.sun.source.tree.Tree;
059: import com.sun.source.tree.Tree.Kind;
060: import com.sun.source.tree.VariableTree;
061: import com.sun.source.util.TreePath;
062: import com.sun.source.util.TreeScanner;
063: import java.util.ArrayList;
064: import java.util.Arrays;
065: import java.util.Collections;
066: import java.util.HashMap;
067: import java.util.HashSet;
068: import java.util.Iterator;
069: import java.util.LinkedList;
070: import java.util.List;
071: import java.util.Map;
072: import java.util.Set;
073: import javax.lang.model.element.ExecutableElement;
074: import javax.lang.model.element.TypeElement;
075: import javax.lang.model.type.DeclaredType;
076: import javax.lang.model.type.ExecutableType;
077: import javax.lang.model.type.TypeKind;
078: import javax.lang.model.type.TypeMirror;
079: import javax.lang.model.util.ElementFilter;
080: import org.netbeans.api.java.source.CompilationInfo;
081: import org.netbeans.modules.editor.java.Utilities;
082: import org.netbeans.modules.java.hints.spi.ErrorRule;
083: import org.netbeans.spi.editor.hints.Fix;
084: import org.openide.util.NbBundle;
085:
086: import static com.sun.source.tree.Tree.Kind.*;
087:
088: /**
089: *
090: * @author Jan Lahoda
091: */
092: public final class AddCast implements ErrorRule<Void> {
093:
094: static void computeType(CompilationInfo info, int offset,
095: TypeMirror[] tm, ExpressionTree[] expression, Tree[] leaf) {
096: TreePath path = info.getTreeUtilities().pathFor(offset + 1);
097:
098: //TODO: this does not seem nice:
099: while (path != null) {
100: Tree scope = path.getLeaf();
101: TypeMirror expected = null;
102: TypeMirror resolved = null;
103: ExpressionTree found = null;
104:
105: if (scope.getKind() == Kind.VARIABLE
106: && ((VariableTree) scope).getInitializer() != null) {
107: expected = info.getTrees().getTypeMirror(path);
108: found = ((VariableTree) scope).getInitializer();
109: resolved = info.getTrees().getTypeMirror(
110: new TreePath(path, found));
111: }
112:
113: if (scope.getKind() == Kind.ASSIGNMENT) {
114: expected = info.getTrees().getTypeMirror(path);
115: found = ((AssignmentTree) scope).getExpression();
116: resolved = info.getTrees().getTypeMirror(
117: new TreePath(path, found));
118: }
119:
120: if (scope.getKind() == Kind.RETURN) {
121: TreePath parents = path;
122:
123: while (parents != null
124: && parents.getLeaf().getKind() != Kind.METHOD)
125: parents = parents.getParentPath();
126:
127: if (parents != null) {
128: expected = info.getTrees().getTypeMirror(
129: new TreePath(parents, ((MethodTree) parents
130: .getLeaf()).getReturnType()));
131: found = ((ReturnTree) scope).getExpression();
132: resolved = info.getTrees().getTypeMirror(
133: new TreePath(path, found));
134: }
135: }
136:
137: if (scope.getKind() == Kind.METHOD_INVOCATION
138: || scope.getKind() == Kind.NEW_CLASS) {
139: TypeMirror[] proposed = new TypeMirror[1];
140: int[] index = new int[1];
141:
142: if (Utilities.fuzzyResolveMethodInvocation(info, path,
143: proposed, index) != null) {
144: expected = proposed[0];
145: found = scope.getKind() == Kind.METHOD_INVOCATION ? ((MethodInvocationTree) scope)
146: .getArguments().get(index[0])
147: : ((NewClassTree) scope).getArguments()
148: .get(index[0]);
149: resolved = info.getTrees().getTypeMirror(
150: new TreePath(path, found));
151: }
152: }
153:
154: if (expected != null && resolved != null) {
155: TypeMirror foundTM = info.getTrees().getTypeMirror(
156: new TreePath(path, found));
157:
158: if (foundTM.getKind() == TypeKind.EXECUTABLE) {
159: //XXX: ignoring executable, see AddCast9 for more information when this happens.
160: } else {
161: if (!info.getTypes()
162: .isAssignable(foundTM, expected)
163: && info.getTypeUtilities().isCastable(
164: resolved, expected)
165: /*#85346: cast hint should not be proposed for error types:*/
166: && foundTM.getKind() != TypeKind.ERROR
167: && expected.getKind() != TypeKind.ERROR) {
168: tm[0] = expected;
169: expression[0] = found;
170: leaf[0] = scope;
171: }
172: }
173: }
174:
175: path = path.getParentPath();
176: }
177: }
178:
179: public Set<String> getCodes() {
180: return new HashSet<String>(Arrays.asList(
181: "compiler.err.prob.found.req",
182: "compiler.err.cant.apply.symbol",
183: "compiler.err.cant.resolve.location")); // NOI18N
184: }
185:
186: public List<Fix> run(CompilationInfo info, String diagnosticKey,
187: int offset, TreePath treePath, Data<Void> data) {
188: List<Fix> result = new ArrayList<Fix>();
189: TypeMirror[] tm = new TypeMirror[1];
190: ExpressionTree[] expression = new ExpressionTree[1];
191: Tree[] leaf = new Tree[1];
192:
193: computeType(info, offset, tm, expression, leaf);
194:
195: if (tm[0] != null) {
196: int position = (int) info.getTrees().getSourcePositions()
197: .getStartPosition(info.getCompilationUnit(),
198: expression[0]);
199: Class interf = expression[0].getKind().asInterface();
200: boolean wrapWithBrackets = interf == BinaryTree.class
201: || interf == ConditionalExpressionTree.class;
202: result.add(new AddCastFix(info.getJavaSource(),
203: new HintDisplayNameVisitor(info).scan(
204: expression[0], null), Utilities
205: .getTypeName(tm[0], false).toString(),
206: position, wrapWithBrackets));
207: }
208:
209: return result;
210: }
211:
212: public void cancel() {
213: //XXX: not done yet
214: }
215:
216: public String getId() {
217: return AddCast.class.getName();
218: }
219:
220: public String getDisplayName() {
221: return NbBundle.getMessage(AddCast.class, "LBL_Add_Cast");
222: }
223:
224: public String getDescription() {
225: return NbBundle.getMessage(AddCast.class, "DSC_Add_Cast");
226: }
227:
228: private static final Map<Kind, String> operator2DN;
229:
230: static {
231: operator2DN = new HashMap<Kind, String>();
232:
233: operator2DN.put(AND, "&");
234: operator2DN.put(XOR, "^");
235: operator2DN.put(OR, "|");
236: operator2DN.put(CONDITIONAL_AND, "&&");
237: operator2DN.put(CONDITIONAL_OR, "||");
238: operator2DN.put(MULTIPLY_ASSIGNMENT, "*=");
239: operator2DN.put(DIVIDE_ASSIGNMENT, "/=");
240: operator2DN.put(REMAINDER_ASSIGNMENT, "%=");
241: operator2DN.put(PLUS_ASSIGNMENT, "+=");
242: operator2DN.put(MINUS_ASSIGNMENT, "-=");
243: operator2DN.put(LEFT_SHIFT_ASSIGNMENT, "<<=");
244: operator2DN.put(RIGHT_SHIFT_ASSIGNMENT, ">>=");
245: operator2DN.put(UNSIGNED_RIGHT_SHIFT_ASSIGNMENT, ">>>=");
246: operator2DN.put(AND_ASSIGNMENT, "&=");
247: operator2DN.put(XOR_ASSIGNMENT, "^=");
248: operator2DN.put(OR_ASSIGNMENT, "|=");
249: operator2DN.put(BITWISE_COMPLEMENT, "~");
250: operator2DN.put(LOGICAL_COMPLEMENT, "!");
251: operator2DN.put(MULTIPLY, "*");
252: operator2DN.put(DIVIDE, "/");
253: operator2DN.put(REMAINDER, "%");
254: operator2DN.put(PLUS, "+");
255: operator2DN.put(MINUS, "-");
256: operator2DN.put(LEFT_SHIFT, "<<");
257: operator2DN.put(RIGHT_SHIFT, ">>");
258: operator2DN.put(UNSIGNED_RIGHT_SHIFT, ">>>");
259: operator2DN.put(LESS_THAN, "<");
260: operator2DN.put(GREATER_THAN, ">");
261: operator2DN.put(LESS_THAN_EQUAL, "<=");
262: operator2DN.put(GREATER_THAN_EQUAL, ">=");
263: operator2DN.put(EQUAL_TO, "==");
264: operator2DN.put(NOT_EQUAL_TO, "!=");
265: }
266:
267: private static class HintDisplayNameVisitor extends
268: TreeScanner<String, Void> {
269:
270: private CompilationInfo info;
271:
272: public HintDisplayNameVisitor(CompilationInfo info) {
273: this .info = info;
274: }
275:
276: public @Override
277: String visitIdentifier(IdentifierTree tree, Void v) {
278: return "..." + tree.getName().toString();
279: }
280:
281: public @Override
282: String visitMethodInvocation(MethodInvocationTree tree, Void v) {
283: ExpressionTree methodSelect = tree.getMethodSelect();
284:
285: return "..." + simpleName(methodSelect) + "(...)"; // NOI18N
286: }
287:
288: public @Override
289: String visitArrayAccess(ArrayAccessTree node, Void p) {
290: return "..." + simpleName(node.getExpression()) + "[]"; // NOI18N
291: }
292:
293: public @Override
294: String visitNewClass(NewClassTree nct, Void p) {
295: return "...new " + simpleName(nct.getIdentifier())
296: + "(...)"; // NOI18N
297: }
298:
299: @Override
300: public String visitBinary(BinaryTree node, Void p) {
301: String dn = operator2DN.get(node.getKind());
302:
303: return scan(node.getLeftOperand(), p) + dn
304: + scan(node.getRightOperand(), p);
305: }
306:
307: @Override
308: public String visitLiteral(LiteralTree node, Void p) {
309: if (node.getValue() instanceof String)
310: return "...";
311:
312: int start = (int) info.getTrees().getSourcePositions()
313: .getStartPosition(info.getCompilationUnit(), node);
314: int end = (int) info.getTrees().getSourcePositions()
315: .getEndPosition(info.getCompilationUnit(), node);
316:
317: return info.getText().substring(start, end);
318: }
319:
320: private String simpleName(Tree t) {
321: if (t.getKind() == Kind.IDENTIFIER) {
322: return ((IdentifierTree) t).getName().toString();
323: }
324:
325: if (t.getKind() == Kind.MEMBER_SELECT) {
326: return ((MemberSelectTree) t).getIdentifier()
327: .toString();
328: }
329:
330: if (t.getKind() == Kind.METHOD_INVOCATION) {
331: return scan(t, null);
332: }
333:
334: if (t.getKind() == Kind.PARAMETERIZED_TYPE) {
335: return simpleName(((ParameterizedTypeTree) t).getType())
336: + "<...>"; // NOI18N
337: }
338:
339: if (t.getKind() == Kind.ARRAY_ACCESS) {
340: return simpleName(((ArrayAccessTree) t).getExpression())
341: + "[]"; //NOI18N
342: }
343:
344: throw new IllegalStateException(
345: "Currently unsupported kind of tree: "
346: + t.getKind()); // NOI18N
347: }
348: }
349:
350: }
|