| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.netbeans.spi.java.hints; |
| |
| import com.sun.source.doctree.DocCommentTree; |
| import com.sun.source.doctree.DocTree; |
| import com.sun.source.doctree.SinceTree; |
| import com.sun.source.tree.AnnotationTree; |
| import com.sun.source.tree.AssignmentTree; |
| import com.sun.source.tree.BinaryTree; |
| import com.sun.source.tree.BlockTree; |
| import com.sun.source.tree.CaseTree; |
| import com.sun.source.tree.CatchTree; |
| import com.sun.source.tree.ClassTree; |
| import com.sun.source.tree.CompilationUnitTree; |
| import com.sun.source.tree.CompoundAssignmentTree; |
| import com.sun.source.tree.DoWhileLoopTree; |
| import com.sun.source.tree.ExpressionStatementTree; |
| import com.sun.source.tree.ExpressionTree; |
| import com.sun.source.tree.IdentifierTree; |
| import com.sun.source.tree.IfTree; |
| import com.sun.source.tree.LambdaExpressionTree; |
| import com.sun.source.tree.LiteralTree; |
| import com.sun.source.tree.MemberSelectTree; |
| import com.sun.source.tree.MethodInvocationTree; |
| import com.sun.source.tree.MethodTree; |
| import com.sun.source.tree.ModifiersTree; |
| import com.sun.source.tree.NewArrayTree; |
| import com.sun.source.tree.NewClassTree; |
| import com.sun.source.tree.ParameterizedTypeTree; |
| import com.sun.source.tree.ParenthesizedTree; |
| import com.sun.source.tree.Scope; |
| import com.sun.source.tree.StatementTree; |
| import com.sun.source.tree.SwitchTree; |
| import com.sun.source.tree.Tree; |
| import com.sun.source.tree.Tree.Kind; |
| import com.sun.source.tree.TryTree; |
| import com.sun.source.tree.TypeParameterTree; |
| import com.sun.source.tree.UnaryTree; |
| import com.sun.source.tree.UnionTypeTree; |
| import com.sun.source.tree.VariableTree; |
| import com.sun.source.tree.WhileLoopTree; |
| import com.sun.source.util.SourcePositions; |
| import com.sun.source.util.TreePath; |
| import com.sun.source.util.TreeScanner; |
| import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner; |
| import org.netbeans.api.java.source.support.ErrorAwareTreeScanner; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EnumMap; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.IdentityHashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.concurrent.Callable; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| import java.util.regex.Matcher; |
| import javax.lang.model.element.Element; |
| import javax.lang.model.element.ElementKind; |
| import javax.lang.model.element.Modifier; |
| import javax.lang.model.element.TypeElement; |
| import javax.lang.model.type.TypeKind; |
| import javax.lang.model.type.TypeMirror; |
| import org.netbeans.api.annotations.common.NonNull; |
| import org.netbeans.api.java.classpath.ClassPath; |
| import org.netbeans.api.java.classpath.ClassPath.PathConversionMode; |
| import org.netbeans.api.java.queries.SourceForBinaryQuery; |
| import org.netbeans.api.java.source.ClasspathInfo; |
| import org.netbeans.api.java.source.ClasspathInfo.PathKind; |
| import org.netbeans.api.java.source.CompilationInfo; |
| import org.netbeans.api.java.source.ElementHandle; |
| import org.netbeans.api.java.source.GeneratorUtilities; |
| import org.netbeans.api.java.source.SourceUtils; |
| import org.netbeans.api.java.source.TreeMaker; |
| import org.netbeans.api.java.source.TreePathHandle; |
| import org.netbeans.api.java.source.TreeUtilities; |
| import org.netbeans.api.java.source.TypeMirrorHandle; |
| import org.netbeans.api.java.source.WorkingCopy; |
| import org.netbeans.api.java.source.matching.Occurrence; |
| import org.netbeans.api.java.source.matching.Pattern; |
| import org.netbeans.api.project.FileOwnerQuery; |
| import org.netbeans.api.project.Project; |
| import org.netbeans.modules.java.hints.spiimpl.Hacks; |
| import org.netbeans.modules.java.hints.spiimpl.Utilities; |
| import org.netbeans.modules.java.hints.spiimpl.ipi.upgrade.ProjectDependencyUpgrader; |
| import org.netbeans.modules.refactoring.spi.SimpleRefactoringElementImplementation; |
| import org.netbeans.spi.editor.hints.Fix; |
| import org.netbeans.spi.java.classpath.support.ClassPathSupport; |
| import org.netbeans.spi.java.hints.JavaFix.TransformationContext; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileUtil; |
| import org.openide.loaders.DataFolder; |
| import org.openide.loaders.DataObject; |
| import org.openide.loaders.DataObjectNotFoundException; |
| import org.openide.modules.SpecificationVersion; |
| import org.openide.text.PositionBounds; |
| import org.openide.util.Exceptions; |
| import org.openide.util.Lookup; |
| import org.openide.util.NbBundle.Messages; |
| import org.openide.util.NbCollections; |
| |
| /**Factory methods for various predefined {@link JavaFix} implementations. |
| * |
| * @author lahvac |
| */ |
| public class JavaFixUtilities { |
| |
| /**Prepare a fix that will replace the given tree node ({@code what}) with the |
| * given code. Any variables in the {@code to} pattern will be replaced with their |
| * values from {@link HintContext#getVariables() }, {@link HintContext#getMultiVariables() } |
| * and {@link HintContext#getVariableNames() }. |
| * |
| * @param ctx basic context for which the fix should be created |
| * @param displayName the display name of the fix |
| * @param what the tree node that should be replaced |
| * @param to the new code that should replaced the {@code what} tree node |
| * @return an editor fix that performs the required transformation |
| */ |
| public static Fix rewriteFix(HintContext ctx, String displayName, TreePath what, final String to) { |
| return rewriteFix(ctx.getInfo(), displayName, what, to, ctx.getVariables(), ctx.getMultiVariables(), ctx.getVariableNames(), ctx.getConstraints(), Collections.<String, String>emptyMap()); |
| } |
| |
| static Fix rewriteFix(CompilationInfo info, String displayName, TreePath what, final String to, Map<String, TreePath> parameters, Map<String, Collection<? extends TreePath>> parametersMulti, final Map<String, String> parameterNames, Map<String, TypeMirror> constraints, Map<String, String> options, String... imports) { |
| final Map<String, TreePathHandle> params = new HashMap<String, TreePathHandle>(); |
| final Map<String, Object> extraParamsData = new HashMap<String, Object>(); |
| final Map<String, ElementHandle<?>> implicitThis = new HashMap<>(); |
| |
| for (Entry<String, TreePath> e : parameters.entrySet()) { |
| TreePath tp = e.getValue(); |
| if (tp.getParentPath() != null && !immediateChildren(tp.getParentPath().getLeaf()).contains(tp.getLeaf())) { |
| Element el = info.getTrees().getElement(tp); |
| if (el != null && el.getSimpleName().contentEquals("this")) { |
| implicitThis.put(e.getKey(), ElementHandle.create(el.getEnclosingElement())); |
| continue; |
| } |
| } |
| params.put(e.getKey(), TreePathHandle.create(e.getValue(), info)); |
| if (e.getValue() instanceof Callable) { |
| try { |
| extraParamsData.put(e.getKey(), ((Callable) e.getValue()).call()); |
| } catch (Exception ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| } |
| } |
| |
| final Map<String, Collection<TreePathHandle>> paramsMulti = new HashMap<String, Collection<TreePathHandle>>(); |
| |
| for (Entry<String, Collection<? extends TreePath>> e : parametersMulti.entrySet()) { |
| Collection<TreePathHandle> tph = new LinkedList<TreePathHandle>(); |
| |
| for (TreePath tp : e.getValue()) { |
| TreePathHandle x = TreePathHandle.create(tp, info); |
| // resolve back, to save some problems later: |
| if (x.resolve(info) == null) { |
| continue; |
| } |
| tph.add(x); |
| } |
| |
| paramsMulti.put(e.getKey(), tph); |
| } |
| |
| final Map<String, TypeMirrorHandle<?>> constraintsHandles = new HashMap<String, TypeMirrorHandle<?>>(); |
| |
| for (Entry<String, TypeMirror> c : constraints.entrySet()) { |
| constraintsHandles.put(c.getKey(), TypeMirrorHandle.create(c.getValue())); |
| } |
| |
| if (displayName == null) { |
| displayName = defaultFixDisplayName(info, parameters, to); |
| } |
| |
| return new JavaFixRealImpl(info, what, options, displayName, to, params, extraParamsData, implicitThis, paramsMulti, parameterNames, constraintsHandles, Arrays.asList(imports)).toEditorFix(); |
| } |
| |
| private static Set<Tree> immediateChildren(Tree t) { |
| Set<Tree> children = new HashSet<>(); |
| |
| t.accept(new TreeScanner<Void, Void>() { |
| @Override |
| public Void scan(Tree tree, Void p) { |
| if (tree != null) |
| children.add(tree); |
| return null; |
| } |
| }, null); |
| |
| return children; |
| } |
| |
| /**Creates a fix that removes the given code corresponding to the given tree |
| * node from the source code. |
| * |
| * @param ctx basic context for which the fix should be created |
| * @param displayName the display name of the fix |
| * @param what the tree node that should be removed |
| * @return an editor fix that removes the give tree from the source code |
| */ |
| public static Fix removeFromParent(HintContext ctx, String displayName, TreePath what) { |
| return new RemoveFromParent(displayName, ctx.getInfo(), what).toEditorFix(); |
| } |
| |
| private static String defaultFixDisplayName(CompilationInfo info, Map<String, TreePath> variables, String replaceTarget) { |
| Map<String, String> stringsForVariables = new HashMap<String, String>(); |
| |
| for (Entry<String, TreePath> e : variables.entrySet()) { |
| Tree t = e.getValue().getLeaf(); |
| SourcePositions sp = info.getTrees().getSourcePositions(); |
| int startPos = (int) sp.getStartPosition(info.getCompilationUnit(), t); |
| int endPos = (int) sp.getEndPosition(info.getCompilationUnit(), t); |
| |
| if (startPos >= 0 && endPos >= 0) { |
| stringsForVariables.put(e.getKey(), info.getText().substring(startPos, endPos)); |
| } else { |
| stringsForVariables.put(e.getKey(), ""); |
| } |
| } |
| |
| if (!stringsForVariables.containsKey("$this")) { |
| //XXX: is this correct? |
| stringsForVariables.put("$this", "this"); |
| } |
| |
| for (Entry<String, String> e : stringsForVariables.entrySet()) { |
| String quotedVariable = java.util.regex.Pattern.quote(e.getKey()); |
| String quotedTarget = Matcher.quoteReplacement(e.getValue()); |
| replaceTarget = replaceTarget.replaceAll(quotedVariable, quotedTarget); |
| } |
| |
| return "Rewrite to " + replaceTarget; |
| } |
| |
| private static void checkDependency(CompilationInfo copy, Element e, boolean canShowUI) { |
| SpecificationVersion sv = computeSpecVersion(copy, e); |
| |
| while (sv == null && e.getKind() != ElementKind.PACKAGE) { |
| e = e.getEnclosingElement(); |
| sv = computeSpecVersion(copy, e); |
| } |
| |
| if (sv == null) { |
| return ; |
| } |
| |
| Project currentProject = FileOwnerQuery.getOwner(copy.getFileObject()); |
| |
| if (currentProject == null) { |
| return ; |
| } |
| |
| FileObject file = getFile(copy, e); |
| |
| if (file == null) { |
| return ; |
| } |
| |
| FileObject root = findRootForFile(file, copy.getClasspathInfo()); |
| |
| if (root == null) { |
| return ; |
| } |
| |
| Project referedProject = FileOwnerQuery.getOwner(file); |
| |
| if (referedProject != null && currentProject.getProjectDirectory().equals(referedProject.getProjectDirectory())) { |
| return ; |
| } |
| |
| for (ProjectDependencyUpgrader pdu : Lookup.getDefault().lookupAll(ProjectDependencyUpgrader.class)) { |
| if (pdu.ensureDependency(currentProject, root, sv, canShowUI)) { |
| return ; |
| } |
| } |
| } |
| |
| private static java.util.regex.Pattern SPEC_VERSION = java.util.regex.Pattern.compile("[0-9]+(\\.[0-9]+)+"); |
| |
| static SpecificationVersion computeSpecVersion(CompilationInfo info, Element el) { |
| if (!Utilities.isJavadocSupported(info)) return null; |
| |
| DocCommentTree javaDoc = info.getDocTrees().getDocCommentTree(el); |
| |
| if (javaDoc == null) return null; |
| |
| for (DocTree tag : javaDoc.getBlockTags()) { |
| if (tag.getKind() != DocTree.Kind.SINCE) { |
| continue; |
| } |
| |
| String text = ((SinceTree) tag).getBody().toString(); |
| |
| Matcher m = SPEC_VERSION.matcher(text); |
| |
| if (!m.find()) { |
| continue; |
| } |
| |
| return new SpecificationVersion(m.group()/*ver.toString()*/); |
| } |
| |
| return null; |
| } |
| |
| @SuppressWarnings("deprecation") |
| private static FileObject getFile(CompilationInfo copy, Element e) { |
| return SourceUtils.getFile(e, copy.getClasspathInfo()); |
| } |
| |
| private static FileObject findRootForFile(final FileObject file, final ClasspathInfo cpInfo) { |
| ClassPath cp = ClassPathSupport.createProxyClassPath( |
| new ClassPath[] { |
| cpInfo.getClassPath(ClasspathInfo.PathKind.SOURCE), |
| cpInfo.getClassPath(ClasspathInfo.PathKind.BOOT), |
| cpInfo.getClassPath(ClasspathInfo.PathKind.COMPILE), |
| }); |
| |
| FileObject root = cp.findOwnerRoot(file); |
| |
| if (root != null) { |
| return root; |
| } |
| |
| for (ClassPath.Entry e : cp.entries()) { |
| FileObject[] sourceRoots = SourceForBinaryQuery.findSourceRoots(e.getURL()).getRoots(); |
| |
| if (sourceRoots.length == 0) continue; |
| |
| ClassPath sourcePath = ClassPathSupport.createClassPath(sourceRoots); |
| |
| root = sourcePath.findOwnerRoot(file); |
| |
| if (root != null) { |
| return root; |
| } |
| } |
| return null; |
| } |
| |
| private static boolean isStaticElement(Element el) { |
| if (el == null) return false; |
| |
| if (el.asType() == null || el.asType().getKind() == TypeKind.ERROR) { |
| return false; |
| } |
| |
| if (el.getModifiers().contains(Modifier.STATIC)) { |
| //XXX: |
| if (!el.getKind().isClass() && !el.getKind().isInterface()) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| if (el.getKind().isClass() || el.getKind().isInterface()) { |
| return el.getEnclosingElement().getKind() == ElementKind.PACKAGE; |
| } |
| |
| return false; |
| } |
| |
| private static class JavaFixRealImpl extends JavaFix { |
| private final String displayName; |
| private final Map<String, TreePathHandle> params; |
| private final Map<String, Object> extraParamsData; |
| private final Map<String, ElementHandle<?>> implicitThis; |
| private final Map<String, Collection<TreePathHandle>> paramsMulti; |
| private final Map<String, String> parameterNames; |
| private final Map<String, TypeMirrorHandle<?>> constraintsHandles; |
| private final Iterable<? extends String> imports; |
| private final String to; |
| |
| public JavaFixRealImpl(CompilationInfo info, TreePath what, Map<String, String> options, String displayName, String to, Map<String, TreePathHandle> params, Map<String, Object> extraParamsData, Map<String, ElementHandle<?>> implicitThis, Map<String, Collection<TreePathHandle>> paramsMulti, final Map<String, String> parameterNames, Map<String, TypeMirrorHandle<?>> constraintsHandles, Iterable<? extends String> imports) { |
| super(info, what, options); |
| |
| this.displayName = displayName; |
| this.to = to; |
| this.params = params; |
| this.extraParamsData = extraParamsData; |
| this.implicitThis = implicitThis; |
| this.paramsMulti = paramsMulti; |
| this.parameterNames = parameterNames; |
| this.constraintsHandles = constraintsHandles; |
| this.imports = imports; |
| } |
| |
| @Override |
| protected String getText() { |
| return displayName; |
| } |
| |
| @Override |
| protected void performRewrite(TransformationContext ctx) { |
| final WorkingCopy wc = ctx.getWorkingCopy(); |
| TreePath tp = ctx.getPath(); |
| |
| final GeneratorUtilities gen = GeneratorUtilities.get(wc); |
| tp = new TreePath(tp.getParentPath(), gen.importComments(tp.getLeaf(), tp.getCompilationUnit())); |
| final Map<String, TreePath> parameters = new HashMap<String, TreePath>(); |
| |
| for (Entry<String, TreePathHandle> e : params.entrySet()) { |
| TreePath p = e.getValue().resolve(wc); |
| |
| if (p == null) { |
| Logger.getLogger(JavaFix.class.getName()).log(Level.SEVERE, "Cannot resolve handle={0}", e.getValue()); |
| } |
| |
| parameters.put(e.getKey(), p); |
| } |
| |
| Map<String, Element> implicitThis = new HashMap<>(); |
| |
| for (Entry<String, ElementHandle<?>> e : this.implicitThis.entrySet()) { |
| Element clazz = e.getValue().resolve(wc); |
| |
| if (clazz == null) { |
| Logger.getLogger(JavaFix.class.getName()).log(Level.SEVERE, "Cannot resolve handle={0}", e.getValue()); |
| continue; |
| } |
| |
| implicitThis.put(e.getKey(), clazz); |
| } |
| |
| final Map<String, Collection<TreePath>> parametersMulti = new HashMap<String, Collection<TreePath>>(); |
| |
| for (Entry<String, Collection<TreePathHandle>> e : paramsMulti.entrySet()) { |
| Collection<TreePath> tps = new LinkedList<TreePath>(); |
| |
| for (TreePathHandle tph : e.getValue()) { |
| TreePath p = tph.resolve(wc); |
| if (p == null) { |
| Logger.getLogger(JavaFix.class.getName()).log(Level.SEVERE, "Cannot resolve handle={0}", e.getValue()); |
| continue; |
| } |
| |
| tps.add(p); |
| } |
| |
| parametersMulti.put(e.getKey(), tps); |
| } |
| |
| Map<String, TypeMirror> constraints = new HashMap<String, TypeMirror>(); |
| |
| for (Entry<String, TypeMirrorHandle<?>> c : constraintsHandles.entrySet()) { |
| constraints.put(c.getKey(), c.getValue().resolve(wc)); |
| } |
| |
| Scope scope = Utilities.constructScope(wc, constraints, imports); |
| |
| assert scope != null; |
| |
| Tree parsed = Utilities.parseAndAttribute(wc, to, scope); |
| |
| if (parsed.getKind() == Kind.EXPRESSION_STATEMENT && ExpressionTree.class.isAssignableFrom(tp.getLeaf().getKind().asInterface())) { |
| parsed = ((ExpressionStatementTree) parsed).getExpression(); |
| } |
| |
| Map<Tree, Tree> rewriteFromTo = new IdentityHashMap<Tree, Tree>(); |
| List<Tree> order = new ArrayList<Tree>(7); |
| Tree original; |
| |
| if (Utilities.isFakeBlock(parsed)) { |
| TreePath parent = tp.getParentPath(); |
| List<? extends StatementTree> statements = ((BlockTree) parsed).getStatements(); |
| |
| if (tp.getLeaf().getKind() == Kind.BLOCK) { |
| BlockTree real = (BlockTree) tp.getLeaf(); |
| rewriteFromTo.put(original = real, wc.getTreeMaker().Block(statements, real.isStatic())); |
| } else { |
| statements = statements.subList(1, statements.size() - 1); |
| |
| if (parent.getLeaf().getKind() == Kind.BLOCK) { |
| List<StatementTree> newStatements = new LinkedList<StatementTree>(); |
| |
| for (StatementTree st : ((BlockTree) parent.getLeaf()).getStatements()) { |
| if (st == tp.getLeaf()) { |
| newStatements.addAll(statements); |
| } else { |
| newStatements.add(st); |
| } |
| } |
| |
| rewriteFromTo.put(original = parent.getLeaf(), wc.getTreeMaker().Block(newStatements, ((BlockTree) parent.getLeaf()).isStatic())); |
| } else { |
| rewriteFromTo.put(original = tp.getLeaf(), wc.getTreeMaker().Block(statements, false)); |
| } |
| } |
| } else if (Utilities.isFakeClass(parsed)) { |
| TreePath parent = tp.getParentPath(); |
| List<? extends Tree> members = ((ClassTree) parsed).getMembers(); |
| |
| members = members.subList(1, members.size()); |
| |
| assert parent.getLeaf().getKind() == Kind.CLASS; |
| |
| List<Tree> newMembers = new LinkedList<Tree>(); |
| |
| ClassTree ct = (ClassTree) parent.getLeaf(); |
| |
| for (Tree t : ct.getMembers()) { |
| if (t == tp.getLeaf()) { |
| newMembers.addAll(members); |
| } else { |
| newMembers.add(t); |
| } |
| } |
| |
| rewriteFromTo.put(original = parent.getLeaf(), wc.getTreeMaker().Class(ct.getModifiers(), ct.getSimpleName(), ct.getTypeParameters(), ct.getExtendsClause(), ct.getImplementsClause(), newMembers)); |
| } else if (tp.getLeaf().getKind() == Kind.BLOCK && parametersMulti.containsKey("$$1$") && parsed.getKind() != Kind.BLOCK && StatementTree.class.isAssignableFrom(parsed.getKind().asInterface())) { |
| List<StatementTree> newStatements = new LinkedList<StatementTree>(); |
| |
| newStatements.add(wc.getTreeMaker().ExpressionStatement(wc.getTreeMaker().Identifier("$$1$"))); |
| newStatements.add((StatementTree) parsed); |
| newStatements.add(wc.getTreeMaker().ExpressionStatement(wc.getTreeMaker().Identifier("$$2$"))); |
| |
| parsed = wc.getTreeMaker().Block(newStatements, ((BlockTree) tp.getLeaf()).isStatic()); |
| |
| rewriteFromTo.put(original = tp.getLeaf(), parsed); |
| } else { |
| while ( tp.getParentPath().getLeaf().getKind() == Kind.PARENTHESIZED |
| && tp.getLeaf().getKind() != parsed.getKind() |
| && tp.getParentPath() != null |
| && tp.getParentPath().getParentPath() != null |
| && !requiresParenthesis(parsed, tp.getParentPath().getLeaf(), tp.getParentPath().getParentPath().getLeaf()) |
| && requiresParenthesis(tp.getLeaf(), tp.getParentPath().getLeaf(), tp.getParentPath().getParentPath().getLeaf())) |
| tp = tp.getParentPath(); |
| rewriteFromTo.put(original = tp.getLeaf(), parsed); |
| } |
| order.add(original); |
| |
| //prevent generating QualIdents inside import clauses - might be better to solve that inside ImportAnalysis2, |
| //but that seems not to be straightforward: |
| boolean inImport = parsed.getKind() == Kind.IMPORT; |
| boolean inPackage = false; |
| TreePath w = tp; |
| |
| while (!inImport && w != null) { |
| inImport |= w.getLeaf().getKind() == Kind.IMPORT; |
| inPackage |= w.getParentPath() != null && w.getParentPath().getLeaf().getKind() == Kind.COMPILATION_UNIT && ((CompilationUnitTree) w.getParentPath().getLeaf()).getPackageName() == w.getLeaf(); |
| w = w.getParentPath(); |
| } |
| |
| final Set<Tree> originalTrees = Collections.newSetFromMap(new IdentityHashMap<Tree, Boolean>()); |
| |
| new ErrorAwareTreeScanner<Void, Void>() { |
| @Override public Void scan(Tree tree, Void p) { |
| originalTrees.add(tree); |
| return super.scan(tree, p); |
| } |
| }.scan(original, null); |
| |
| new ReplaceParameters(wc, ctx.isCanShowUI(), inImport, parameters, extraParamsData, implicitThis, parametersMulti, parameterNames, rewriteFromTo, order, originalTrees).scan(new TreePath(tp.getParentPath(), rewriteFromTo.get(original)), null); |
| |
| if (inPackage) { |
| String newPackage = wc.getTreeUtilities().translate(wc.getCompilationUnit().getPackageName(), new IdentityHashMap<Tree, Tree>(rewriteFromTo))./*XXX: not correct*/toString(); |
| |
| ClassPath source = wc.getClasspathInfo().getClassPath(PathKind.SOURCE); |
| FileObject ownerRoot = source.findOwnerRoot(wc.getFileObject()); |
| |
| if (ownerRoot != null) { |
| ctx.getFileChanges().add(new MoveFile(wc.getFileObject(), ownerRoot, newPackage.replace('.', '/'))); |
| } else { |
| Logger.getLogger(JavaFix.class.getName()).log(Level.WARNING, "{0} not on its source path ({1})", new Object[] {FileUtil.getFileDisplayName(wc.getFileObject()), source.toString(PathConversionMode.PRINT)}); |
| } |
| } |
| for (Tree from : order) { |
| Tree to = rewriteFromTo.get(from); |
| // gen.copyComments(from, to, true); |
| // gen.copyComments(from, to, false); |
| wc.rewrite(from, to); |
| } |
| } |
| } |
| |
| private static class IK { |
| private final Object o; |
| |
| public IK(Object o) { |
| this.o = o; |
| } |
| |
| @Override |
| public int hashCode() { |
| int hash = 7; |
| if (o != null) { |
| hash = 59 * hash + System.identityHashCode(o); |
| } |
| return hash; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (!(obj instanceof IK)) { |
| return false; |
| } |
| final IK other = (IK) obj; |
| return other.o == this.o; |
| } |
| } |
| |
| private static final Set<Kind> NUMBER_LITERAL_KINDS = EnumSet.of(Kind.FLOAT_LITERAL, Kind.DOUBLE_LITERAL, Kind.INT_LITERAL, Kind.LONG_LITERAL); |
| |
| private static class ReplaceParameters extends ErrorAwareTreePathScanner<Number, Void> { |
| |
| private final CompilationInfo info; |
| private final TreeMaker make; |
| private final boolean canShowUI; |
| private final boolean inImport; |
| private final Map<String, TreePath> parameters; |
| private final Map<String, Object> extraParamsData; |
| private final Map<String, Element> implicitThis; |
| private final Map<String, Collection<TreePath>> parametersMulti; |
| private final Map<String, String> parameterNames; |
| private final Map<Tree, Tree> rewriteFromTo; |
| private final Set<Tree> originalTrees; |
| private final List<Tree> order; |
| private final List<Element> nestedTypes = new ArrayList<>(); |
| |
| public ReplaceParameters(WorkingCopy wc, boolean canShowUI, boolean inImport, Map<String, TreePath> parameters, Map<String, Object> extraParamsData, Map<String, Element> implicitThis, Map<String, Collection<TreePath>> parametersMulti, Map<String, String> parameterNames, Map<Tree, Tree> rewriteFromTo, List<Tree> order, Set<Tree> originalTrees) { |
| this.parameters = parameters; |
| this.info = wc; |
| this.make = wc.getTreeMaker(); |
| this.canShowUI = canShowUI; |
| this.inImport = inImport; |
| this.extraParamsData = extraParamsData; |
| this.implicitThis = implicitThis; |
| this.parametersMulti = parametersMulti; |
| this.parameterNames = parameterNames; |
| this.rewriteFromTo = rewriteFromTo; |
| this.order = order; |
| this.originalTrees = originalTrees; |
| } |
| |
| @Override |
| public Number visitIdentifier(IdentifierTree node, Void p) { |
| String name = node.getName().toString(); |
| Tree newNode = handleIdentifier(name, node); |
| |
| if (newNode != null) { |
| rewrite(node, newNode); |
| if (NUMBER_LITERAL_KINDS.contains(newNode.getKind())) { |
| return (Number) ((LiteralTree) newNode).getValue(); |
| } |
| } else { |
| Element implicitThisClass = implicitThis.get(name); |
| if (implicitThisClass != null) { |
| Element enclClass = findEnclosingClass(); |
| if (enclClass == implicitThisClass) { |
| rewrite(node, make.Identifier("this")); |
| } else { |
| rewrite(node, make.MemberSelect(make.QualIdent(implicitThisClass), "this")); |
| } |
| return null; |
| } |
| } |
| |
| Element e = info.getTrees().getElement(getCurrentPath()); |
| |
| if (e != null && isStaticElement(e) && !inImport) { |
| rewrite(node, make.QualIdent(e)); |
| } |
| |
| return super.visitIdentifier(node, p); |
| } |
| |
| private Element findEnclosingClass() { |
| TreePath findClass = getCurrentPath(); |
| while (findClass != null && !TreeUtilities.CLASS_TREE_KINDS.contains(findClass.getLeaf().getKind())) { |
| findClass = findClass.getParentPath(); |
| } |
| return findClass != null ? info.getTrees().getElement(findClass) : null; |
| } |
| |
| @Override |
| public Number visitTypeParameter(TypeParameterTree node, Void p) { |
| String name = node.getName().toString(); |
| Tree newNode = handleIdentifier(name, node); |
| |
| if (newNode != null) { |
| rewrite(node, newNode); |
| if (NUMBER_LITERAL_KINDS.contains(newNode.getKind())) { |
| return (Number) ((LiteralTree) newNode).getValue(); |
| } |
| } |
| |
| return super.visitTypeParameter(node, p); |
| } |
| |
| private static final EnumSet<Kind> COMPLEX_OPS = EnumSet.of(Kind.CONDITIONAL_AND, Kind.CONDITIONAL_OR); |
| |
| private Tree handleIdentifier(String name, Tree node) { |
| TreePath tp = parameters.get(name); |
| |
| if (tp != null) { |
| if (tp.getLeaf() instanceof Hacks.RenameTree) { |
| Hacks.RenameTree rt = (Hacks.RenameTree) tp.getLeaf(); |
| return make.setLabel(rt.originalTree, rt.newName); |
| } |
| if (!parameterNames.containsKey(name)) { |
| Tree target = tp.getLeaf(); |
| if (NUMBER_LITERAL_KINDS.contains(target.getKind())) { |
| return target; |
| } |
| //TODO: might also remove parenthesis, but needs to ensure the diff will still be minimal |
| // while (target.getKind() == Kind.PARENTHESIZED |
| // && !requiresParenthesis(((ParenthesizedTree) target).getExpression(), getCurrentPath().getParentPath().getLeaf())) { |
| // target = ((ParenthesizedTree) target).getExpression(); |
| // } |
| if ( getCurrentPath().getParentPath() != null |
| && getCurrentPath().getParentPath().getLeaf().getKind() == Kind.LOGICAL_COMPLEMENT) { |
| boolean rewriteNegated; |
| |
| if (tp.getParentPath() == null) { |
| rewriteNegated = true; |
| } else { |
| Tree parent = tp.getParentPath().getLeaf(); |
| TreePath aboveNewComplement = getCurrentPath().getParentPath().getParentPath(); |
| // Do not try to optimize too complex expressions, following the principle of the least surprise. |
| // 1/ optimization is OK if the target is without parenthesis - extra level of parens can be avoided |
| // 2/ if both target and original are parenthesized, then optimization should be done to avoid one extra paren level |
| // 3/ do not optimize complex expressions - least surprise |
| rewriteNegated = (parent.getKind() != Kind.LOGICAL_COMPLEMENT) && |
| (!COMPLEX_OPS.contains(target.getKind()) || |
| parent.getKind() != Kind.PARENTHESIZED || |
| (aboveNewComplement != null && aboveNewComplement.getLeaf().getKind() == Kind.PARENTHESIZED)); |
| } |
| |
| if (rewriteNegated) { |
| Tree negated = negate((ExpressionTree) tp.getLeaf(), getCurrentPath().getParentPath().getParentPath().getLeaf(), true); |
| |
| if (negated != null) { |
| rewrite(getCurrentPath().getParentPath().getLeaf(), negated); |
| } |
| } |
| } |
| if (requiresParenthesis(target, node, getCurrentPath().getParentPath().getLeaf())) { |
| target = make.Parenthesized((ExpressionTree) target); |
| } |
| return target; |
| } |
| } |
| |
| String variableName = parameterNames.get(name); |
| |
| if (variableName != null) { |
| return make.Identifier(variableName); |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public Number visitMemberSelect(MemberSelectTree node, Void p) { |
| Element e = info.getTrees().getElement(getCurrentPath()); |
| |
| if (e != null && (e.getKind() != ElementKind.CLASS || ((TypeElement) e).asType().getKind() != TypeKind.ERROR)) { |
| //check correct dependency: |
| checkDependency(info, e, canShowUI); |
| |
| if (isStaticElement(e) && !inImport) { |
| rewrite(node, make.QualIdent(e)); |
| |
| return null; |
| } |
| } |
| |
| MemberSelectTree nue = node; |
| String selectedName = node.getIdentifier().toString(); |
| |
| if (selectedName.startsWith("$") && parameterNames.get(selectedName) != null) { |
| nue = make.MemberSelect(node.getExpression(), parameterNames.get(selectedName)); |
| } |
| |
| if (nue.getExpression().getKind() == Kind.IDENTIFIER) { |
| String name = ((IdentifierTree) nue.getExpression()).getName().toString(); |
| |
| if (name.startsWith("$") && parameters.get(name) == null) { |
| Element implicitThisClass = implicitThis.get(name); |
| if (implicitThisClass != null) { |
| TreePath findClass = getCurrentPath(); |
| OUTER: while (findClass != null) { |
| if (TreeUtilities.CLASS_TREE_KINDS.contains(findClass.getLeaf().getKind())) { |
| Element clazz = info.getTrees().getElement(findClass); |
| if (implicitThisClass.equals(clazz)) { |
| //this.<...>, the this may be implicit: |
| rewrite(node, make.Identifier(nue.getIdentifier())); |
| return null; |
| } |
| if (clazz.getKind().isClass() || clazz.getKind().isInterface()) { |
| for (Element currentClassElement : info.getElements().getAllMembers((TypeElement) clazz)) { |
| if (currentClassElement.getSimpleName().equals(node.getIdentifier())) { |
| //there may be a resolution conflict, let the member select be qualified |
| //TODO: no conflicts between fields and methods of the same name |
| //but we current still qualify the name |
| break OUTER; |
| } |
| } |
| } |
| } |
| findClass = findClass.getParentPath(); |
| } |
| //let visitIdent handle this |
| } else { |
| //XXX: unbound variable, use identifier instead of member select - may cause problems? |
| rewrite(node, make.Identifier(nue.getIdentifier())); |
| return null; |
| } |
| } |
| } |
| |
| if (nue != node) { |
| rewrite(node, nue); |
| } |
| |
| return super.visitMemberSelect(node, p); |
| } |
| |
| @Override |
| public Number visitVariable(VariableTree node, Void p) { |
| String name = node.getName().toString(); |
| |
| if (name.startsWith("$")) { |
| String nueName = parameterNames.get(name); |
| |
| if (nueName != null) { |
| name = nueName; |
| } |
| } |
| |
| VariableTree nue = make.Variable(node.getModifiers(), name, node.getType(), resolveOptionalValue(node.getInitializer())); |
| |
| rewrite(node, nue); |
| |
| return super.visitVariable(nue, p); |
| } |
| |
| @Override |
| public Number visitIf(IfTree node, Void p) { |
| IfTree nue = make.If(node.getCondition(), node.getThenStatement(), resolveOptionalValue(node.getElseStatement())); |
| |
| rewrite(node, nue); |
| |
| return super.visitIf(nue, p); |
| } |
| |
| @Override |
| public Number visitMethod(MethodTree node, Void p) { |
| String name = node.getName().toString(); |
| String newName = name; |
| |
| if (name.startsWith("$")) { |
| if (parameterNames.containsKey(name)) { |
| newName = parameterNames.get(name); |
| } |
| } |
| |
| List<? extends TypeParameterTree> typeParams = resolveMultiParameters(node.getTypeParameters()); |
| List<? extends VariableTree> params = resolveMultiParameters(node.getParameters()); |
| List<? extends ExpressionTree> thrown = resolveMultiParameters(node.getThrows()); |
| |
| MethodTree nue = make.Method(node.getModifiers(), newName, node.getReturnType(), typeParams, params, thrown, node.getBody(), (ExpressionTree) node.getDefaultValue()); |
| |
| rewrite(node, nue); |
| |
| return super.visitMethod(nue, p); |
| } |
| |
| @Override |
| public Number visitClass(ClassTree node, Void p) { |
| String name = node.getSimpleName().toString(); |
| String newName = name; |
| |
| if (name.startsWith("$")) { |
| if (parameterNames.containsKey(name)) { |
| newName = parameterNames.get(name); |
| } |
| } |
| |
| List<? extends TypeParameterTree> typeParams = resolveMultiParameters(node.getTypeParameters()); |
| List<? extends Tree> implementsClauses = resolveMultiParameters(node.getImplementsClause()); |
| List<? extends Tree> members = resolveMultiParameters(Utilities.filterHidden(getCurrentPath(), node.getMembers())); |
| Tree extend = resolveOptionalValue(node.getExtendsClause()); |
| ClassTree nue = make.Class(node.getModifiers(), newName, typeParams, extend, implementsClauses, members); |
| |
| rewrite(node, nue); |
| |
| Element el = info.getTrees().getElement(getCurrentPath()); |
| |
| nestedTypes.add(el); |
| |
| try { |
| return super.visitClass(nue, p); |
| } finally { |
| nestedTypes.remove(nestedTypes.size() - 1); |
| } |
| } |
| |
| @Override |
| public Number visitExpressionStatement(ExpressionStatementTree node, Void p) { |
| CharSequence name = Utilities.getWildcardTreeName(node); |
| |
| if (name != null) { |
| TreePath tp = parameters.get(name.toString()); |
| |
| if (tp != null && StatementTree.class.isAssignableFrom(tp.getLeaf().getKind().asInterface())) { |
| rewrite(node, tp.getLeaf()); |
| return null; |
| } |
| } |
| |
| return super.visitExpressionStatement(node, p); |
| } |
| |
| @Override |
| public Number visitLiteral(LiteralTree node, Void p) { |
| if (node.getValue() instanceof Number) { |
| return (Number) node.getValue(); |
| } |
| |
| return super.visitLiteral(node, p); |
| } |
| |
| @Override |
| public Number visitBinary(BinaryTree node, Void p) { |
| Number left = scan(node.getLeftOperand(), p); |
| Number right = scan(node.getRightOperand(), p); |
| |
| if (left != null && right != null) { |
| Number result = null; |
| switch (node.getKind()) { |
| case MULTIPLY: |
| if (left instanceof Double || right instanceof Double) { |
| result = left.doubleValue() * right.doubleValue(); |
| } else if (left instanceof Float || right instanceof Float) { |
| result = left.floatValue() * right.floatValue(); |
| } else if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() * right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() * right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case DIVIDE: |
| if (left instanceof Double || right instanceof Double) { |
| result = left.doubleValue() / right.doubleValue(); |
| } else if (left instanceof Float || right instanceof Float) { |
| result = left.floatValue() / right.floatValue(); |
| } else if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() / right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() / right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case REMAINDER: |
| if (left instanceof Double || right instanceof Double) { |
| result = left.doubleValue() % right.doubleValue(); |
| } else if (left instanceof Float || right instanceof Float) { |
| result = left.floatValue() % right.floatValue(); |
| } else if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() % right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() % right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case PLUS: |
| if (left instanceof Double || right instanceof Double) { |
| result = left.doubleValue() + right.doubleValue(); |
| } else if (left instanceof Float || right instanceof Float) { |
| result = left.floatValue() + right.floatValue(); |
| } else if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() + right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() + right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case MINUS: |
| if (left instanceof Double || right instanceof Double) { |
| result = left.doubleValue() - right.doubleValue(); |
| } else if (left instanceof Float || right instanceof Float) { |
| result = left.floatValue() - right.floatValue(); |
| } else if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() - right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() - right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case LEFT_SHIFT: |
| if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() << right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() << right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case RIGHT_SHIFT: |
| if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() >> right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() >> right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case UNSIGNED_RIGHT_SHIFT: |
| if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() >>> right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() >>> right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case AND: |
| if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() & right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() & right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case XOR: |
| if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() ^ right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() ^ right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| |
| case OR: |
| if (left instanceof Long || right instanceof Long) { |
| result = left.longValue() | right.longValue(); |
| } else if (left instanceof Integer || right instanceof Integer) { |
| result = left.intValue() | right.intValue(); |
| } else { |
| throw new IllegalStateException("left=" + left.getClass() + ", right=" + right.getClass()); |
| } |
| break; |
| } |
| |
| if (result != null) { |
| rewrite(node, make.Literal(result)); |
| |
| return result; |
| } |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public Number visitUnary(UnaryTree node, Void p) { |
| Number op = scan(node.getExpression(), p); |
| |
| if (op != null) { |
| Number result = null; |
| switch (node.getKind()) { |
| case UNARY_MINUS: |
| if (op instanceof Double) { |
| result = -op.doubleValue(); |
| } else if (op instanceof Float) { |
| result = -op.floatValue(); |
| } else if (op instanceof Long) { |
| result = -op.longValue(); |
| } else if (op instanceof Integer) { |
| result = -op.intValue(); |
| } else { |
| throw new IllegalStateException("op=" + op.getClass()); |
| } |
| break; |
| case UNARY_PLUS: |
| result = op; |
| break; |
| } |
| |
| if (result != null) { |
| rewrite(node, make.Literal(result)); |
| |
| return result; |
| } |
| } |
| |
| return super.visitUnary(node, p); |
| } |
| |
| @Override |
| public Number visitBlock(BlockTree node, Void p) { |
| List<? extends StatementTree> nueStatement = resolveMultiParameters(node.getStatements()); |
| BlockTree nue = make.Block(nueStatement, node.isStatic()); |
| |
| rewrite(node, nue); |
| |
| return super.visitBlock(nue, p); |
| } |
| |
| @Override |
| public Number visitCase(CaseTree node, Void p) { |
| List<? extends StatementTree> statements = (List<? extends StatementTree>) resolveMultiParameters(node.getStatements()); |
| CaseTree nue = make.Case(node.getExpression(), statements); |
| |
| rewrite(node, nue); |
| return super.visitCase(node, p); |
| } |
| |
| @Override |
| public Number visitMethodInvocation(MethodInvocationTree node, Void p) { |
| List<? extends ExpressionTree> typeArgs = (List<? extends ExpressionTree>) resolveMultiParameters(node.getTypeArguments()); |
| List<? extends ExpressionTree> args = resolveMultiParameters(node.getArguments()); |
| MethodInvocationTree nue = make.MethodInvocation(typeArgs, node.getMethodSelect(), args); |
| |
| rewrite(node, nue); |
| |
| return super.visitMethodInvocation(nue, p); |
| } |
| |
| @Override |
| public Number visitNewClass(NewClassTree node, Void p) { |
| List<? extends ExpressionTree> typeArgs = (List<? extends ExpressionTree>) resolveMultiParameters(node.getTypeArguments()); |
| List<? extends ExpressionTree> args = resolveMultiParameters(node.getArguments()); |
| NewClassTree nue = make.NewClass(node.getEnclosingExpression(), typeArgs, node.getIdentifier(), args, node.getClassBody()); |
| |
| rewrite(node, nue); |
| return super.visitNewClass(nue, p); |
| } |
| |
| @Override |
| public Number visitParameterizedType(ParameterizedTypeTree node, Void p) { |
| List<? extends ExpressionTree> typeArgs = (List<? extends ExpressionTree>) resolveMultiParameters(node.getTypeArguments()); |
| ParameterizedTypeTree nue = make.ParameterizedType(node.getType(), typeArgs); |
| |
| rewrite(node, nue); |
| return super.visitParameterizedType(node, p); |
| } |
| |
| @Override |
| public Number visitSwitch(SwitchTree node, Void p) { |
| List<? extends CaseTree> cases = (List<? extends CaseTree>) resolveMultiParameters(node.getCases()); |
| SwitchTree nue = make.Switch(node.getExpression(), cases); |
| |
| rewrite(node, nue); |
| return super.visitSwitch(node, p); |
| } |
| |
| @Override |
| public Number visitTry(TryTree node, Void p) { |
| List<? extends Tree> resources = (List<? extends Tree>) resolveMultiParameters(node.getResources()); |
| List<? extends CatchTree> catches = (List<? extends CatchTree>) resolveMultiParameters(node.getCatches()); |
| TryTree nue = make.Try(resources, node.getBlock(), catches, node.getFinallyBlock()); |
| |
| rewrite(node, nue); |
| return super.visitTry(node, p); |
| } |
| |
| @Override |
| public Number visitModifiers(ModifiersTree node, Void p) { |
| List<AnnotationTree> annotations = new ArrayList<AnnotationTree>(node.getAnnotations()); |
| IdentifierTree ident = !annotations.isEmpty() && annotations.get(0).getAnnotationType().getKind() == Kind.IDENTIFIER ? (IdentifierTree) annotations.get(0).getAnnotationType() : null; |
| |
| if (ident != null) { |
| annotations.remove(0); |
| |
| String name = ident.getName().toString(); |
| TreePath orig = parameters.get(name); |
| ModifiersTree nue; |
| |
| if (orig != null && orig.getLeaf().getKind() == Kind.MODIFIERS) { |
| ModifiersTree origMods = (ModifiersTree) orig.getLeaf(); |
| Object actualContent = extraParamsData.get(name); |
| Set<Modifier> actualFlags = EnumSet.noneOf(Modifier.class); |
| boolean[] actualAnnotationsMask = new boolean[0]; |
| |
| if (actualContent instanceof Object[] && ((Object[]) actualContent)[0] instanceof Set) { |
| actualFlags.addAll(NbCollections.checkedSetByFilter((Set) ((Object[]) actualContent)[0], Modifier.class, false)); |
| } |
| |
| if (actualContent instanceof Object[] && ((Object[]) actualContent)[1] instanceof boolean[]) { |
| actualAnnotationsMask = (boolean[]) ((Object[]) actualContent)[1]; |
| } |
| |
| nue = origMods; |
| |
| for (Modifier m : origMods.getFlags()) { |
| if (actualFlags.contains(m)) continue; |
| nue = make.removeModifiersModifier(nue, m); |
| } |
| |
| for (Modifier m : node.getFlags()) { |
| nue = make.addModifiersModifier(nue, m); |
| } |
| |
| int ai = 0; |
| |
| OUTER: for (AnnotationTree a : origMods.getAnnotations()) { |
| if (actualAnnotationsMask.length <= ai || actualAnnotationsMask[ai++]) continue; |
| for (Iterator<AnnotationTree> it = annotations.iterator(); it.hasNext();) { |
| AnnotationTree toCheck = it.next(); |
| Collection<? extends Occurrence> match = org.netbeans.api.java.source.matching.Matcher.create(info).setTreeTopSearch().setSearchRoot(new TreePath(getCurrentPath(), a)).match(Pattern.createSimplePattern(new TreePath(getCurrentPath(), toCheck))); |
| |
| if (!match.isEmpty()) { |
| //should be kept: |
| it.remove(); |
| break OUTER; |
| } |
| } |
| |
| nue = make.removeModifiersAnnotation(nue, a); |
| } |
| |
| for (AnnotationTree a : annotations) { |
| nue = make.addModifiersAnnotation(nue, a); |
| scan(a, p); |
| } |
| } else { |
| nue = make.removeModifiersAnnotation(node, 0); |
| } |
| |
| rewrite(node, nue); |
| |
| return null; |
| } |
| |
| return super.visitModifiers(node, p); |
| } |
| |
| @Override |
| public Number visitNewArray(NewArrayTree node, Void p) { |
| List<? extends ExpressionTree> dimensions = (List<? extends ExpressionTree>) resolveMultiParameters(node.getDimensions()); |
| List<? extends ExpressionTree> initializers = (List<? extends ExpressionTree>) resolveMultiParameters(node.getInitializers()); |
| NewArrayTree nue = make.NewArray(node.getType(), dimensions, initializers); |
| |
| rewrite(node, nue); |
| return super.visitNewArray(node, p); |
| } |
| |
| @Override |
| public Number visitLambdaExpression(LambdaExpressionTree node, Void p) { |
| List<? extends VariableTree> args = resolveMultiParameters(node.getParameters()); |
| LambdaExpressionTree nue = make.LambdaExpression(args, node.getBody()); |
| |
| Hacks.copyLambdaKind(node, nue); |
| |
| rewrite(node, nue); |
| |
| return super.visitLambdaExpression(node, p); |
| } |
| |
| @Override |
| public Number visitAnnotation(AnnotationTree node, Void p) { |
| List<? extends ExpressionTree> args = resolveMultiParameters(node.getArguments()); |
| AnnotationTree nue = make.Annotation(node.getAnnotationType(), args); |
| |
| rewrite(node, nue); |
| |
| return super.visitAnnotation(node, p); |
| } |
| |
| private <T extends Tree> List<T> resolveMultiParameters(List<T> list) { |
| if (list == null) return null; |
| if (!Utilities.containsMultistatementTrees(list)) return list; |
| |
| List<T> result = new LinkedList<T>(); |
| |
| for (T t : list) { |
| if (Utilities.isMultistatementWildcardTree(t)) { |
| Collection<TreePath> embedded = parametersMulti.get(Utilities.getWildcardTreeName(t).toString()); |
| |
| if (embedded != null) { |
| for (TreePath tp : embedded) { |
| if (tp != null) { |
| result.add((T) tp.getLeaf()); |
| } |
| } |
| } |
| } else { |
| result.add(t); |
| } |
| } |
| |
| return result; |
| } |
| |
| private <T extends Tree> T resolveOptionalValue(T in) { |
| if (in != null && Utilities.isMultistatementWildcardTree(in)) { |
| TreePath out = parameters.get(Utilities.getWildcardTreeName(in).toString()); |
| if (out != null) return (T) out.getLeaf(); |
| return null; |
| } |
| |
| return in; |
| } |
| |
| private ExpressionTree negate(ExpressionTree original, Tree parent, boolean nullOnPlainNeg) { |
| ExpressionTree newTree; |
| switch (original.getKind()) { |
| case PARENTHESIZED: |
| ExpressionTree expr = ((ParenthesizedTree) original).getExpression(); |
| ExpressionTree negatedOrNull = negate(expr, original, nullOnPlainNeg); |
| if (negatedOrNull != null) { |
| if (negatedOrNull.getKind() != Kind.PARENTHESIZED) { |
| negatedOrNull = make.Parenthesized(negatedOrNull); |
| } |
| } |
| return negatedOrNull; |
| /** |
| if (nullOnPlainNeg) { |
| return null; |
| } else { |
| return make.Unary(Kind.LOGICAL_COMPLEMENT, original); |
| } |
| */ |
| |
| case INSTANCE_OF: |
| return make.Unary(Kind.LOGICAL_COMPLEMENT, make.Parenthesized(original)); |
| |
| case LOGICAL_COMPLEMENT: |
| newTree = ((UnaryTree) original).getExpression(); |
| while (newTree.getKind() == Kind.PARENTHESIZED && !JavaFixUtilities.requiresParenthesis(((ParenthesizedTree) newTree).getExpression(), original, parent)) { |
| newTree = ((ParenthesizedTree) newTree).getExpression(); |
| } |
| break; |
| case NOT_EQUAL_TO: |
| newTree = negateBinaryOperator(original, Kind.EQUAL_TO, false); |
| break; |
| case EQUAL_TO: |
| newTree = negateBinaryOperator(original, Kind.NOT_EQUAL_TO, false); |
| break; |
| case BOOLEAN_LITERAL: |
| newTree = make.Literal(!(Boolean) ((LiteralTree) original).getValue()); |
| break; |
| case CONDITIONAL_AND: |
| newTree = negateBinaryOperator(original, Kind.CONDITIONAL_OR, true); |
| break; |
| case CONDITIONAL_OR: |
| newTree = negateBinaryOperator(original, Kind.CONDITIONAL_AND, true); |
| break; |
| case LESS_THAN: |
| newTree = negateBinaryOperator(original, Kind.GREATER_THAN_EQUAL, false); |
| break; |
| case LESS_THAN_EQUAL: |
| newTree = negateBinaryOperator(original, Kind.GREATER_THAN, false); |
| break; |
| case GREATER_THAN: |
| newTree = negateBinaryOperator(original, Kind.LESS_THAN_EQUAL, false); |
| break; |
| case GREATER_THAN_EQUAL: |
| newTree = negateBinaryOperator(original, Kind.LESS_THAN, false); |
| break; |
| default: |
| if (nullOnPlainNeg) |
| return null; |
| newTree = make.Unary(Kind.LOGICAL_COMPLEMENT, original); |
| } |
| |
| if (JavaFixUtilities.requiresParenthesis(newTree, original, parent)) { |
| newTree = make.Parenthesized(newTree); |
| } |
| |
| return newTree; |
| } |
| |
| private ExpressionTree negateBinaryOperator(Tree original, Kind newKind, boolean negateOperands) { |
| BinaryTree bt = (BinaryTree) original; |
| BinaryTree nonNegated = make.Binary(newKind, |
| bt.getLeftOperand(), |
| bt.getRightOperand()); |
| if (negateOperands) { |
| ExpressionTree lo = negate(bt.getLeftOperand(), nonNegated, false); |
| ExpressionTree ro = negate(bt.getRightOperand(), nonNegated, false); |
| return make.Binary(newKind, |
| lo != null ? lo : bt.getLeftOperand(), |
| ro != null ? ro : bt.getRightOperand()); |
| } |
| return nonNegated; |
| } |
| |
| private void rewrite(Tree from, Tree to) { |
| if (originalTrees.contains(from)) return ; |
| rewriteFromTo.put(from, to); |
| order.add(from); |
| } |
| } |
| |
| private static final Map<Kind, Integer> OPERATOR_PRIORITIES; |
| |
| static { |
| OPERATOR_PRIORITIES = new EnumMap<Kind, Integer>(Kind.class); |
| |
| OPERATOR_PRIORITIES.put(Kind.IDENTIFIER, 0); |
| |
| for (Kind k : Kind.values()) { |
| if (k.asInterface() == LiteralTree.class) { |
| OPERATOR_PRIORITIES.put(k, 0); |
| } |
| } |
| |
| OPERATOR_PRIORITIES.put(Kind.ARRAY_ACCESS, 1); |
| OPERATOR_PRIORITIES.put(Kind.METHOD_INVOCATION, 1); |
| OPERATOR_PRIORITIES.put(Kind.MEMBER_REFERENCE, 1); |
| OPERATOR_PRIORITIES.put(Kind.MEMBER_SELECT, 1); |
| OPERATOR_PRIORITIES.put(Kind.POSTFIX_DECREMENT, 1); |
| OPERATOR_PRIORITIES.put(Kind.POSTFIX_INCREMENT, 1); |
| OPERATOR_PRIORITIES.put(Kind.NEW_ARRAY, 1); |
| OPERATOR_PRIORITIES.put(Kind.NEW_CLASS, 1); |
| |
| OPERATOR_PRIORITIES.put(Kind.BITWISE_COMPLEMENT, 2); |
| OPERATOR_PRIORITIES.put(Kind.LOGICAL_COMPLEMENT, 2); |
| OPERATOR_PRIORITIES.put(Kind.PREFIX_DECREMENT, 2); |
| OPERATOR_PRIORITIES.put(Kind.PREFIX_INCREMENT, 2); |
| OPERATOR_PRIORITIES.put(Kind.UNARY_MINUS, 2); |
| OPERATOR_PRIORITIES.put(Kind.UNARY_PLUS, 2); |
| |
| OPERATOR_PRIORITIES.put(Kind.TYPE_CAST, 3); |
| |
| OPERATOR_PRIORITIES.put(Kind.DIVIDE, 4); |
| OPERATOR_PRIORITIES.put(Kind.MULTIPLY, 4); |
| OPERATOR_PRIORITIES.put(Kind.REMAINDER, 4); |
| |
| OPERATOR_PRIORITIES.put(Kind.MINUS, 5); |
| OPERATOR_PRIORITIES.put(Kind.PLUS, 5); |
| |
| OPERATOR_PRIORITIES.put(Kind.LEFT_SHIFT, 6); |
| OPERATOR_PRIORITIES.put(Kind.RIGHT_SHIFT, 6); |
| OPERATOR_PRIORITIES.put(Kind.UNSIGNED_RIGHT_SHIFT, 6); |
| |
| OPERATOR_PRIORITIES.put(Kind.INSTANCE_OF, 7); |
| OPERATOR_PRIORITIES.put(Kind.GREATER_THAN, 7); |
| OPERATOR_PRIORITIES.put(Kind.GREATER_THAN_EQUAL, 7); |
| OPERATOR_PRIORITIES.put(Kind.LESS_THAN, 7); |
| OPERATOR_PRIORITIES.put(Kind.LESS_THAN_EQUAL, 7); |
| |
| OPERATOR_PRIORITIES.put(Kind.EQUAL_TO, 8); |
| OPERATOR_PRIORITIES.put(Kind.NOT_EQUAL_TO, 8); |
| |
| OPERATOR_PRIORITIES.put(Kind.AND, 9); |
| OPERATOR_PRIORITIES.put(Kind.OR, 11); |
| OPERATOR_PRIORITIES.put(Kind.XOR, 10); |
| |
| OPERATOR_PRIORITIES.put(Kind.CONDITIONAL_AND, 12); |
| OPERATOR_PRIORITIES.put(Kind.CONDITIONAL_OR, 13); |
| |
| OPERATOR_PRIORITIES.put(Kind.CONDITIONAL_EXPRESSION, 14); |
| |
| OPERATOR_PRIORITIES.put(Kind.AND_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.DIVIDE_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.LEFT_SHIFT_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.MINUS_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.MULTIPLY_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.OR_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.PLUS_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.REMAINDER_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.RIGHT_SHIFT_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.UNSIGNED_RIGHT_SHIFT_ASSIGNMENT, 15); |
| OPERATOR_PRIORITIES.put(Kind.XOR_ASSIGNMENT, 15); |
| } |
| |
| /** |
| * Checks whether {@code tree} can be used in places where a Primary is |
| * required (for instance, as the receiver expression of a method invocation). |
| * <p>This is a friend API intended to be used by the java.hints module. |
| * Other modules should not use this API because it might not be stabilized. |
| * @param tree the tree to check |
| * @return {@code true} iff {@code tree} can be used where a Primary is |
| * required. |
| * @since 1.31 |
| */ |
| public static boolean isPrimary(@NonNull Tree tree) { |
| final Integer treePriority = OPERATOR_PRIORITIES.get(tree.getKind()); |
| return (treePriority != null && treePriority <= 1); |
| } |
| |
| /**Checks whether putting {@code inner} tree into {@code outter} tree, |
| * when {@code original} is being replaced with {@code inner} requires parentheses. |
| * |
| * @param inner the new tree node that will be placed under {@code outter} |
| * @param original the tree node that is being replaced with {@code inner} |
| * @param outter the future parent node of {@code inner} |
| * @return true if and only if inner needs to be wrapped using {@link TreeMaker#Parenthesized(com.sun.source.tree.ExpressionTree) } |
| * to keep the original meaning. |
| */ |
| public static boolean requiresParenthesis(Tree inner, Tree original, Tree outter) { |
| if (!ExpressionTree.class.isAssignableFrom(inner.getKind().asInterface()) || outter == null) return false; |
| if (!ExpressionTree.class.isAssignableFrom(outter.getKind().asInterface())) { |
| boolean condition = false; |
| switch (outter.getKind()) { |
| case IF: |
| condition = original == ((IfTree)outter).getCondition(); |
| break; |
| case WHILE_LOOP: |
| condition = original == ((WhileLoopTree)outter).getCondition(); |
| break; |
| case DO_WHILE_LOOP: |
| condition = original == ((DoWhileLoopTree)outter).getCondition(); |
| break; |
| } |
| return condition && inner.getKind() != Tree.Kind.PARENTHESIZED; |
| } |
| |
| if (outter.getKind() == Kind.PARENTHESIZED || inner.getKind() == Kind.PARENTHESIZED) return false; |
| |
| if (outter.getKind() == Kind.METHOD_INVOCATION) { |
| if (((MethodInvocationTree) outter).getArguments().contains(original)) return false; |
| } |
| |
| if (outter.getKind() == Kind.LAMBDA_EXPRESSION) { |
| LambdaExpressionTree lt = ((LambdaExpressionTree)outter); |
| if (lt.getParameters().contains(original)) { |
| return false; |
| } |
| if (lt.getBodyKind() == LambdaExpressionTree.BodyKind.STATEMENT) { |
| return false; |
| } |
| return original.getKind() == Tree.Kind.PARENTHESIZED; |
| } |
| |
| if (outter.getKind() == Kind.NEW_CLASS) { |
| if (((NewClassTree) outter).getArguments().contains(original)) return false; |
| } |
| |
| Integer innerPriority = OPERATOR_PRIORITIES.get(inner.getKind()); |
| Integer outterPriority = OPERATOR_PRIORITIES.get(outter.getKind()); |
| |
| if (innerPriority == null || outterPriority == null) { |
| Logger.getLogger(JavaFix.class.getName()).log(Level.WARNING, "Unknown tree kind(s): {0}/{1}", new Object[] {inner.getKind(), outter.getKind()}); |
| return true; |
| } |
| |
| if (innerPriority > outterPriority) { |
| return true; |
| } |
| |
| if (innerPriority < outterPriority) { |
| return false; |
| } |
| |
| //associativity |
| if (BinaryTree.class.isAssignableFrom(outter.getKind().asInterface())) { |
| BinaryTree ot = (BinaryTree) outter; |
| |
| //TODO: for + it might be possible to skip the parenthesis: |
| return ot.getRightOperand() == original; |
| } |
| |
| if (CompoundAssignmentTree.class.isAssignableFrom(outter.getKind().asInterface())) { |
| CompoundAssignmentTree ot = (CompoundAssignmentTree) outter; |
| |
| return ot.getVariable() == original; |
| } |
| |
| if (AssignmentTree.class.isAssignableFrom(outter.getKind().asInterface())) { |
| AssignmentTree ot = (AssignmentTree) outter; |
| |
| return ot.getVariable() == original; |
| } |
| |
| return false; |
| } |
| |
| private static final class RemoveFromParent extends JavaFix { |
| |
| private final String displayName; |
| |
| public RemoveFromParent(String displayName, CompilationInfo info, TreePath toRemove) { |
| super(info, toRemove); |
| this.displayName = displayName; |
| } |
| |
| @Override |
| protected String getText() { |
| return displayName; |
| } |
| |
| @Override |
| protected void performRewrite(TransformationContext ctx) { |
| WorkingCopy wc = ctx.getWorkingCopy(); |
| TreePath tp = ctx.getPath(); |
| |
| doRemoveFromParent(wc, tp); |
| } |
| |
| private void doRemoveFromParent(WorkingCopy wc, TreePath what) { |
| TreeMaker make = wc.getTreeMaker(); |
| Tree leaf = what.getLeaf(); |
| Tree parentLeaf = what.getParentPath().getLeaf(); |
| |
| switch (parentLeaf.getKind()) { |
| case ANNOTATION: |
| AnnotationTree at = (AnnotationTree) parentLeaf; |
| AnnotationTree newAnnot; |
| |
| newAnnot = make.removeAnnotationAttrValue(at, (ExpressionTree) leaf); |
| |
| wc.rewrite(at, newAnnot); |
| break; |
| case BLOCK: |
| BlockTree bt = (BlockTree) parentLeaf; |
| |
| wc.rewrite(bt, make.removeBlockStatement(bt, (StatementTree) leaf)); |
| break; |
| case CASE: |
| CaseTree caseTree = (CaseTree) parentLeaf; |
| |
| wc.rewrite(caseTree, make.removeCaseStatement(caseTree, (StatementTree) leaf)); |
| break; |
| case CLASS: |
| ClassTree classTree = (ClassTree) parentLeaf; |
| ClassTree nueClassTree; |
| |
| if (classTree.getTypeParameters().contains(leaf)) { |
| nueClassTree = make.removeClassTypeParameter(classTree, (TypeParameterTree) leaf); |
| } else if (classTree.getExtendsClause() == leaf) { |
| nueClassTree = make.Class(classTree.getModifiers(), classTree.getSimpleName(), classTree.getTypeParameters(), null, classTree.getImplementsClause(), classTree.getMembers()); |
| } else if (classTree.getImplementsClause().contains(leaf)) { |
| nueClassTree = make.removeClassImplementsClause(classTree, leaf); |
| } else if (classTree.getMembers().contains(leaf)) { |
| nueClassTree = make.removeClassMember(classTree, leaf); |
| } else { |
| throw new UnsupportedOperationException(); |
| } |
| |
| wc.rewrite(classTree, nueClassTree); |
| break; |
| case UNION_TYPE: |
| UnionTypeTree disjunct = (UnionTypeTree) parentLeaf; |
| List<? extends Tree> alternatives = new LinkedList<Tree>(disjunct.getTypeAlternatives()); |
| |
| alternatives.remove(leaf); |
| |
| wc.rewrite(disjunct, make.UnionType(alternatives)); |
| break; |
| case METHOD: |
| MethodTree mTree = (MethodTree) parentLeaf; |
| MethodTree newMethod; |
| |
| if (mTree.getTypeParameters().contains(leaf)) { |
| newMethod = make.removeMethodTypeParameter(mTree, (TypeParameterTree) leaf); |
| } else if (mTree.getParameters().contains(leaf)) { |
| newMethod = make.removeMethodParameter(mTree, (VariableTree) leaf); |
| } else if (mTree.getThrows().contains(leaf)) { |
| newMethod = make.removeMethodThrows(mTree, (ExpressionTree) leaf); |
| } else { |
| throw new UnsupportedOperationException(); |
| } |
| |
| wc.rewrite(mTree, newMethod); |
| break; |
| case METHOD_INVOCATION: |
| MethodInvocationTree iTree = (MethodInvocationTree) parentLeaf; |
| MethodInvocationTree newInvocation; |
| |
| if (iTree.getTypeArguments().contains(leaf)) { |
| newInvocation = make.removeMethodInvocationTypeArgument(iTree, (ExpressionTree) leaf); |
| } else if (iTree.getArguments().contains(leaf)) { |
| newInvocation = make.removeMethodInvocationArgument(iTree, (ExpressionTree) leaf); |
| } else { |
| throw new UnsupportedOperationException(); |
| } |
| |
| wc.rewrite(iTree, newInvocation); |
| break; |
| case MODIFIERS: |
| ModifiersTree modsTree = (ModifiersTree) parentLeaf; |
| |
| wc.rewrite(modsTree, make.removeModifiersAnnotation(modsTree, (AnnotationTree) leaf)); |
| break; |
| case NEW_CLASS: |
| NewClassTree newCTree = (NewClassTree) parentLeaf; |
| NewClassTree newNCT; |
| |
| if (newCTree.getTypeArguments().contains(leaf)) { |
| newNCT = make.removeNewClassTypeArgument(newCTree, (ExpressionTree) leaf); |
| } else if (newCTree.getArguments().contains(leaf)) { |
| newNCT = make.removeNewClassArgument(newCTree, (ExpressionTree) leaf); |
| } else { |
| throw new UnsupportedOperationException(); |
| } |
| |
| wc.rewrite(newCTree, newNCT); |
| break; |
| case PARAMETERIZED_TYPE: |
| ParameterizedTypeTree parTree = (ParameterizedTypeTree) parentLeaf; |
| |
| wc.rewrite(parTree, make.removeParameterizedTypeTypeArgument(parTree, (ExpressionTree) leaf)); |
| break; |
| case SWITCH: |
| SwitchTree switchTree = (SwitchTree) parentLeaf; |
| SwitchTree newSwitch; |
| |
| if (switchTree.getCases().contains(leaf)) { |
| newSwitch = make.removeSwitchCase(switchTree, (CaseTree) leaf); |
| } else { |
| throw new UnsupportedOperationException(); |
| } |
| |
| wc.rewrite(switchTree, newSwitch); |
| break; |
| case TRY: |
| TryTree tryTree = (TryTree) parentLeaf; |
| TryTree newTry; |
| |
| if (tryTree.getResources().contains(leaf)) { |
| LinkedList<Tree> resources = new LinkedList<Tree>(tryTree.getResources()); |
| |
| resources.remove(leaf); |
| |
| newTry = make.Try(resources, tryTree.getBlock(), tryTree.getCatches(), tryTree.getFinallyBlock()); |
| } else if (tryTree.getCatches().contains(leaf)) { |
| newTry = make.removeTryCatch(tryTree, (CatchTree) leaf); |
| } else { |
| throw new UnsupportedOperationException(); |
| } |
| |
| wc.rewrite(tryTree, newTry); |
| break; |
| case EXPRESSION_STATEMENT: |
| doRemoveFromParent(wc, what.getParentPath()); |
| break; |
| default: |
| wc.rewrite(what.getLeaf(), make.Block(Collections.<StatementTree>emptyList(), false)); |
| break; |
| } |
| } |
| |
| } |
| |
| //TODO: from FileMovePlugin |
| private static class MoveFile extends SimpleRefactoringElementImplementation { |
| |
| private FileObject toMove; |
| private final FileObject sourceRoot; |
| private final String targetFolderName; |
| |
| public MoveFile(FileObject toMove, FileObject sourceRoot, String targetFolderName) { |
| this.toMove = toMove; |
| this.sourceRoot = sourceRoot; |
| this.targetFolderName = targetFolderName; |
| } |
| |
| @Override |
| @Messages({"#{0} - original file name", "TXT_MoveFile=Move {0}"}) |
| public String getText() { |
| return Bundle.TXT_MoveFile(toMove.getNameExt()); |
| } |
| |
| @Override |
| public String getDisplayText() { |
| return getText(); |
| } |
| |
| DataFolder sourceFolder; |
| DataObject source; |
| @Override |
| public void performChange() { |
| try { |
| FileObject target = FileUtil.createFolder(sourceRoot, targetFolderName); |
| DataFolder targetFolder = DataFolder.findFolder(target); |
| if (!toMove.isValid()) { |
| String path = FileUtil.getFileDisplayName(toMove); |
| Logger.getLogger(JavaFix.class.getName()).fine("Invalid FileObject " + path + "trying to recreate..."); |
| toMove = FileUtil.toFileObject(FileUtil.toFile(toMove)); |
| if (toMove==null) { |
| Logger.getLogger(JavaFix.class.getName()).severe("Invalid FileObject " + path + "\n. File not found."); |
| return; |
| } |
| } |
| source = DataObject.find(toMove); |
| sourceFolder = source.getFolder(); |
| source.move(targetFolder); |
| } catch (DataObjectNotFoundException ex) { |
| ex.printStackTrace(); |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| @Override |
| public void undoChange() { |
| try { |
| source.move(sourceFolder); |
| } catch (DataObjectNotFoundException ex) { |
| ex.printStackTrace(); |
| } catch (IOException ex) { |
| ex.printStackTrace(); |
| } |
| } |
| |
| @Override |
| public Lookup getLookup() { |
| return Lookup.EMPTY; |
| } |
| |
| @Override |
| public FileObject getParentFile() { |
| return toMove; |
| } |
| |
| @Override |
| public PositionBounds getPosition() { |
| return null; |
| } |
| } |
| } |