1 19 20 package org.netbeans.modules.java.hints; 21 22 import com.sun.source.tree.BlockTree; 23 import com.sun.source.tree.CatchTree; 24 import com.sun.source.tree.ExpressionTree; 25 import com.sun.source.tree.StatementTree; 26 import com.sun.source.tree.Tree.Kind; 27 import com.sun.source.tree.TryTree; 28 import com.sun.source.tree.VariableTree; 29 import com.sun.source.util.TreePath; 30 import com.sun.source.util.TreeScanner; 31 import java.io.IOException ; 32 import java.util.ArrayList ; 33 import java.util.Arrays ; 34 import java.util.Collections ; 35 import java.util.EnumSet ; 36 import java.util.List ; 37 import javax.lang.model.element.Element; 38 import javax.lang.model.element.Modifier; 39 import javax.lang.model.element.TypeElement; 40 import javax.lang.model.type.TypeMirror; 41 import org.netbeans.api.java.source.CancellableTask; 42 import org.netbeans.api.java.source.CompilationInfo; 43 import org.netbeans.api.java.source.JavaSource; 44 import org.netbeans.api.java.source.JavaSource.Phase; 45 import org.netbeans.api.java.source.TreeMaker; 46 import org.netbeans.api.java.source.TypeMirrorHandle; 47 import org.netbeans.api.java.source.WorkingCopy; 48 import org.netbeans.modules.java.editor.codegen.GeneratorUtils; 49 import org.netbeans.spi.editor.hints.ChangeInfo; 50 import org.netbeans.spi.editor.hints.Fix; 51 import org.openide.ErrorManager; 52 53 54 58 final class MagicSurroundWithTryCatch implements Fix { 59 60 private JavaSource js; 61 private List <TypeMirrorHandle> thandles; 62 private int offset; 63 64 public MagicSurroundWithTryCatch(JavaSource js, List <TypeMirrorHandle> thandles, int offset) { 65 this.js = js; 66 this.thandles = thandles; 67 this.offset = offset; 68 } 69 70 public String getText() { 71 return "Surround with try-catch"; 72 } 73 74 private static final String [] STREAM_ALIKE_CLASSES = new String [] { 75 "java.io.InputStream", 76 "java.io.OutputStream", 77 "java.io.Reader", 78 "java.io.Writer", 79 }; 80 81 private boolean isStreamAlike(CompilationInfo info, TypeMirror type) { 82 for (String fqn : STREAM_ALIKE_CLASSES) { 83 Element inputStream = info.getElements().getTypeElement(fqn); 84 85 if (info.getTypes().isAssignable(type, inputStream.asType())) 86 return true; 87 } 88 89 return false; 90 } 91 92 public ChangeInfo implement() { 93 try { 94 js.runModificationTask(new CancellableTask<WorkingCopy>() { 95 public void cancel() { 96 } 97 public void run(WorkingCopy wc) throws Exception { 98 wc.toPhase(Phase.RESOLVED); 99 TreePath currentPath = wc.getTreeUtilities().pathFor(offset + 1); 100 101 while (currentPath != null && !UncaughtExceptionCreator.STATEMENT_KINDS.contains(currentPath.getLeaf().getKind())) 103 currentPath = currentPath.getParentPath(); 104 105 TreePath statement = currentPath; 107 boolean streamAlike = false; 108 109 if (statement.getLeaf().getKind() == Kind.VARIABLE) { 110 Element curType = wc.getTrees().getElement(statement); 112 113 streamAlike = isStreamAlike(wc, curType.asType()); 114 } 115 116 TreePath catchTree = currentPath; 118 119 while (catchTree != null 120 && catchTree.getLeaf().getKind() != Kind.TRY 121 && catchTree.getLeaf().getKind() != Kind.CLASS 122 && catchTree.getLeaf().getKind() != Kind.CATCH) 123 catchTree = catchTree.getParentPath(); 124 125 if (catchTree.getLeaf().getKind() == Kind.TRY) { 126 new TransformerImpl(wc, thandles, streamAlike, statement).scan(catchTree.getLeaf(), null); 128 } else { 129 TreePath blockTree = currentPath; 131 132 while (blockTree != null 133 && blockTree.getLeaf().getKind() != Kind.BLOCK) 134 blockTree = blockTree.getParentPath(); 135 136 new TransformerImpl(wc, thandles, streamAlike, statement).scan(blockTree.getLeaf(), null); 137 } 138 } 139 }).commit(); 140 } catch (IOException e) { 141 ErrorManager.getDefault().notify(e); 142 } 143 return null; 144 } 145 146 private final class TransformerImpl extends TreeScanner<Void , Void > { 147 148 private WorkingCopy info; 149 private List <TypeMirrorHandle> thandles; 150 private boolean streamAlike; 151 private TreePath statement; 152 private TreeMaker make; 153 154 public TransformerImpl(WorkingCopy info, List <TypeMirrorHandle> thandles, boolean streamAlike, TreePath statement) { 155 this.info = info; 156 this.thandles = thandles; 157 this.streamAlike = streamAlike; 158 this.statement = statement; 159 this.make = info.getTreeMaker(); 160 } 161 162 private StatementTree createExceptionsStatement() { 163 TypeElement exceptions = info.getElements().getTypeElement("org.openide.util.Exceptions"); 164 165 if (exceptions == null) { 166 return null; 167 } 168 169 return make.ExpressionStatement(make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.QualIdent(exceptions), "printStackTrace"), Arrays.asList(make.Identifier("ex")))); 170 } 171 172 private StatementTree createLogStatement() { 173 if (!GeneratorUtils.supportsOverride(info.getFileObject())) 174 return null; 175 176 TypeElement logger = info.getElements().getTypeElement("java.util.logging.Logger"); 177 TypeElement level = info.getElements().getTypeElement("java.util.logging.Level"); 178 179 if (logger == null || level == null) { 180 return null; 181 } 182 183 ExpressionTree etExpression = make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.QualIdent(logger), "getLogger"), Collections.singletonList(make.Literal("global"))); 184 ExpressionTree levelExpression = make.MemberSelect(make.QualIdent(level), "SEVERE"); 185 186 return make.ExpressionStatement(make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(etExpression, "log"), Arrays.asList(levelExpression, make.Literal(null), make.Identifier("ex")))); 187 } 188 189 private StatementTree createPrintStackTraceStatement() { 190 return make.ExpressionStatement(make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier("ex"), "printStackTrace"), Collections.<ExpressionTree>emptyList())); 191 } 192 193 private CatchTree createCatch(TypeMirror type) { 194 StatementTree logStatement = createExceptionsStatement(); 195 196 if (logStatement == null) { 197 logStatement = createLogStatement(); 198 } 199 200 if (logStatement == null) { 201 logStatement = createPrintStackTraceStatement(); 202 } 203 204 return make.Catch(make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), "ex", make.Type(type), null), make.Block(Collections.singletonList(logStatement), false)); 205 } 206 207 private List <CatchTree> createCatches() { 208 List <CatchTree> catches = new ArrayList <CatchTree>(); 209 210 for (TypeMirrorHandle th : thandles) { 211 catches.add(createCatch(th.resolve(info))); 212 } 213 214 return catches; 215 } 216 217 public @Override Void visitTry(TryTree tt, Void p) { 218 List <CatchTree> catches = createCatches(); 219 220 catches.addAll(tt.getCatches()); 221 222 if (!streamAlike) { 223 info.rewrite(tt, make.Try(tt.getBlock(), catches, tt.getFinallyBlock())); 224 } else { 225 VariableTree originalDeclaration = (VariableTree) statement.getLeaf(); 226 VariableTree declaration = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), originalDeclaration.getName(), originalDeclaration.getType(), make.Literal(null)); 227 StatementTree assignment = make.ExpressionStatement(make.Assignment(make.Identifier(originalDeclaration.getName()), originalDeclaration.getInitializer())); 228 List <StatementTree> finallyStatements = new ArrayList <StatementTree>(tt.getFinallyBlock() != null ? tt.getFinallyBlock().getStatements() : Collections.<StatementTree>emptyList()); 229 230 finallyStatements.add(createFinallyCloseBlockStatement(originalDeclaration.getName())); 231 232 BlockTree finallyTree = make.Block(finallyStatements, false); 233 234 info.rewrite(originalDeclaration, assignment); 235 236 TryTree nueTry = make.Try(tt.getBlock(), catches, finallyTree); 237 238 TreePath currentBlockCandidate = statement; 239 240 while (currentBlockCandidate.getLeaf() != tt) { 241 currentBlockCandidate = currentBlockCandidate.getParentPath(); 242 } 243 244 if (currentBlockCandidate.getLeaf().getKind() == Kind.BLOCK) { 245 BlockTree originalTree = (BlockTree) currentBlockCandidate.getLeaf(); 246 List <StatementTree> statements = new ArrayList <StatementTree>(originalTree.getStatements()); 247 int index = statements.indexOf(tt); 248 249 statements.remove(index); 250 statements.add(index, nueTry); 251 statements.add(index, declaration); 252 info.rewrite(originalTree, make.Block(statements, false)); 253 } else { 254 BlockTree nueBlock = make.Block(Arrays.asList(declaration, nueTry), false); 255 256 info.rewrite(tt, nueBlock); 257 } 258 } 259 260 return null; 261 } 262 263 private StatementTree createFinallyCloseBlockStatement(CharSequence name) { 264 StatementTree close = make.ExpressionStatement(make.MethodInvocation(Collections.<ExpressionTree>emptyList(), make.MemberSelect(make.Identifier(name), "close"), Collections.<ExpressionTree>emptyList())); 265 StatementTree tryStatement = make.Try(make.Block(Collections.singletonList(close), false), Collections.singletonList(createCatch(info.getElements().getTypeElement("java.io.IOException").asType())), null); 266 267 return tryStatement; 268 } 269 270 public @Override Void visitBlock(BlockTree bt, Void p) { 271 List <CatchTree> catches = createCatches(); 272 273 if (!streamAlike) { 274 info.rewrite(bt, make.Block(Collections.singletonList(make.Try(bt, catches, null)), false)); 275 } else { 276 VariableTree originalDeclaration = (VariableTree) statement.getLeaf(); 277 VariableTree declaration = make.Variable(make.Modifiers(EnumSet.noneOf(Modifier.class)), originalDeclaration.getName(), originalDeclaration.getType(), make.Identifier("null")); 278 StatementTree assignment = make.ExpressionStatement(make.Assignment(make.Identifier(originalDeclaration.getName()), originalDeclaration.getInitializer())); 279 BlockTree finallyTree = make.Block(Collections.singletonList(createFinallyCloseBlockStatement(originalDeclaration.getName())), false); 280 281 info.rewrite(originalDeclaration, assignment); 282 info.rewrite(bt, make.Block(Arrays.asList(declaration, make.Try(bt, catches, finallyTree)), false)); 283 } 284 285 return null; 286 } 287 288 } 289 290 } 291 | Popular Tags |