New condition: matchesWithBind - if the given variable matches the given pattern, all pattern variables that were not bound before are bound to the values from the matching.
1.1 --- a/api/src/org/netbeans/modules/jackpot30/impl/pm/CopyFinder.java Thu Jan 06 22:50:12 2011 +0100
1.2 +++ b/api/src/org/netbeans/modules/jackpot30/impl/pm/CopyFinder.java Sun Jan 16 13:56:54 2011 +0100
1.3 @@ -174,10 +174,10 @@
1.4 }
1.5
1.6 public static boolean isDuplicate(CompilationInfo info, TreePath one, TreePath second, boolean fullElementVerify, AtomicBoolean cancel) {
1.7 - return isDuplicate(info, one, second, fullElementVerify, null, false, cancel);
1.8 + return isDuplicate(info, one, second, fullElementVerify, null, null, null, cancel);
1.9 }
1.10
1.11 - public static boolean isDuplicate(final CompilationInfo info, TreePath one, TreePath second, boolean fullElementVerify, HintContext inVariables, boolean fillInVariables, AtomicBoolean cancel) {
1.12 + public static boolean isDuplicate(final CompilationInfo info, TreePath one, TreePath second, boolean fullElementVerify, Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names, AtomicBoolean cancel) {
1.13 if (!sameKind(one.getLeaf(), second.getLeaf())) {
1.14 return false;
1.15 }
1.16 @@ -186,14 +186,8 @@
1.17 ? new CopyFinder(one, info, cancel)
1.18 : new UnattributedCopyFinder(one, info, cancel);
1.19
1.20 - if (inVariables != null) {
1.21 - if (fillInVariables) {
1.22 - f.bindState = State.from(inVariables.getVariables(), inVariables.getMultiVariables(), inVariables.getVariableNames());
1.23 - } else {
1.24 - f.bindState.variables.putAll(inVariables.getVariables());
1.25 - f.bindState.variables2Names.putAll(inVariables.getVariableNames());
1.26 - f.bindState.multiVariables.putAll(inVariables.getMultiVariables());
1.27 - }
1.28 + if (variables != null) {
1.29 + f.bindState = State.from(variables, multiVariables, variables2Names);
1.30 }
1.31
1.32 f.allowGoDeeper = false;
1.33 @@ -1583,7 +1577,7 @@
1.34 }
1.35
1.36 public static State from(Map<String, TreePath> variables, Map<String, Collection<? extends TreePath>> multiVariables, Map<String, String> variables2Names) {
1.37 - return new State(variables, multiVariables, variables2Names, null, null);
1.38 + return new State(variables, multiVariables, variables2Names, new HashMap<Element, Element>(), new HashMap<Element, TreePath>());
1.39 }
1.40 }
1.41
2.1 --- a/api/src/org/netbeans/modules/jackpot30/spi/MatcherUtilities.java Thu Jan 06 22:50:12 2011 +0100
2.2 +++ b/api/src/org/netbeans/modules/jackpot30/spi/MatcherUtilities.java Sun Jan 16 13:56:54 2011 +0100
2.3 @@ -42,7 +42,10 @@
2.4 import com.sun.source.tree.Scope;
2.5 import com.sun.source.tree.Tree;
2.6 import com.sun.source.util.TreePath;
2.7 +import java.util.Collection;
2.8 import java.util.Collections;
2.9 +import java.util.HashMap;
2.10 +import java.util.Map;
2.11 import java.util.concurrent.atomic.AtomicBoolean;
2.12 import javax.lang.model.type.TypeMirror;
2.13 import org.netbeans.api.annotations.common.NonNull;
2.14 @@ -56,16 +59,37 @@
2.15 public class MatcherUtilities {
2.16
2.17 public static boolean matches(@NonNull HintContext ctx, @NonNull TreePath variable, @NonNull String pattern) {
2.18 - return matches(ctx, variable, pattern, false);
2.19 + return matches(ctx, variable, pattern, null, null, null);
2.20 }
2.21
2.22 //fillInVariables is a hack to allow declarative hint debugging
2.23 - public static boolean matches(@NonNull HintContext ctx, @NonNull TreePath variable, @NonNull String pattern, boolean fillInVariables) {
2.24 + public static boolean matches(@NonNull HintContext ctx, @NonNull TreePath variable, @NonNull String pattern, Map<String, TreePath> outVariables, Map<String, Collection<? extends TreePath>> outMultiVariables, Map<String, String> outVariables2Names) {
2.25 Scope s = Utilities.constructScope(ctx.getInfo(), Collections.<String, TypeMirror>emptyMap());
2.26 Tree patternTree = Utilities.parseAndAttribute(ctx.getInfo(), pattern, s);
2.27 TreePath patternTreePath = new TreePath(new TreePath(ctx.getInfo().getCompilationUnit()), patternTree);
2.28 -
2.29 - return CopyFinder.isDuplicate(ctx.getInfo(), patternTreePath, variable, true, ctx, fillInVariables, new AtomicBoolean()/*XXX*/);
2.30 + Map<String, TreePath> variables = new HashMap<String, TreePath>(ctx.getVariables());
2.31 + Map<String, Collection<? extends TreePath>> multiVariables = new HashMap<String, Collection<? extends TreePath>>(ctx.getMultiVariables());
2.32 + Map<String, String> variables2Names = new HashMap<String, String>(ctx.getVariableNames());
2.33 +
2.34 + if (CopyFinder.isDuplicate(ctx.getInfo(), patternTreePath, variable, true, variables, multiVariables, variables2Names, new AtomicBoolean()/*XXX*/)) {
2.35 + outVariables(outVariables, variables, ctx.getVariables());
2.36 + outVariables(outMultiVariables, multiVariables, ctx.getMultiVariables());
2.37 + outVariables(outVariables2Names, variables2Names, ctx.getVariableNames());
2.38 +
2.39 + return true;
2.40 + }
2.41 +
2.42 + return false;
2.43 + }
2.44 +
2.45 + private static <T> void outVariables(Map<String, T> outMap, Map<String, T> currentValues, Map<String, T> origValues) {
2.46 + if (outMap == null) return;
2.47 +
2.48 + for (String key : origValues.keySet()) {
2.49 + currentValues.remove(key);
2.50 + }
2.51 +
2.52 + outMap.putAll(currentValues);
2.53 }
2.54
2.55 }
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/APIAccessor.java Sun Jan 16 13:56:54 2011 +0100
3.3 @@ -0,0 +1,75 @@
3.4 +/*
3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3.6 + *
3.7 + * Copyright 2011 Oracle and/or its affiliates. All rights reserved.
3.8 + *
3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
3.10 + * Other names may be trademarks of their respective owners.
3.11 + *
3.12 + * The contents of this file are subject to the terms of either the GNU
3.13 + * General Public License Version 2 only ("GPL") or the Common
3.14 + * Development and Distribution License("CDDL") (collectively, the
3.15 + * "License"). You may not use this file except in compliance with the
3.16 + * License. You can obtain a copy of the License at
3.17 + * http://www.netbeans.org/cddl-gplv2.html
3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
3.19 + * specific language governing permissions and limitations under the
3.20 + * License. When distributing the software, include this License Header
3.21 + * Notice in each file and include the License file at
3.22 + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
3.23 + * particular file as subject to the "Classpath" exception as provided
3.24 + * by Oracle in the GPL Version 2 section of the License file that
3.25 + * accompanied this code. If applicable, add the following below the
3.26 + * License Header, with the fields enclosed by brackets [] replaced by
3.27 + * your own identifying information:
3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
3.29 + *
3.30 + * If you wish your version of this file to be governed by only the CDDL
3.31 + * or only the GPL Version 2, indicate your decision by adding
3.32 + * "[Contributor] elects to include this software in this distribution
3.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
3.34 + * single choice of license, a recipient has the option to distribute
3.35 + * your version of this file under either the CDDL, the GPL Version 2 or
3.36 + * to extend the choice of license to its licensees as provided above.
3.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
3.38 + * Version 2 license, then the option applies only if the new code is
3.39 + * made subject to such option by the copyright holder.
3.40 + *
3.41 + * Contributor(s):
3.42 + *
3.43 + * Portions Copyrighted 2011 Sun Microsystems, Inc.
3.44 + */
3.45 +
3.46 +package org.netbeans.modules.jackpot30.file;
3.47 +
3.48 +import com.sun.source.util.TreePath;
3.49 +import java.util.Collection;
3.50 +import java.util.Map;
3.51 +import org.netbeans.modules.jackpot30.file.conditionapi.Context;
3.52 +import org.netbeans.modules.jackpot30.file.conditionapi.Variable;
3.53 +import org.netbeans.modules.jackpot30.spi.HintContext;
3.54 +
3.55 +/**
3.56 + *
3.57 + * @author lahvac
3.58 + */
3.59 +public abstract class APIAccessor {
3.60 +
3.61 + public static APIAccessor IMPL;
3.62 +
3.63 + static {
3.64 + try {
3.65 + Class.forName(Context.class.getName(), true, Context.class.getClassLoader());
3.66 + } catch (ClassNotFoundException ex) {
3.67 + throw new IllegalStateException(ex);
3.68 + }
3.69 + }
3.70 +
3.71 + public abstract TreePath getSingleVariable(Context ctx, Variable var);
3.72 + public abstract HintContext getHintContext(Context ctx);
3.73 +
3.74 + public abstract Map<String, TreePath> getVariables(Context ctx);
3.75 + public abstract Map<String, Collection<? extends TreePath>> getMultiVariables(Context ctx);
3.76 + public abstract Map<String, String> getVariableNames(Context ctx);
3.77 +
3.78 +}
4.1 --- a/file/src/org/netbeans/modules/jackpot30/file/Condition.java Thu Jan 06 22:50:12 2011 +0100
4.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/Condition.java Sun Jan 16 13:56:54 2011 +0100
4.3 @@ -40,18 +40,13 @@
4.4 package org.netbeans.modules.jackpot30.file;
4.5
4.6 import com.sun.source.util.TreePath;
4.7 -import java.lang.reflect.InvocationTargetException;
4.8 import java.lang.reflect.Method;
4.9 -import java.util.Collection;
4.10 -import java.util.LinkedList;
4.11 import java.util.Map;
4.12 -import java.util.Map.Entry;
4.13 import java.util.concurrent.atomic.AtomicReference;
4.14 -import javax.lang.model.element.TypeElement;
4.15 import javax.lang.model.type.TypeMirror;
4.16 +import org.netbeans.api.java.source.CompilationInfo;
4.17 +import org.netbeans.modules.jackpot30.file.conditionapi.Context;
4.18 import org.netbeans.modules.jackpot30.spi.Hacks;
4.19 -import org.netbeans.modules.jackpot30.spi.HintContext;
4.20 -import org.openide.util.Exceptions;
4.21
4.22 /**
4.23 *
4.24 @@ -65,7 +60,7 @@
4.25 this.not = not;
4.26 }
4.27
4.28 - public abstract boolean holds(HintContext ctx, boolean global);
4.29 + public abstract boolean holds(Context ctx, boolean global);
4.30
4.31 @Override
4.32 public abstract String toString();
4.33 @@ -84,18 +79,19 @@
4.34 }
4.35
4.36 @Override
4.37 - public boolean holds(HintContext ctx, boolean global) {
4.38 + public boolean holds(Context ctx, boolean global) {
4.39 if (global && !not) {
4.40 //if this is a global condition, not == false, then the computation should always lead to true
4.41 //note that ctx.getVariables().get(variable) might even by null (implicit this)
4.42 return true;
4.43 }
4.44
4.45 - TreePath boundTo = ctx.getVariables().get(variable);
4.46 - TypeMirror realType = ctx.getInfo().getTrees().getTypeMirror(boundTo);
4.47 - TypeMirror designedType = Hacks.parseFQNType(ctx.getInfo(), constraint);
4.48 + TreePath boundTo = APIAccessor.IMPL.getSingleVariable(ctx, ctx.variableForName(variable));
4.49 + CompilationInfo info = APIAccessor.IMPL.getHintContext(ctx).getInfo();
4.50 + TypeMirror realType = info.getTrees().getTypeMirror(boundTo);
4.51 + TypeMirror designedType = Hacks.parseFQNType(info, constraint);
4.52
4.53 - return not ^ ctx.getInfo().getTypes().isSubtype(realType, designedType);
4.54 + return not ^ info.getTypes().isSubtype(realType, designedType);
4.55 }
4.56
4.57 @Override
4.58 @@ -120,7 +116,7 @@
4.59 }
4.60
4.61 @Override
4.62 - public boolean holds(HintContext ctx, boolean global) {
4.63 + public boolean holds(Context ctx, boolean global) {
4.64 if (toCall.get() == null) {
4.65 //not linked yet?
4.66 if (!link()) {
4.67 @@ -158,7 +154,7 @@
4.68 }
4.69
4.70 @Override
4.71 - public boolean holds(HintContext ctx, boolean global) {
4.72 + public boolean holds(Context ctx, boolean global) {
4.73 return false;
4.74 }
4.75
5.1 --- a/file/src/org/netbeans/modules/jackpot30/file/DeclarativeHintsWorker.java Thu Jan 06 22:50:12 2011 +0100
5.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/DeclarativeHintsWorker.java Sun Jan 16 13:56:54 2011 +0100
5.3 @@ -46,6 +46,7 @@
5.4 import java.util.List;
5.5 import java.util.Map;
5.6 import javax.lang.model.type.TypeMirror;
5.7 +import org.netbeans.modules.jackpot30.file.conditionapi.Context;
5.8 import org.netbeans.modules.jackpot30.spi.HintContext;
5.9 import org.netbeans.modules.jackpot30.spi.HintContext.MessageKind;
5.10 import org.netbeans.modules.jackpot30.spi.HintDescription.Worker;
5.11 @@ -88,8 +89,12 @@
5.12 }
5.13
5.14 public Collection<? extends ErrorDescription> createErrors(HintContext ctx) {
5.15 + Context context = new Context(ctx);
5.16 +
5.17 + context.enterScope();
5.18 +
5.19 for (Condition c : conditions) {
5.20 - if (!c.holds(ctx, true)) {
5.21 + if (!c.holds(context, true)) {
5.22 return null;
5.23 }
5.24 }
5.25 @@ -97,17 +102,34 @@
5.26 List<Fix> editorFixes = new LinkedList<Fix>();
5.27
5.28 OUTER: for (DeclarativeFix fix : fixes) {
5.29 - for (Condition c : fix.getConditions()) {
5.30 - if (!c.holds(ctx, false)) {
5.31 - continue OUTER;
5.32 + context.enterScope();
5.33 +
5.34 + try {
5.35 + for (Condition c : fix.getConditions()) {
5.36 + if (!c.holds(context, false)) {
5.37 + continue OUTER;
5.38 + }
5.39 }
5.40 +
5.41 + reportErrorWarning(ctx, fix.getOptions());
5.42 +
5.43 + //XXX: empty/noop fixes should not be realized:
5.44 + editorFixes.add(JavaFix.rewriteFix(ctx.getInfo(),
5.45 + fix.getDisplayName(),
5.46 + ctx.getPath(),
5.47 + fix.getPattern(),
5.48 + APIAccessor.IMPL.getVariables(context),
5.49 + APIAccessor.IMPL.getMultiVariables(context),
5.50 + APIAccessor.IMPL.getVariableNames(context),
5.51 + ctx.getConstraints(),
5.52 + fix.getOptions(),
5.53 + imports));
5.54 + } finally {
5.55 + context.leaveScope();
5.56 }
5.57 + }
5.58
5.59 - reportErrorWarning(ctx, fix.getOptions());
5.60 -
5.61 - //XXX: empty/noop fixes should not be realized:
5.62 - editorFixes.add(JavaFix.rewriteFix(ctx.getInfo(), fix.getDisplayName(), ctx.getPath(), fix.getPattern(), ctx.getVariables(), ctx.getMultiVariables(), ctx.getVariableNames(), ctx.getConstraints(), fix.getOptions(), imports));
5.63 - }
5.64 + context.leaveScope();
5.65
5.66 // if (primarySuppressWarningsKey != null && primarySuppressWarningsKey.length() > 0) {
5.67 // editorFixes.addAll(FixFactory.createSuppressWarnings(ctx.getInfo(), ctx.getPath(), primarySuppressWarningsKey));
6.1 --- a/file/src/org/netbeans/modules/jackpot30/file/MethodInvocationContext.java Thu Jan 06 22:50:12 2011 +0100
6.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/MethodInvocationContext.java Sun Jan 16 13:56:54 2011 +0100
6.3 @@ -61,7 +61,6 @@
6.4 import org.netbeans.modules.jackpot30.file.conditionapi.Matcher;
6.5 import org.netbeans.modules.jackpot30.file.conditionapi.Variable;
6.6 import org.netbeans.modules.jackpot30.spi.Hacks;
6.7 -import org.netbeans.modules.jackpot30.spi.HintContext;
6.8 import org.netbeans.spi.java.classpath.support.ClassPathSupport;
6.9 import org.openide.filesystems.FileUtil;
6.10 import org.openide.util.Exceptions;
6.11 @@ -145,7 +144,7 @@
6.12 return varArgMethod;
6.13 }
6.14
6.15 - public boolean invokeMethod(HintContext ctx, @NonNull Method method, Map<? extends String, ? extends ParameterKind> params) {
6.16 + public boolean invokeMethod(Context ctx, @NonNull Method method, Map<? extends String, ? extends ParameterKind> params) {
6.17 Collection<Object> paramValues = new LinkedList<Object>();
6.18 int i = 0;
6.19 Collection<Object> vararg = null;
6.20 @@ -181,7 +180,6 @@
6.21 paramValues.add(arr);
6.22 }
6.23
6.24 - Context context = new Context(ctx);
6.25 Matcher matcher = new Matcher(ctx);
6.26
6.27 Class<?> clazz = method.getDeclaringClass();
6.28 @@ -191,7 +189,7 @@
6.29 method.setAccessible(true);
6.30 c.setAccessible(true);
6.31
6.32 - Object instance = c.newInstance(context, matcher);
6.33 + Object instance = c.newInstance(ctx, matcher);
6.34
6.35 return (Boolean) method.invoke(instance, paramValues.toArray(new Object[0]));
6.36 } catch (InstantiationException ex) {
7.1 --- a/file/src/org/netbeans/modules/jackpot30/file/conditionapi/Context.java Thu Jan 06 22:50:12 2011 +0100
7.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/conditionapi/Context.java Sun Jan 16 13:56:54 2011 +0100
7.3 @@ -45,7 +45,10 @@
7.4 import java.util.Collection;
7.5 import java.util.Collections;
7.6 import java.util.EnumSet;
7.7 +import java.util.HashMap;
7.8 import java.util.LinkedList;
7.9 +import java.util.List;
7.10 +import java.util.Map;
7.11 import java.util.Set;
7.12 import java.util.concurrent.atomic.AtomicInteger;
7.13 import javax.lang.model.SourceVersion;
7.14 @@ -57,6 +60,7 @@
7.15 import org.netbeans.api.annotations.common.CheckForNull;
7.16 import org.netbeans.api.annotations.common.NonNull;
7.17 import org.netbeans.api.java.queries.SourceLevelQuery;
7.18 +import org.netbeans.modules.jackpot30.file.APIAccessor;
7.19 import org.netbeans.modules.jackpot30.spi.Hacks;
7.20 import org.netbeans.modules.jackpot30.spi.HintContext;
7.21
7.22 @@ -66,12 +70,18 @@
7.23 */
7.24 public class Context {
7.25
7.26 - private final HintContext ctx;
7.27 + final HintContext ctx;
7.28 + final List<Map<String, TreePath>> variables = new LinkedList<Map<String, TreePath>>();
7.29 + final List<Map<String, Collection<? extends TreePath>>> multiVariables = new LinkedList<Map<String, Collection<? extends TreePath>>>();
7.30 + final List<Map<String, String>> variableNames = new LinkedList<Map<String, String>>();
7.31 private final AtomicInteger auxiliaryVariableCounter = new AtomicInteger();
7.32
7.33 //XXX: should not be public:
7.34 public Context(HintContext ctx) {
7.35 this.ctx = ctx;
7.36 + this.variables.add(Collections.unmodifiableMap(ctx.getVariables()));
7.37 + this.multiVariables.add(Collections.unmodifiableMap(ctx.getMultiVariables()));
7.38 + this.variableNames.add(Collections.unmodifiableMap(ctx.getVariableNames()));
7.39 }
7.40
7.41 public @NonNull SourceVersion sourceVersion() {
7.42 @@ -88,7 +98,7 @@
7.43 }
7.44
7.45 public @NonNull Set<Modifier> modifiers(@NonNull Variable variable) {
7.46 - final Element e = ctx.getInfo().getTrees().getElement(ctx.getVariables().get(variable.variableName));
7.47 + final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(variable));
7.48
7.49 if (e == null) {
7.50 return Collections.unmodifiableSet(EnumSet.noneOf(Modifier.class));
7.51 @@ -98,7 +108,7 @@
7.52 }
7.53
7.54 public @CheckForNull ElementKind elementKind(@NonNull Variable variable) {
7.55 - final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(ctx, variable));
7.56 + final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(variable));
7.57
7.58 if (e == null) {
7.59 return null;
7.60 @@ -108,7 +118,7 @@
7.61 }
7.62
7.63 public @CheckForNull TypeKind typeKind(@NonNull Variable variable) {
7.64 - final TypeMirror tm = ctx.getInfo().getTrees().getTypeMirror(getSingleVariable(ctx, variable));
7.65 + final TypeMirror tm = ctx.getInfo().getTrees().getTypeMirror(getSingleVariable(variable));
7.66
7.67 if (tm == null) {
7.68 return null;
7.69 @@ -118,7 +128,7 @@
7.70 }
7.71
7.72 public @CheckForNull String name(@NonNull Variable variable) {
7.73 - final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(ctx, variable));
7.74 + final Element e = ctx.getInfo().getTrees().getElement(getSingleVariable(variable));
7.75
7.76 if (e == null) {
7.77 return null;
7.78 @@ -128,43 +138,45 @@
7.79 }
7.80
7.81 public @CheckForNull Variable parent(@NonNull Variable variable) {
7.82 - TreePath tp = ctx.getVariables().get(variable.variableName);
7.83 + TreePath tp = getSingleVariable(variable);
7.84
7.85 if (tp.getParentPath() == null) {
7.86 return null;
7.87 }
7.88 -
7.89 +
7.90 String output = "*" + auxiliaryVariableCounter.getAndIncrement();
7.91
7.92 - ctx.getVariables().put(output, tp.getParentPath());
7.93 + variables.get(0).put(output, tp.getParentPath());
7.94
7.95 return new Variable(output);
7.96 }
7.97
7.98 public @NonNull Variable variableForName(@NonNull String variableName) {
7.99 - if (!ctx.getVariables().containsKey(variableName)) {
7.100 + Variable result = new Variable(variableName);
7.101 +
7.102 + if (getSingleVariable(result) == null) {
7.103 throw new IllegalStateException("Unknown variable");
7.104 }
7.105
7.106 - return new Variable(variableName);
7.107 + return result;
7.108 }
7.109
7.110 public void createRenamed(@NonNull Variable from, @NonNull Variable to, @NonNull String newName) {
7.111 //TODO: check (the variable should not exist)
7.112 - ctx.getVariableNames().put(to.variableName, newName);
7.113 - TreePath origVariablePath = ctx.getVariables().get(from.variableName);
7.114 + variableNames.get(0).put(to.variableName, newName);
7.115 + TreePath origVariablePath = getSingleVariable(from);
7.116 TreePath newVariablePath = new TreePath(origVariablePath.getParentPath(), Hacks.createRenameTree(origVariablePath.getLeaf(), newName));
7.117 - ctx.getVariables().put(to.variableName, newVariablePath);
7.118 + variables.get(0).put(to.variableName, newVariablePath);
7.119 }
7.120
7.121 public boolean isNullLiteral(@NonNull Variable var) {
7.122 - TreePath varPath = ctx.getVariables().get(var.variableName);
7.123 + TreePath varPath = getSingleVariable(var);
7.124
7.125 return varPath.getLeaf().getKind() == Kind.NULL_LITERAL;
7.126 }
7.127
7.128 public @NonNull Iterable<? extends Variable> getIndexedVariables(@NonNull Variable multiVariable) {
7.129 - Collection<? extends TreePath> paths = ctx.getMultiVariables().get(multiVariable.variableName);
7.130 + Iterable<? extends TreePath> paths = getMultiVariable(multiVariable);
7.131
7.132 if (paths == null) {
7.133 throw new IllegalArgumentException("TODO: explanation");
7.134 @@ -180,11 +192,23 @@
7.135 return result;
7.136 }
7.137
7.138 - static Iterable<? extends TreePath> getVariable(HintContext ctx, Variable v) {
7.139 - if (isMultistatementWildcard(v.variableName)) {
7.140 - return ctx.getMultiVariables().get(v.variableName);
7.141 + public void enterScope() {
7.142 + variables.add(0, new HashMap<String, TreePath>());
7.143 + multiVariables.add(0, new HashMap<String, Collection<? extends TreePath>>());
7.144 + variableNames.add(0, new HashMap<String, String>());
7.145 + }
7.146 +
7.147 + public void leaveScope() {
7.148 + variables.remove(0);
7.149 + multiVariables.remove(0);
7.150 + variableNames.remove(0);
7.151 + }
7.152 +
7.153 + Iterable<? extends TreePath> getVariable(Variable v) {
7.154 + if (isMultistatementWildcard(v.variableName) && v.index == (-1)) {
7.155 + return getMultiVariable(v);
7.156 } else {
7.157 - return Collections.singletonList(ctx.getVariables().get(v.variableName));
7.158 + return Collections.singletonList(getSingleVariable(v));
7.159 }
7.160 }
7.161
7.162 @@ -194,11 +218,88 @@
7.163 }
7.164
7.165 //TODO: check if correct variable is provided:
7.166 - private static TreePath getSingleVariable(HintContext ctx, Variable v) {
7.167 + TreePath getSingleVariable(Variable v) {
7.168 if (v.index == (-1)) {
7.169 - return ctx.getVariables().get(v.variableName);
7.170 + for (Map<String, TreePath> map : variables) {
7.171 + if (map.containsKey(v.variableName)) {
7.172 + return map.get(v.variableName);
7.173 + }
7.174 + }
7.175 +
7.176 + return null;
7.177 } else {
7.178 - return new ArrayList<TreePath>(ctx.getMultiVariables().get(v.variableName)).get(v.index);
7.179 + return new ArrayList<TreePath>(getMultiVariable(v)).get(v.index);
7.180 }
7.181 }
7.182 +
7.183 + private Collection<? extends TreePath> getMultiVariable(Variable v) {
7.184 + for (Map<String, Collection<? extends TreePath>> multi : multiVariables) {
7.185 + if (multi.containsKey(v.variableName)) {
7.186 + return multi.get(v.variableName);
7.187 + }
7.188 + }
7.189 +
7.190 + return null;
7.191 + }
7.192 +
7.193 + static {
7.194 + APIAccessor.IMPL = new APIAccessorImpl();
7.195 + }
7.196 +
7.197 + static final class APIAccessorImpl extends APIAccessor {
7.198 +
7.199 + @Override
7.200 + public TreePath getSingleVariable(Context ctx, Variable var) {
7.201 + return ctx.getSingleVariable(var);
7.202 + }
7.203 +
7.204 + @Override
7.205 + public HintContext getHintContext(Context ctx) {
7.206 + return ctx.ctx;
7.207 + }
7.208 +
7.209 + @Override
7.210 + public Map<String, TreePath> getVariables(Context ctx) {
7.211 + Map<String, TreePath> result = new HashMap<String, TreePath>();
7.212 +
7.213 + for (Map<String, TreePath> m : reverse(ctx.variables)) {
7.214 + result.putAll(m);
7.215 + }
7.216 +
7.217 + return result;
7.218 + }
7.219 +
7.220 + @Override
7.221 + public Map<String, Collection<? extends TreePath>> getMultiVariables(Context ctx) {
7.222 + Map<String, Collection<? extends TreePath>> result = new HashMap<String, Collection<? extends TreePath>>();
7.223 +
7.224 + for (Map<String, Collection<? extends TreePath>> m : reverse(ctx.multiVariables)) {
7.225 + result.putAll(m);
7.226 + }
7.227 +
7.228 + return result;
7.229 + }
7.230 +
7.231 + @Override
7.232 + public Map<String, String> getVariableNames(Context ctx) {
7.233 + Map<String, String> result = new HashMap<String, String>();
7.234 +
7.235 + for (Map<String, String> m : reverse(ctx.variableNames)) {
7.236 + result.putAll(m);
7.237 + }
7.238 +
7.239 + return result;
7.240 + }
7.241 +
7.242 + private <T> List<T> reverse(List<T> original) {
7.243 + List<T> result = new LinkedList<T>();
7.244 +
7.245 + for (T t : original) {
7.246 + result.add(0, t);
7.247 + }
7.248 +
7.249 + return result;
7.250 + }
7.251 +
7.252 + }
7.253 }
8.1 --- a/file/src/org/netbeans/modules/jackpot30/file/conditionapi/DefaultRuleUtilities.java Thu Jan 06 22:50:12 2011 +0100
8.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/conditionapi/DefaultRuleUtilities.java Sun Jan 16 13:56:54 2011 +0100
8.3 @@ -103,6 +103,10 @@
8.4 return matchesAny(current, pattern); //XXX: $_ currently not part of variables map, so this won't work!!!
8.5 }
8.6
8.7 + public boolean matchesWithBind(Variable var, String pattern) {
8.8 + return matcher.matchesWithBind(var, pattern);
8.9 + }
8.10 +
8.11 public boolean matchesAny(Variable var, String... patterns) {
8.12 return matcher.matchesAny(var, patterns);
8.13 }
9.1 --- a/file/src/org/netbeans/modules/jackpot30/file/conditionapi/Matcher.java Thu Jan 06 22:50:12 2011 +0100
9.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/conditionapi/Matcher.java Sun Jan 16 13:56:54 2011 +0100
9.3 @@ -42,10 +42,8 @@
9.4 import com.sun.source.tree.Tree;
9.5 import com.sun.source.util.TreePath;
9.6 import com.sun.source.util.TreePathScanner;
9.7 -import java.util.Collections;
9.8 import javax.lang.model.element.Element;
9.9 import org.netbeans.api.annotations.common.NonNull;
9.10 -import org.netbeans.modules.jackpot30.spi.HintContext;
9.11 import org.netbeans.modules.jackpot30.spi.MatcherUtilities;
9.12
9.13 /**
9.14 @@ -54,10 +52,10 @@
9.15 */
9.16 public final class Matcher {
9.17
9.18 - private final HintContext ctx;
9.19 + private final Context ctx;
9.20
9.21 //XXX: should not be public:
9.22 - public Matcher(HintContext ctx) {
9.23 + public Matcher(Context ctx) {
9.24 this.ctx = ctx;
9.25 }
9.26
9.27 @@ -66,14 +64,13 @@
9.28 }
9.29
9.30 public boolean matchesAny(@NonNull Variable var, @NonNull String /*of @NonNull*/... patterns) {
9.31 - TreePath path = ctx.getVariables().get(var.variableName);
9.32 - Iterable<? extends TreePath> paths = path != null ? Collections.singletonList(path) : ctx.getMultiVariables().get(var.variableName);
9.33 + Iterable<? extends TreePath> paths = ctx.getVariable(var);
9.34
9.35 if (paths == null) return false;
9.36
9.37 for (String pattern : patterns) {
9.38 for (TreePath toSearch : paths) {
9.39 - if (MatcherUtilities.matches(ctx, toSearch, pattern)) {
9.40 + if (MatcherUtilities.matches(ctx.ctx, toSearch, pattern)) {
9.41 return true;
9.42 }
9.43 }
9.44 @@ -83,8 +80,7 @@
9.45 }
9.46
9.47 public boolean containsAny(@NonNull Variable var, @NonNull final String /*of @NonNull*/... patterns) {
9.48 - TreePath path = ctx.getVariables().get(var.variableName);
9.49 - Iterable<? extends TreePath> paths = path != null ? Collections.singletonList(path) : ctx.getMultiVariables().get(var.variableName);
9.50 + Iterable<? extends TreePath> paths = ctx.getVariable(var);
9.51 final boolean[] result = new boolean[1];
9.52
9.53 if (paths == null) return false;
9.54 @@ -99,7 +95,7 @@
9.55 TreePath tp = new TreePath(getCurrentPath(), tree);
9.56
9.57 for (String pattern : patterns) {
9.58 - if (MatcherUtilities.matches(ctx, tp, pattern)) {
9.59 + if (MatcherUtilities.matches(ctx.ctx, tp, pattern)) {
9.60 result[0] = true;
9.61 }
9.62 }
9.63 @@ -113,13 +109,13 @@
9.64 }
9.65
9.66 public boolean referencedIn(@NonNull Variable variable, @NonNull Variable in) {
9.67 - final Element e = ctx.getInfo().getTrees().getElement(ctx.getVariables().get(variable.variableName));
9.68 + final Element e = ctx.ctx.getInfo().getTrees().getElement(ctx.getSingleVariable(variable));
9.69
9.70 if (e == null) { //TODO: check also error
9.71 return false;
9.72 }
9.73
9.74 - for (TreePath tp : Context.getVariable(ctx, in)) {
9.75 + for (TreePath tp : ctx.getVariable(in)) {
9.76 boolean occurs = new TreePathScanner<Boolean, Void>() {
9.77 @Override
9.78 public Boolean scan(Tree tree, Void p) {
9.79 @@ -128,7 +124,7 @@
9.80 }
9.81
9.82 TreePath currentPath = new TreePath(getCurrentPath(), tree);
9.83 - Element currentElement = ctx.getInfo().getTrees().getElement(currentPath);
9.84 + Element currentElement = ctx.ctx.getInfo().getTrees().getElement(currentPath);
9.85
9.86 if (e.equals(currentElement)) {
9.87 return true; //TODO: throwing an exception might be faster...
9.88 @@ -160,4 +156,18 @@
9.89 return false;
9.90 }
9.91
9.92 + public boolean matchesWithBind(Variable var, String pattern) {
9.93 + TreePath path = ctx.getSingleVariable(var);
9.94 +
9.95 + if (path == null) {
9.96 + return false;
9.97 + }
9.98 +
9.99 + if (MatcherUtilities.matches(ctx.ctx, path, pattern, ctx.variables.get(0), ctx.multiVariables.get(0), ctx.variableNames.get(0))) {
9.100 + return true;
9.101 + }
9.102 +
9.103 + return false;
9.104 + }
9.105 +
9.106 }
10.1 --- a/file/src/org/netbeans/modules/jackpot30/file/debugging/EvaluationSpanTask.java Thu Jan 06 22:50:12 2011 +0100
10.2 +++ b/file/src/org/netbeans/modules/jackpot30/file/debugging/EvaluationSpanTask.java Sun Jan 16 13:56:54 2011 +0100
10.3 @@ -67,11 +67,13 @@
10.4 import org.netbeans.api.java.source.JavaSource.Phase;
10.5 import org.netbeans.api.java.source.Task;
10.6 import org.netbeans.api.lexer.TokenSequence;
10.7 +import org.netbeans.modules.jackpot30.file.APIAccessor;
10.8 import org.netbeans.modules.jackpot30.file.Condition;
10.9 import org.netbeans.modules.jackpot30.file.DeclarativeHintsParser.FixTextDescription;
10.10 +import org.netbeans.modules.jackpot30.file.conditionapi.Context;
10.11 +import org.netbeans.modules.jackpot30.file.conditionapi.Matcher;
10.12 import org.netbeans.modules.jackpot30.file.test.TestTokenId;
10.13 import org.netbeans.modules.jackpot30.spi.HintContext;
10.14 -import org.netbeans.modules.jackpot30.spi.MatcherUtilities;
10.15 import org.netbeans.modules.parsing.api.Snapshot;
10.16 import org.netbeans.modules.parsing.spi.CursorMovedSchedulerEvent;
10.17 import org.netbeans.modules.parsing.spi.Parser.Result;
10.18 @@ -209,24 +211,31 @@
10.19
10.20 HintContext ctx = new HintContext(info, null, tp, variables, multiVariables, variableNames);
10.21 String pattern = d.spec.substring(d.desc.textStart, d.desc.textEnd);
10.22 + Context context = new Context(ctx);
10.23
10.24 - boolean matches = MatcherUtilities.matches(ctx, tp, pattern, true);
10.25 + context.enterScope();
10.26 +
10.27 + boolean matches = new Matcher(context).matchesWithBind(context.variableForName("$_"), pattern);
10.28
10.29 List<int[]> target = matches ? passed : failed;
10.30
10.31 target.add(trim(d.spec, new int[] {d.desc.textStart, d.desc.textEnd}));
10.32
10.33 if (matches) {
10.34 - evaluateConditions(d.desc.conditions, d.desc.conditionSpans, ctx, passed, failed, d);
10.35 + evaluateConditions(d.desc.conditions, d.desc.conditionSpans, context, passed, failed, d);
10.36 +
10.37 + context.enterScope();
10.38
10.39 for (FixTextDescription f : d.desc.fixes) {
10.40 - evaluateConditions(f.conditions, f.conditionSpans, ctx, passed, failed, d);
10.41 + evaluateConditions(f.conditions, f.conditionSpans, context, passed, failed, d);
10.42 }
10.43 +
10.44 + context.leaveScope();
10.45 }
10.46 }
10.47 }
10.48
10.49 - private static void evaluateConditions(Iterable<Condition> conditions, Iterable<int[]> conditionSpans, HintContext ctx, List<int[]> passed, List<int[]> failed, HintWrapper d) {
10.50 + private static void evaluateConditions(Iterable<Condition> conditions, Iterable<int[]> conditionSpans, Context ctx, List<int[]> passed, List<int[]> failed, HintWrapper d) {
10.51 Iterator<Condition> cond = conditions.iterator();
10.52 Iterator<int[]> span = conditionSpans.iterator();
10.53
11.1 --- a/file/test/unit/src/org/netbeans/modules/jackpot30/file/conditionapi/MatcherTest.java Thu Jan 06 22:50:12 2011 +0100
11.2 +++ b/file/test/unit/src/org/netbeans/modules/jackpot30/file/conditionapi/MatcherTest.java Sun Jan 16 13:56:54 2011 +0100
11.3 @@ -42,6 +42,7 @@
11.4 import com.sun.source.util.TreePath;
11.5 import java.util.Arrays;
11.6 import java.util.Collection;
11.7 +import java.util.Collections;
11.8 import java.util.HashMap;
11.9 import java.util.Map;
11.10 import java.util.regex.Pattern;
11.11 @@ -72,11 +73,10 @@
11.12
11.13 TreePath tp = info.getTreeUtilities().pathFor(pos);
11.14 TreePath var = info.getTreeUtilities().pathFor(varpos);
11.15 - Map<String, TreePath> variables = new HashMap<String, TreePath>();
11.16 - variables.put("$1", var);
11.17 - Map<String, Collection<? extends TreePath>> multiVariables = new HashMap<String, Collection<? extends TreePath>>();
11.18 - multiVariables.put("$2$", Arrays.asList(tp));
11.19 - HintContext ctx = HintContext.create(info, null, null, variables, multiVariables, null);
11.20 + Map<String, TreePath> variables = Collections.singletonMap("$1", var);
11.21 + Map<String, Collection<? extends TreePath>> multiVariables = Collections.<String, Collection<? extends TreePath>>singletonMap("$2$", Arrays.asList(tp));
11.22 + Map<String, String> variables2Names = Collections.emptyMap();
11.23 + Context ctx = new Context(HintContext.create(info, null, null, variables, multiVariables, variables2Names));
11.24
11.25 new Matcher(ctx).referencedIn(new Variable("$1"), new Variable("$2$"));
11.26 }
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/file/test/unit/src/org/netbeans/modules/jackpot30/file/conditionapi/matchWithBind.hint Sun Jan 16 13:56:54 2011 +0100
12.3 @@ -0,0 +1,4 @@
12.4 + return $expr;
12.5 +=> return $o != $t; :: matchesWithBind($expr, "$o == $t")
12.6 +=> return $t == $o; :: matchesWithBind($expr, "$t != $o")
12.7 +;;
13.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
13.2 +++ b/file/test/unit/src/org/netbeans/modules/jackpot30/file/conditionapi/matchWithBind.test Sun Jan 16 13:56:54 2011 +0100
13.3 @@ -0,0 +1,28 @@
13.4 +%%TestCase bind-works-1
13.5 +package test;
13.6 +public class Test {
13.7 + private boolean t(int v1, int v2) {
13.8 + return v1 == v2;
13.9 + }
13.10 +}
13.11 +%%=>
13.12 +package test;
13.13 +public class Test {
13.14 + private boolean t(int v1, int v2) {
13.15 + return v1 != v2;
13.16 + }
13.17 +}
13.18 +%%TestCase scope-works-1
13.19 +package test;
13.20 +public class Test {
13.21 + private boolean t(int v1, int v2) {
13.22 + return v1 != v2;
13.23 + }
13.24 +}
13.25 +%%=>
13.26 +package test;
13.27 +public class Test {
13.28 + private boolean t(int v1, int v2) {
13.29 + return v1 == v2;
13.30 + }
13.31 +}