1 19 20 package org.netbeans.modules.java.hints; 21 22 import com.sun.source.tree.AssignmentTree; 23 import com.sun.source.tree.BlockTree; 24 import com.sun.source.tree.ExpressionStatementTree; 25 import com.sun.source.tree.ExpressionTree; 26 import com.sun.source.tree.IdentifierTree; 27 import com.sun.source.tree.MethodTree; 28 import com.sun.source.tree.StatementTree; 29 import com.sun.source.tree.Tree; 30 import com.sun.source.tree.Tree.Kind; 31 import com.sun.source.tree.VariableTree; 32 import com.sun.source.util.TreePath; 33 import com.sun.source.util.TreePathScanner; 34 import java.io.IOException ; 35 import java.util.Arrays ; 36 import java.util.EnumSet ; 37 import java.util.EnumSet ; 38 import java.util.logging.Level ; 39 import java.util.logging.Logger ; 40 import javax.lang.model.element.Element; 41 import javax.lang.model.element.Modifier; 42 import javax.lang.model.element.Modifier; 43 import javax.lang.model.type.DeclaredType; 44 import javax.lang.model.type.TypeKind; 45 import javax.lang.model.type.TypeMirror; 46 import org.netbeans.api.java.source.CancellableTask; 47 import org.netbeans.api.java.source.CompilationInfo; 48 import org.netbeans.api.java.source.JavaSource; 49 import org.netbeans.api.java.source.JavaSource.Phase; 50 import org.netbeans.api.java.source.TreeMaker; 51 import org.netbeans.api.java.source.TypeMirrorHandle; 52 import org.netbeans.api.java.source.WorkingCopy; 53 import org.netbeans.spi.editor.hints.ChangeInfo; 54 import org.netbeans.spi.editor.hints.Fix; 55 import org.openide.filesystems.FileObject; 56 57 61 public class AddParameterOrLocalFix implements Fix { 62 63 private FileObject file; 64 private TypeMirrorHandle type; 65 private String name; 66 private boolean parameter; 67 68 private int unresolvedVariable; 69 70 public AddParameterOrLocalFix(CompilationInfo info, 71 TypeMirror type, String name, 72 boolean parameter, 73 int unresolvedVariable) { 74 this.file = info.getFileObject(); 75 if (type.getKind() == TypeKind.NULL) { 76 type = info.getElements().getTypeElement("java.lang.Object").asType(); 77 } 78 this.type = TypeMirrorHandle.create(type); 79 this.name = name; 80 this.parameter = parameter; 81 this.unresolvedVariable = unresolvedVariable; 82 } 83 84 public String getText() { 85 return parameter ? "Create parameter " + name : "Create local variable " + name; 86 } 87 88 public ChangeInfo implement() { 89 try { 90 JavaSource js = JavaSource.forFileObject(file); 92 93 js.runModificationTask(new CancellableTask<WorkingCopy>() { 94 public void cancel() { 95 } 96 public void run(final WorkingCopy working) throws IOException { 97 working.toPhase(Phase.RESOLVED); 98 99 TypeMirror proposedType = type.resolve(working); 100 101 if (proposedType == null) { 102 JavaHintsProvider.LOG.log(Level.INFO, "Cannot resolve proposed type."); 103 return; 104 } 105 106 TreeMaker make = working.getTreeMaker(); 107 TreePath tp = working.getTreeUtilities().pathFor(unresolvedVariable + 1); 108 109 assert tp.getLeaf().getKind() == Kind.IDENTIFIER; 110 111 MethodTree targetTree = findMethod(tp); 112 113 if (parameter) { 114 if (targetTree == null) { 115 Logger.getLogger("global").log(Level.WARNING, "Add parameter - cannot find the method."); 116 } 117 118 MethodTree result = make.addMethodParameter(targetTree, make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(proposedType), null)); 119 120 working.rewrite(targetTree, result); 121 } else { 122 resolveLocalVariable(working, tp, make, proposedType); 123 } 124 } 125 }).commit(); 126 } catch (IOException e) { 127 throw (IllegalStateException ) new IllegalStateException ().initCause(e); 128 } 129 130 return null; 131 } 132 133 private void resolveLocalVariable(final WorkingCopy wc, TreePath tp, TreeMaker make, TypeMirror proposedType) { 134 final String name = ((IdentifierTree) tp.getLeaf()).getName().toString(); 135 136 final Element el = wc.getTrees().getElement(tp); 137 138 TreePath method = tp; 140 141 while (method.getLeaf().getKind() != Kind.COMPILATION_UNIT) { 142 if (method.getLeaf().getKind() == Kind.METHOD) { 143 break; 144 } 145 146 method = method.getParentPath(); 147 } 148 149 if (method.getLeaf().getKind() != Kind.METHOD) { 150 return; 152 } 153 154 class FirstUsage extends TreePathScanner<TreePath, Void > { 155 private TreePath found; 156 public @Override TreePath visitIdentifier(IdentifierTree tree, Void v) { 157 if (tree.getName().contentEquals(el.getSimpleName())) { 158 if (found == null) { 159 found = getCurrentPath(); 160 } 161 return findStatement(getCurrentPath()); 162 } 163 return null; 164 } 165 public @Override TreePath visitBlock(BlockTree tree, Void v) { 166 TreePath result = null; 167 TreePath firstBranchStatementWithUsage = null; 168 for (StatementTree t : tree.getStatements()) { 169 TreePath currentResult = scan(t, null); 170 171 if (currentResult != null && result == null) { 172 result = currentResult; 173 firstBranchStatementWithUsage = new TreePath(getCurrentPath(), t); 174 } 175 176 if (currentResult != t && result != null && result.getLeaf() != firstBranchStatementWithUsage.getLeaf()) { 177 result = firstBranchStatementWithUsage; 179 } 180 } 181 super.visitBlock(tree, v); 182 return result; 183 } 184 public @Override TreePath reduce(TreePath tp1, TreePath tp2) { 185 if (tp2 == null) 186 return tp1; 187 188 return tp2; 189 190 } 191 } 192 193 FirstUsage firstUsage = new FirstUsage(); 194 TreePath firstUse = firstUsage.scan(method, null); 195 196 if (firstUse == null || !isStatement(firstUse.getLeaf())) { 197 Logger.getLogger("global").log(Level.WARNING, "Add local variable - cannot find a statement."); 198 return; 199 } 200 201 StatementTree statement = (StatementTree) firstUse.getLeaf(); 202 203 if (statement.getKind() == Kind.EXPRESSION_STATEMENT) { 204 ExpressionTree exp = ((ExpressionStatementTree) statement).getExpression(); 205 206 if (exp.getKind() == Kind.ASSIGNMENT) { 207 AssignmentTree at = (AssignmentTree) exp; 209 VariableTree vt = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(proposedType), at.getExpression()); 210 211 wc.rewrite(statement, vt); 212 213 return; 214 } 215 } 216 217 Tree statementParent = firstUse.getParentPath().getLeaf(); 218 VariableTree vt = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), name, make.Type(proposedType), null); 219 220 if (statementParent.getKind() == Kind.BLOCK) { 221 BlockTree block = (BlockTree) statementParent; 222 BlockTree nueBlock = make.insertBlockStatement(block, block.getStatements().indexOf(statement), vt); 223 224 wc.rewrite(block, nueBlock); 225 } else { 226 BlockTree block = make.Block(Arrays.asList(vt, statement), false); 227 228 wc.rewrite(statement, block); 229 } 230 } 231 232 private TreePath findStatement(TreePath tp) { 233 TreePath statement = tp; 234 235 while (statement.getLeaf().getKind() != Kind.COMPILATION_UNIT) { 236 if (isStatement(statement.getLeaf())) { 237 return statement; 238 } 239 240 statement = statement.getParentPath(); 241 } 242 243 return null; 244 } 245 246 private MethodTree findMethod(TreePath tp) { 247 TreePath method = tp; 248 249 while (method.getLeaf().getKind() != Kind.COMPILATION_UNIT) { 250 if (method.getLeaf().getKind() == Kind.METHOD) { 251 return (MethodTree) method.getLeaf(); 252 } 253 254 method = method.getParentPath(); 255 } 256 257 return null; 258 } 259 260 private boolean isStatement(Tree t) { 261 Class intClass = t.getKind().asInterface(); 262 263 return StatementTree.class.isAssignableFrom(intClass); 264 } 265 266 String toDebugString(CompilationInfo info) { 267 return "AddParameterOrLocalFix:" + name + ":" + type.resolve(info).toString() + ":" + parameter; 268 } 269 } 270 | Popular Tags |