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.
authorJan Lahoda <jlahoda@netbeans.org>
Sun, 16 Jan 2011 13:56:54 +0100
changeset 5134e4e3643aa73
parent 512 c70867b1de60
child 514 7c501d1f2ad8
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.
api/src/org/netbeans/modules/jackpot30/impl/pm/CopyFinder.java
api/src/org/netbeans/modules/jackpot30/spi/MatcherUtilities.java
file/src/org/netbeans/modules/jackpot30/file/APIAccessor.java
file/src/org/netbeans/modules/jackpot30/file/Condition.java
file/src/org/netbeans/modules/jackpot30/file/DeclarativeHintsWorker.java
file/src/org/netbeans/modules/jackpot30/file/MethodInvocationContext.java
file/src/org/netbeans/modules/jackpot30/file/conditionapi/Context.java
file/src/org/netbeans/modules/jackpot30/file/conditionapi/DefaultRuleUtilities.java
file/src/org/netbeans/modules/jackpot30/file/conditionapi/Matcher.java
file/src/org/netbeans/modules/jackpot30/file/debugging/EvaluationSpanTask.java
file/test/unit/src/org/netbeans/modules/jackpot30/file/conditionapi/MatcherTest.java
file/test/unit/src/org/netbeans/modules/jackpot30/file/conditionapi/matchWithBind.hint
file/test/unit/src/org/netbeans/modules/jackpot30/file/conditionapi/matchWithBind.test
     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 +}