sandbox/java.hints/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/pm/NFABasedBulkSearch.java
branchdonation_review
changeset 1043 57843026e60b
parent 1027 205b7632914c
parent 1040 f7b6892fd754
child 1044 7feb751ba76b
     1.1 --- a/sandbox/java.hints/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/pm/NFABasedBulkSearch.java	Mon Dec 19 11:37:36 2016 +0100
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,826 +0,0 @@
     1.4 -/*
     1.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     1.6 - *
     1.7 - * Copyright 2009-2010 Oracle and/or its affiliates. All rights reserved.
     1.8 - *
     1.9 - * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    1.10 - * Other names may be trademarks of their respective owners.
    1.11 - *
    1.12 - * The contents of this file are subject to the terms of either the GNU
    1.13 - * General Public License Version 2 only ("GPL") or the Common
    1.14 - * Development and Distribution License("CDDL") (collectively, the
    1.15 - * "License"). You may not use this file except in compliance with the
    1.16 - * License. You can obtain a copy of the License at
    1.17 - * http://www.netbeans.org/cddl-gplv2.html
    1.18 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    1.19 - * specific language governing permissions and limitations under the
    1.20 - * License.  When distributing the software, include this License Header
    1.21 - * Notice in each file and include the License file at
    1.22 - * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    1.23 - * particular file as subject to the "Classpath" exception as provided
    1.24 - * by Oracle in the GPL Version 2 section of the License file that
    1.25 - * accompanied this code. If applicable, add the following below the
    1.26 - * License Header, with the fields enclosed by brackets [] replaced by
    1.27 - * your own identifying information:
    1.28 - * "Portions Copyrighted [year] [name of copyright owner]"
    1.29 - *
    1.30 - * If you wish your version of this file to be governed by only the CDDL
    1.31 - * or only the GPL Version 2, indicate your decision by adding
    1.32 - * "[Contributor] elects to include this software in this distribution
    1.33 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
    1.34 - * single choice of license, a recipient has the option to distribute
    1.35 - * your version of this file under either the CDDL, the GPL Version 2 or
    1.36 - * to extend the choice of license to its licensees as provided above.
    1.37 - * However, if you add GPL Version 2 code and therefore, elected the GPL
    1.38 - * Version 2 license, then the option applies only if the new code is
    1.39 - * made subject to such option by the copyright holder.
    1.40 - *
    1.41 - * Contributor(s):
    1.42 - *
    1.43 - * Portions Copyrighted 2009-2010 Sun Microsystems, Inc.
    1.44 - */
    1.45 -
    1.46 -package org.netbeans.modules.java.hints.spiimpl.pm;
    1.47 -
    1.48 -import com.sun.source.tree.AnnotationTree;
    1.49 -import com.sun.source.tree.BlockTree;
    1.50 -import com.sun.source.tree.ClassTree;
    1.51 -import com.sun.source.tree.IdentifierTree;
    1.52 -import com.sun.source.tree.LiteralTree;
    1.53 -import com.sun.source.tree.MemberSelectTree;
    1.54 -import com.sun.source.tree.MethodTree;
    1.55 -import com.sun.source.tree.ModifiersTree;
    1.56 -import com.sun.source.tree.StatementTree;
    1.57 -import com.sun.source.tree.Tree;
    1.58 -import com.sun.source.tree.Tree.Kind;
    1.59 -import com.sun.source.tree.VariableTree;
    1.60 -import com.sun.source.util.TreePath;
    1.61 -import com.sun.source.util.TreeScanner;
    1.62 -import java.io.ByteArrayOutputStream;
    1.63 -import java.io.IOException;
    1.64 -import java.io.InputStream;
    1.65 -import java.io.UnsupportedEncodingException;
    1.66 -import java.util.ArrayList;
    1.67 -import java.util.Collection;
    1.68 -import java.util.Collections;
    1.69 -import java.util.EnumMap;
    1.70 -import java.util.EnumSet;
    1.71 -import java.util.HashMap;
    1.72 -import java.util.HashSet;
    1.73 -import java.util.Iterator;
    1.74 -import java.util.LinkedHashMap;
    1.75 -import java.util.LinkedList;
    1.76 -import java.util.List;
    1.77 -import java.util.Map;
    1.78 -import java.util.Map.Entry;
    1.79 -import java.util.Set;
    1.80 -import java.util.Stack;
    1.81 -import java.util.concurrent.atomic.AtomicBoolean;
    1.82 -import javax.lang.model.element.Name;
    1.83 -import org.netbeans.api.java.source.CompilationInfo;
    1.84 -import org.netbeans.api.java.source.support.CancellableTreeScanner;
    1.85 -import org.netbeans.modules.java.hints.spiimpl.Utilities;
    1.86 -import org.netbeans.modules.java.hints.providers.spi.HintDescription.AdditionalQueryConstraints;
    1.87 -import org.openide.util.Exceptions;
    1.88 -
    1.89 -/**
    1.90 - *
    1.91 - * @author lahvac
    1.92 - */
    1.93 -public class NFABasedBulkSearch extends BulkSearch {
    1.94 -
    1.95 -    public NFABasedBulkSearch() {
    1.96 -        super(false);
    1.97 -    }
    1.98 -
    1.99 -    @Override
   1.100 -    public Map<String, Collection<TreePath>> match(CompilationInfo info, final AtomicBoolean cancel, TreePath tree, BulkPattern patternIn, Map<String, Long> timeLog) {
   1.101 -        BulkPatternImpl pattern = (BulkPatternImpl) patternIn;
   1.102 -        
   1.103 -        final Map<Res, Collection<TreePath>> occurringPatterns = new HashMap<Res, Collection<TreePath>>();
   1.104 -        final NFA<Input, Res> nfa = pattern.toNFA();
   1.105 -        final Set<String> identifiers = new HashSet<String>();
   1.106 -
   1.107 -        new CollectIdentifiers<Void, TreePath>(identifiers, cancel) {
   1.108 -            private NFA.State active = nfa.getStartingState();
   1.109 -            @Override
   1.110 -            public Void scan(Tree node, TreePath p) {
   1.111 -                if (node == null) {
   1.112 -                    return null;
   1.113 -                }
   1.114 -
   1.115 -                TreePath currentPath = new TreePath(p, node);
   1.116 -                boolean[] goDeeper = new boolean[1];
   1.117 -                final NFA.State newActiveAfterVariable = nfa.transition(active, new Input(Kind.IDENTIFIER, "$", false));
   1.118 -                Input normalizedInput = normalizeInput(node, goDeeper, null);
   1.119 -                boolean ignoreKind = normalizedInput.kind == Kind.IDENTIFIER || normalizedInput.kind == Kind.MEMBER_SELECT;
   1.120 -
   1.121 -                NFA.State newActiveBefore = nfa.transition(active, normalizedInput);
   1.122 -
   1.123 -                if (normalizedInput.name != null && !ignoreKind) {
   1.124 -                    newActiveBefore = nfa.join(newActiveBefore, nfa.transition(active, new Input(normalizedInput.kind, "$", false)));
   1.125 -                }
   1.126 -
   1.127 -                active = newActiveBefore;
   1.128 -
   1.129 -                if (goDeeper[0]) {
   1.130 -                    super.scan(node, currentPath);
   1.131 -                } else {
   1.132 -                    new CollectIdentifiers<Void, Void>(identifiers, cancel).scan(node, null);
   1.133 -                }
   1.134 -                
   1.135 -                if (cancel.get()) return null;
   1.136 -
   1.137 -                NFA.State newActiveAfter = nfa.transition(active, UP);
   1.138 -
   1.139 -                active = nfa.join(newActiveAfter, nfa.transition(newActiveAfterVariable, UP));
   1.140 -
   1.141 -                for (Res r : nfa.getResults(active)) {
   1.142 -                    addOccurrence(r, currentPath);
   1.143 -                }
   1.144 -
   1.145 -                return null;
   1.146 -            }
   1.147 -
   1.148 -            @Override
   1.149 -            public Void scan(Iterable<? extends Tree> nodes, TreePath p) {
   1.150 -                active = nfa.transition(active, new Input(Kind.IDENTIFIER, "(", false));
   1.151 -                try {
   1.152 -                    return super.scan(nodes, p);
   1.153 -                } finally {
   1.154 -                    active = nfa.transition(active, UP);
   1.155 -                }
   1.156 -            }
   1.157 -            
   1.158 -            private void addOccurrence(Res r, TreePath currentPath) {
   1.159 -                Collection<TreePath> occurrences = occurringPatterns.get(r);
   1.160 -                if (occurrences == null) {
   1.161 -                    occurringPatterns.put(r, occurrences = new LinkedList<TreePath>());
   1.162 -                }
   1.163 -                occurrences.add(currentPath);
   1.164 -            }
   1.165 -        }.scan(tree, tree.getParentPath());
   1.166 -
   1.167 -        if (cancel.get()) return null;
   1.168 -        
   1.169 -        Map<String, Collection<TreePath>> result = new HashMap<String, Collection<TreePath>>();
   1.170 -
   1.171 -        for (Entry<Res, Collection<TreePath>> e : occurringPatterns.entrySet()) {
   1.172 -            if (cancel.get()) return null;
   1.173 -            if (!identifiers.containsAll(pattern.getIdentifiers().get(e.getKey().patternIndex))) {
   1.174 -                continue;
   1.175 -            }
   1.176 -
   1.177 -            result.put(e.getKey().pattern, e.getValue());
   1.178 -        }
   1.179 -
   1.180 -        return result;
   1.181 -    }
   1.182 -
   1.183 -    @Override
   1.184 -    public BulkPattern create(Collection<? extends String> code, Collection<? extends Tree> patterns, Collection<? extends AdditionalQueryConstraints> additionalConstraints, final AtomicBoolean cancel) {
   1.185 -        int startState = 0;
   1.186 -        final int[] nextState = new int[] {1};
   1.187 -        final Map<NFA.Key<Input>, NFA.State> transitionTable = new LinkedHashMap<NFA.Key<Input>, NFA.State>();
   1.188 -        Map<Integer, Res> finalStates = new HashMap<Integer, Res>();
   1.189 -        List<Set<? extends String>> identifiers = new LinkedList<Set<? extends String>>();
   1.190 -        List<List<List<String>>> requiredContent = new ArrayList<List<List<String>>>();
   1.191 -        Iterator<? extends String> codeIt = code.iterator();
   1.192 -        int patternIndex = 0;
   1.193 -
   1.194 -        for (final Tree pattern : patterns) {
   1.195 -            final int[] currentState = new int[] {startState};
   1.196 -            final Set<String> patternIdentifiers = new HashSet<String>();
   1.197 -            final List<List<String>> content = new ArrayList<List<String>>();
   1.198 -
   1.199 -            identifiers.add(patternIdentifiers);
   1.200 -            requiredContent.add(content);
   1.201 -
   1.202 -            class Scanner extends CollectIdentifiers<Void, Void> {
   1.203 -                public Scanner() {
   1.204 -                    super(patternIdentifiers, cancel);
   1.205 -                }
   1.206 -                private boolean auxPath;
   1.207 -                private List<String> currentContent;
   1.208 -                {
   1.209 -                    content.add(currentContent = new ArrayList<String>());
   1.210 -                }
   1.211 -                @Override
   1.212 -                public Void scan(Tree t, Void v) {
   1.213 -                    if (t == null) {
   1.214 -                        return null;
   1.215 -                    }
   1.216 -
   1.217 -                    if (Utilities.isMultistatementWildcardTree(t) || multiModifiers(t)) {
   1.218 -                        int target = nextState[0]++;
   1.219 -
   1.220 -                        setBit(transitionTable, NFA.Key.create(currentState[0], new Input(Kind.IDENTIFIER, "$", false)), target);
   1.221 -                        setBit(transitionTable, NFA.Key.create(target, UP), currentState[0]);
   1.222 -
   1.223 -                        content.add(currentContent = new ArrayList<String>());
   1.224 -                        
   1.225 -                        return null;
   1.226 -                    }
   1.227 -
   1.228 -                    if (t.getKind() == Kind.BLOCK) {
   1.229 -                        StatementTree singletonStatement = null;
   1.230 -                        BlockTree bt = (BlockTree) t;
   1.231 -
   1.232 -                        if (!bt.isStatic()) {
   1.233 -                            switch (bt.getStatements().size()) {
   1.234 -                                case 1:
   1.235 -                                    singletonStatement = bt.getStatements().get(0);
   1.236 -                                    break;
   1.237 -                                case 2:
   1.238 -                                    if (Utilities.isMultistatementWildcardTree(bt.getStatements().get(0))) {
   1.239 -                                        singletonStatement = bt.getStatements().get(1);
   1.240 -                                    } else {
   1.241 -                                        if (Utilities.isMultistatementWildcardTree(bt.getStatements().get(1))) {
   1.242 -                                            singletonStatement = bt.getStatements().get(0);
   1.243 -                                        }
   1.244 -                                    }
   1.245 -                                    break;
   1.246 -                                case 3:
   1.247 -                                    if (Utilities.isMultistatementWildcardTree(bt.getStatements().get(0)) && Utilities.isMultistatementWildcardTree(bt.getStatements().get(2))) {
   1.248 -                                        singletonStatement = bt.getStatements().get(1);
   1.249 -                                    }
   1.250 -                                    break;
   1.251 -                            }
   1.252 -                        }
   1.253 -
   1.254 -                        if (singletonStatement != null) {
   1.255 -                            int backup = currentState[0];
   1.256 -
   1.257 -                            boolean oldAuxPath = auxPath;
   1.258 -
   1.259 -                            auxPath = true;
   1.260 -
   1.261 -                            scan(singletonStatement, null);
   1.262 -
   1.263 -                            auxPath = oldAuxPath;
   1.264 -
   1.265 -                            int target = currentState[0];
   1.266 -
   1.267 -                            setBit(transitionTable, NFA.Key.create(backup, new Input(Kind.BLOCK, null, false)), currentState[0] = nextState[0]++);
   1.268 -                            setBit(transitionTable, NFA.Key.create(currentState[0], new Input(Kind.IDENTIFIER, "(", false)), currentState[0] = nextState[0]++);
   1.269 -
   1.270 -                            for (StatementTree st : bt.getStatements()) {
   1.271 -                                scan(st, null);
   1.272 -                            }
   1.273 -
   1.274 -                            setBit(transitionTable, NFA.Key.create(currentState[0], UP), currentState[0] = nextState[0]++);
   1.275 -                            setBit(transitionTable, NFA.Key.create(currentState[0], UP), target);
   1.276 -                            currentState[0] = target;
   1.277 -
   1.278 -                            return null;
   1.279 -                        }
   1.280 -                    }
   1.281 -                    
   1.282 -                    boolean[] goDeeper = new boolean[1];
   1.283 -                    Input[] bypass = new Input[1];
   1.284 -                    Input i = normalizeInput(t, goDeeper, bypass);
   1.285 -
   1.286 -                    if (!TO_IGNORE.contains(i.kind) && !auxPath) {
   1.287 -                        currentContent.add(kind2EncodedString.get(i.kind));
   1.288 -                    }
   1.289 -
   1.290 -                    if (i.name != null && !auxPath) {
   1.291 -                        if (!"$".equals(i.name)) {
   1.292 -                            if (isIdentifierAcceptable(i.name)) {
   1.293 -                                currentContent.add(i.name);
   1.294 -                            }
   1.295 -                            if (Utilities.isPureMemberSelect(t, false)) {
   1.296 -                                content.add(currentContent = new ArrayList<String>());
   1.297 -                            }
   1.298 -                        } else {
   1.299 -                            content.add(currentContent = new ArrayList<String>());
   1.300 -                        }
   1.301 -                    }
   1.302 -
   1.303 -                    int backup = currentState[0];
   1.304 -
   1.305 -                    handleTree(i, goDeeper, t, bypass);
   1.306 -
   1.307 -                    boolean oldAuxPath = auxPath;
   1.308 -
   1.309 -                    auxPath = true;
   1.310 -
   1.311 -                    if (StatementTree.class.isAssignableFrom(t.getKind().asInterface()) && t != pattern) {
   1.312 -                        int target = currentState[0];
   1.313 -
   1.314 -                        setBit(transitionTable, NFA.Key.create(backup, new Input(Kind.BLOCK, null, false)), currentState[0] = nextState[0]++);
   1.315 -                        setBit(transitionTable, NFA.Key.create(currentState[0], new Input(Kind.IDENTIFIER, "(", false)), currentState[0] = nextState[0]++);
   1.316 -                        handleTree(i, goDeeper, t, bypass);
   1.317 -                        setBit(transitionTable, NFA.Key.create(currentState[0], UP), currentState[0] = nextState[0]++);
   1.318 -                        setBit(transitionTable, NFA.Key.create(currentState[0], UP), target);
   1.319 -                        currentState[0] = target;
   1.320 -                    }
   1.321 -
   1.322 -                    auxPath = oldAuxPath;
   1.323 -
   1.324 -                    return null;
   1.325 -                }
   1.326 -
   1.327 -                @Override
   1.328 -                public Void scan(Iterable<? extends Tree> nodes, Void p) {
   1.329 -                    setBit(transitionTable, NFA.Key.create(currentState[0], new Input(Kind.IDENTIFIER, "(", false)), currentState[0] = nextState[0]++);
   1.330 -                    try {
   1.331 -                        return super.scan(nodes, p);
   1.332 -                    } finally {
   1.333 -                        setBit(transitionTable, NFA.Key.create(currentState[0], UP), currentState[0] = nextState[0]++);
   1.334 -                    }
   1.335 -                }
   1.336 -
   1.337 -                private void handleTree(Input i, boolean[] goDeeper, Tree t, Input[] bypass) {
   1.338 -                    int backup = currentState[0];
   1.339 -                    int target = nextState[0]++;
   1.340 -
   1.341 -                    setBit(transitionTable, NFA.Key.create(backup, i), currentState[0] = nextState[0]++);
   1.342 -
   1.343 -                    if (goDeeper[0]) {
   1.344 -                        super.scan(t, null);
   1.345 -                    } else {
   1.346 -                        new CollectIdentifiers<Void, Void>(patternIdentifiers, cancel).scan(t, null);
   1.347 -                        int aux = nextState[0]++;
   1.348 -                        setBit(transitionTable, NFA.Key.create(backup, new Input(Kind.MEMBER_SELECT, i.name, false)), aux);
   1.349 -                        setBit(transitionTable, NFA.Key.create(aux, new Input(Kind.IDENTIFIER, "$", false)), aux = nextState[0]++);
   1.350 -                        setBit(transitionTable, NFA.Key.create(aux, UP), aux = nextState[0]++);
   1.351 -                        setBit(transitionTable, NFA.Key.create(aux, UP), target);
   1.352 -                    }
   1.353 -
   1.354 -                    setBit(transitionTable, NFA.Key.create(currentState[0], UP), target);
   1.355 -                    
   1.356 -                    if (bypass[0] != null) {
   1.357 -                        int intermediate = nextState[0]++;
   1.358 -                        
   1.359 -                        setBit(transitionTable, NFA.Key.create(backup, bypass[0]), intermediate);
   1.360 -                        setBit(transitionTable, NFA.Key.create(intermediate, UP), target);
   1.361 -                    }
   1.362 -                    
   1.363 -                    currentState[0] = target;
   1.364 -                }
   1.365 -            }
   1.366 -
   1.367 -            Scanner s = new Scanner();
   1.368 -
   1.369 -            s.scan(pattern, null);
   1.370 -
   1.371 -            finalStates.put(currentState[0], new Res(codeIt.next(), patternIndex++));
   1.372 -        }
   1.373 -
   1.374 -        if (cancel.get()) return null;
   1.375 -        
   1.376 -        NFA<Input, Res> nfa = NFA.<Input, Res>create(startState, nextState[0], null, transitionTable, finalStates);
   1.377 -
   1.378 -        return new BulkPatternImpl(new LinkedList<String>(code), identifiers, requiredContent, new LinkedList<AdditionalQueryConstraints>(additionalConstraints), nfa);
   1.379 -    }
   1.380 -
   1.381 -    private static void setBit(Map<NFA.Key<Input>, NFA.State> transitionTable, NFA.Key<Input> input, int state) {
   1.382 -        NFA.State target = transitionTable.get(input);
   1.383 -
   1.384 -        if (target == null) {
   1.385 -            transitionTable.put(input, target = NFA.State.create());
   1.386 -        }
   1.387 -
   1.388 -        target.mutableOr(state);
   1.389 -    }
   1.390 -
   1.391 -    private static Input normalizeInput(Tree t, boolean[] goDeeper, Input[] bypass) {
   1.392 -        if (t.getKind() == Kind.IDENTIFIER && ((IdentifierTree) t).getName().toString().startsWith("$")) {
   1.393 -            goDeeper[0] = false;
   1.394 -            return new Input(Kind.IDENTIFIER, "$", false);
   1.395 -        }
   1.396 -
   1.397 -        if (Utilities.getWildcardTreeName(t) != null) {
   1.398 -            goDeeper[0] = false;
   1.399 -            return new Input(Kind.IDENTIFIER, "$", false);
   1.400 -        }
   1.401 -        
   1.402 -        if (t.getKind() == Kind.IDENTIFIER) {
   1.403 -            goDeeper[0] = false;
   1.404 -            String name = ((IdentifierTree) t).getName().toString();
   1.405 -            return new Input(Kind.IDENTIFIER, name, false);
   1.406 -        }
   1.407 -
   1.408 -        if (t.getKind() == Kind.MEMBER_SELECT) {
   1.409 -            String name = ((MemberSelectTree) t).getIdentifier().toString();
   1.410 -            if (name.startsWith("$")) {
   1.411 -                goDeeper[0] = false;//???
   1.412 -                return new Input(Kind.IDENTIFIER, "$", false);
   1.413 -            }
   1.414 -            if (bypass != null && Utilities.isPureMemberSelect(t, true)) {
   1.415 -                bypass[0] = new Input(Kind.IDENTIFIER, name, false);
   1.416 -            }
   1.417 -            goDeeper[0] = true;
   1.418 -            return new Input(Kind.MEMBER_SELECT, name, false);
   1.419 -        }
   1.420 -
   1.421 -        goDeeper[0] = true;
   1.422 -
   1.423 -        String name;
   1.424 -
   1.425 -        switch (t.getKind()) {
   1.426 -            case CLASS: name = ((ClassTree)t).getSimpleName().toString(); break;
   1.427 -            case VARIABLE: name = ((VariableTree)t).getName().toString(); break;
   1.428 -            case METHOD: name = ((MethodTree)t).getName().toString(); break;
   1.429 -            case BOOLEAN_LITERAL: name = ((LiteralTree) t).getValue().toString(); break;
   1.430 -            default: name = null;
   1.431 -        }
   1.432 -
   1.433 -        if (name != null) {
   1.434 -            if (!name.isEmpty() && name.charAt(0) == '$') {
   1.435 -                name = "$";
   1.436 -            }
   1.437 -        }
   1.438 -        return new Input(t.getKind(), name, false);
   1.439 -    }
   1.440 -    
   1.441 -    private boolean multiModifiers(Tree t) {
   1.442 -        if (t.getKind() != Kind.MODIFIERS) return false;
   1.443 -        
   1.444 -        List<AnnotationTree> annotations = new ArrayList<AnnotationTree>(((ModifiersTree) t).getAnnotations());
   1.445 -
   1.446 -        return !annotations.isEmpty() && annotations.get(0).getAnnotationType().getKind() == Kind.IDENTIFIER;
   1.447 -    }
   1.448 -
   1.449 -    @Override
   1.450 -    public boolean matches(CompilationInfo info, AtomicBoolean cancel, TreePath tree, BulkPattern pattern) {
   1.451 -        //XXX: performance
   1.452 -        return !match(info, cancel, tree, pattern).isEmpty();
   1.453 -    }
   1.454 -
   1.455 -    private static final Set<Kind> TO_IGNORE = EnumSet.of(Kind.BLOCK, Kind.IDENTIFIER, Kind.MEMBER_SELECT);
   1.456 -
   1.457 -    @Override
   1.458 -    public void encode(Tree tree, final EncodingContext ctx, AtomicBoolean cancel) {
   1.459 -        final Set<String> identifiers = new HashSet<String>();
   1.460 -        final List<String> content = new ArrayList<String>();
   1.461 -        if (!ctx.isForDuplicates()) {
   1.462 -            new CollectIdentifiers<Void, Void>(identifiers, cancel).scan(tree, null);
   1.463 -            try {
   1.464 -                int size = identifiers.size();
   1.465 -                ctx.getOut().write((size >> 24) & 0xFF);
   1.466 -                ctx.getOut().write((size >> 16) & 0xFF);
   1.467 -                ctx.getOut().write((size >>  8) & 0xFF);
   1.468 -                ctx.getOut().write((size >>  0) & 0xFF);
   1.469 -                for (String ident : identifiers) {
   1.470 -                    ctx.getOut().write(ident.getBytes("UTF-8"));//XXX: might probably contain ';'
   1.471 -                    ctx.getOut().write(';');
   1.472 -                }
   1.473 -            } catch (IOException ex) {
   1.474 -                throw new IllegalStateException(ex);
   1.475 -            }
   1.476 -        }
   1.477 -        if (cancel.get());
   1.478 -        new CollectIdentifiers<Void, Void>(new HashSet<String>(), cancel) {
   1.479 -            private boolean encode = true;
   1.480 -            @Override
   1.481 -            public Void scan(Tree t, Void v) {
   1.482 -                if (t == null) return null;
   1.483 -
   1.484 -                if (t instanceof StatementTree && Utilities.isMultistatementWildcardTree((StatementTree) t)) {
   1.485 -                    return null;
   1.486 -                }
   1.487 -
   1.488 -                boolean[] goDeeper = new boolean[1];
   1.489 -
   1.490 -                Input i = normalizeInput(t, goDeeper, null);
   1.491 -                try {
   1.492 -                    ctx.getOut().write('(');
   1.493 -                    ctx.getOut().write(kind2Encoded.get(i.kind));
   1.494 -                    if (!TO_IGNORE.contains(i.kind)) {
   1.495 -                        content.add(kind2EncodedString.get(i.kind));
   1.496 -                    }
   1.497 -                    if (i.name != null) {
   1.498 -                        if (encode) {
   1.499 -                            ctx.getOut().write('$');
   1.500 -                            ctx.getOut().write(i.name.getBytes("UTF-8"));
   1.501 -                            ctx.getOut().write(';');
   1.502 -                        }
   1.503 -                        if (isIdentifierAcceptable(i.name)) content.add(i.name);
   1.504 -                    }
   1.505 -
   1.506 -                    boolean oldEncode = encode;
   1.507 -
   1.508 -                    encode &= goDeeper[0];
   1.509 -                    super.scan(t, v);
   1.510 -                    encode = oldEncode;
   1.511 -
   1.512 -                    ctx.getOut().write(')');
   1.513 -                } catch (IOException ex) {
   1.514 -                    //XXX
   1.515 -                    Exceptions.printStackTrace(ex);
   1.516 -                }
   1.517 -
   1.518 -                return null;
   1.519 -            }
   1.520 -            @Override
   1.521 -            public Void scan(Iterable<? extends Tree> nodes, Void p) {
   1.522 -                try {
   1.523 -                    ctx.getOut().write('(');
   1.524 -                    ctx.getOut().write(kind2Encoded.get(Kind.IDENTIFIER));
   1.525 -                    ctx.getOut().write('$');
   1.526 -                    ctx.getOut().write('(');
   1.527 -                    ctx.getOut().write(';');
   1.528 -                    super.scan(nodes, p);
   1.529 -                    ctx.getOut().write(')');
   1.530 -                } catch (IOException ex) {
   1.531 -                    //XXX
   1.532 -                    Exceptions.printStackTrace(ex);
   1.533 -                }
   1.534 -                return null;
   1.535 -            }
   1.536 -        }.scan(tree, null);
   1.537 -
   1.538 -        ctx.setIdentifiers(identifiers);
   1.539 -        ctx.setContent(content);
   1.540 -        if (cancel.get());
   1.541 -    }
   1.542 -
   1.543 -    @Override
   1.544 -    public boolean matches(InputStream encoded, AtomicBoolean cancel, BulkPattern patternIn) {
   1.545 -        try {
   1.546 -            return !matchesImpl(encoded, cancel, patternIn, false).isEmpty();
   1.547 -        } catch (IOException ex) {
   1.548 -            Exceptions.printStackTrace(ex);
   1.549 -            return false;
   1.550 -        }
   1.551 -    }
   1.552 -
   1.553 -    @Override
   1.554 -    public Map<String, Integer> matchesWithFrequencies(InputStream encoded, BulkPattern patternIn, AtomicBoolean cancel) {
   1.555 -        try {
   1.556 -            return matchesImpl(encoded, cancel, patternIn, true);
   1.557 -        } catch (IOException ex) {
   1.558 -            Exceptions.printStackTrace(ex);
   1.559 -            return Collections.emptyMap();
   1.560 -        }
   1.561 -    }
   1.562 -
   1.563 -    public Map<String, Integer> matchesImpl(InputStream encoded, AtomicBoolean cancel, BulkPattern patternIn, boolean withFrequencies) throws IOException {
   1.564 -        BulkPatternImpl pattern = (BulkPatternImpl) patternIn;
   1.565 -        final NFA<Input, Res> nfa = pattern.toNFA();
   1.566 -        Stack<NFA.State> skips = new Stack<NFA.State>();
   1.567 -        NFA.State active = nfa.getStartingState();
   1.568 -        int identSize = 0;
   1.569 -
   1.570 -        identSize = encoded.read();
   1.571 -        identSize = (identSize << 8) + encoded.read();
   1.572 -        identSize = (identSize << 8) + encoded.read();
   1.573 -        identSize = (identSize << 8) + encoded.read();
   1.574 -
   1.575 -        Set<String> identifiers = new HashSet<String>(2 * identSize);
   1.576 -
   1.577 -        while (identSize-- > 0) {
   1.578 -            if (cancel.get()) return null;
   1.579 -            int read = encoded.read();
   1.580 -
   1.581 -            ByteArrayOutputStream baos = new ByteArrayOutputStream();
   1.582 -
   1.583 -            //read name:
   1.584 -            while (read != ';') {
   1.585 -                baos.write(read);
   1.586 -                read = encoded.read();
   1.587 -            }
   1.588 -
   1.589 -            identifiers.add(new String(baos.toByteArray(), "UTF-8"));
   1.590 -        }
   1.591 -
   1.592 -        Map<String, Integer> patternsAndFrequencies = new HashMap<String, Integer>();
   1.593 -        int read = encoded.read();
   1.594 -        
   1.595 -        while (read != (-1)) {
   1.596 -            if (cancel.get()) return null;
   1.597 -            if (read == '(') {
   1.598 -                read = encoded.read(); //kind
   1.599 -
   1.600 -                Kind k = encoded2Kind.get((read << 8) + encoded.read());
   1.601 -
   1.602 -                read = encoded.read();
   1.603 -
   1.604 -                String name;
   1.605 -
   1.606 -                if (read == '$') {
   1.607 -                    //XXX:
   1.608 -                    read = encoded.read();
   1.609 -
   1.610 -                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
   1.611 -
   1.612 -                    //read name:
   1.613 -                    while (read != ';') {
   1.614 -                        baos.write(read);
   1.615 -                        read = encoded.read();
   1.616 -                    }
   1.617 -
   1.618 -                    read = encoded.read();
   1.619 -                    name = new String(baos.toByteArray(), "UTF-8");
   1.620 -                } else {
   1.621 -                    name = null;
   1.622 -                }
   1.623 -                
   1.624 -                final NFA.State newActiveAfterVariable = nfa.transition(active, new Input(Kind.IDENTIFIER, "$", false));
   1.625 -                Input normalizedInput = new Input(k, name, false);
   1.626 -                boolean ignoreKind = normalizedInput.kind == Kind.IDENTIFIER || normalizedInput.kind == Kind.MEMBER_SELECT;
   1.627 -
   1.628 -                NFA.State newActive = nfa.transition(active, normalizedInput);
   1.629 -
   1.630 -                if (normalizedInput.name != null && !ignoreKind) {
   1.631 -                    newActive = nfa.join(newActive, nfa.transition(active, new Input(k, "$", false)));
   1.632 -                }
   1.633 -
   1.634 -                active = newActive;
   1.635 -
   1.636 -                skips.push(newActiveAfterVariable);
   1.637 -            } else {
   1.638 -                NFA.State newActiveAfterVariable = skips.pop();
   1.639 -                NFA.State newActive = nfa.transition(active, UP);
   1.640 -                NFA.State s2 = nfa.transition(newActiveAfterVariable, UP);
   1.641 -
   1.642 -                active = nfa.join(newActive, s2);
   1.643 -                
   1.644 -                for (Res res : nfa.getResults(active)) {
   1.645 -                    if (identifiers.containsAll(pattern.getIdentifiers().get(res.patternIndex))) {
   1.646 -                        if (!withFrequencies) {
   1.647 -                            patternsAndFrequencies.put(res.pattern, 1);
   1.648 -                            return patternsAndFrequencies;
   1.649 -                        }
   1.650 -                        
   1.651 -                        Integer freqs = patternsAndFrequencies.get(res.pattern);
   1.652 -
   1.653 -                        if (freqs == null) freqs = 0;
   1.654 -
   1.655 -                        patternsAndFrequencies.put(res.pattern, freqs + 1);
   1.656 -                    }
   1.657 -                }
   1.658 -
   1.659 -                read = encoded.read();
   1.660 -            }
   1.661 -        }
   1.662 -
   1.663 -        return patternsAndFrequencies;
   1.664 -    }
   1.665 -
   1.666 -    private static final Map<Kind, byte[]> kind2Encoded;
   1.667 -    private static final Map<Kind, String> kind2EncodedString;
   1.668 -    private static final Map<Integer, Kind> encoded2Kind;
   1.669 -
   1.670 -    static {
   1.671 -        kind2Encoded = new EnumMap<Kind, byte[]>(Kind.class);
   1.672 -        kind2EncodedString = new EnumMap<Kind, String>(Kind.class);
   1.673 -        encoded2Kind = new HashMap<Integer, Kind>();
   1.674 -
   1.675 -        for (Kind k : Kind.values()) {
   1.676 -            String enc = Integer.toHexString(k.ordinal());
   1.677 -
   1.678 -            if (enc.length() < 2) {
   1.679 -                enc = "0" + enc;
   1.680 -            }
   1.681 -
   1.682 -            try {
   1.683 -                final byte[] bytes = enc.getBytes("UTF-8");
   1.684 -
   1.685 -                assert bytes.length == 2;
   1.686 -
   1.687 -                kind2Encoded.put(k, bytes);
   1.688 -                kind2EncodedString.put(k, enc);
   1.689 -
   1.690 -                encoded2Kind.put((bytes[0] << 8) + bytes[1], k);
   1.691 -            } catch (UnsupportedEncodingException ex) {
   1.692 -                throw new IllegalStateException(ex);
   1.693 -            }
   1.694 -        }
   1.695 -    }
   1.696 -
   1.697 -    public static class BulkPatternImpl extends BulkPattern {
   1.698 -
   1.699 -        private final NFA<Input, Res> nfa;
   1.700 -
   1.701 -        private BulkPatternImpl(List<? extends String> patterns, List<? extends Set<? extends String>> identifiers, List<List<List<String>>> requiredContent, List<AdditionalQueryConstraints> additionalConstraints, NFA<Input, Res> nfa) {
   1.702 -            super(patterns, identifiers, requiredContent, additionalConstraints);
   1.703 -            this.nfa = nfa;
   1.704 -        }
   1.705 -
   1.706 -        NFA<Input, Res> toNFA() {
   1.707 -            return nfa;
   1.708 -        }
   1.709 -        
   1.710 -    }
   1.711 -
   1.712 -    private static final class Res {
   1.713 -        private final String pattern;
   1.714 -        private final int patternIndex;
   1.715 -
   1.716 -        public Res(String pattern, int patternIndex) {
   1.717 -            this.pattern = pattern;
   1.718 -            this.patternIndex = patternIndex;
   1.719 -        }
   1.720 -
   1.721 -    }
   1.722 -
   1.723 -    private static final class Input {
   1.724 -        private final Kind kind;
   1.725 -        private final String name;
   1.726 -        private final boolean end;
   1.727 -
   1.728 -        private Input(Kind kind, String name, boolean end) {
   1.729 -            this.kind = kind;
   1.730 -            this.name = name;
   1.731 -            this.end = end;
   1.732 -        }
   1.733 -
   1.734 -        @Override
   1.735 -        public boolean equals(Object obj) {
   1.736 -            if (obj == null) {
   1.737 -                return false;
   1.738 -            }
   1.739 -            if (getClass() != obj.getClass()) {
   1.740 -                return false;
   1.741 -            }
   1.742 -            final Input other = (Input) obj;
   1.743 -            if (this.kind != other.kind) {
   1.744 -                return false;
   1.745 -            }
   1.746 -            if ((this.name == null) ? (other.name != null) : !this.name.equals(other.name)) {
   1.747 -                return false;
   1.748 -            }
   1.749 -            if (this.end != other.end) {
   1.750 -                return false;
   1.751 -            }
   1.752 -            return true;
   1.753 -        }
   1.754 -
   1.755 -        @Override
   1.756 -        public int hashCode() {
   1.757 -            int hash = 7;
   1.758 -            hash = 47 * hash + (this.kind != null ? this.kind.hashCode() : 17);
   1.759 -            hash = 47 * hash + (this.name != null ? this.name.hashCode() : 0);
   1.760 -            hash = 47 * hash + (this.end ? 1 : 0);
   1.761 -            return hash;
   1.762 -        }
   1.763 -
   1.764 -        @Override
   1.765 -        public String toString() {
   1.766 -            return kind + ", " + name + ", " + end;
   1.767 -        }
   1.768 -
   1.769 -    }
   1.770 -
   1.771 -    private static final Input UP = new Input(null, null, true);
   1.772 -
   1.773 -    private static boolean isIdentifierAcceptable(CharSequence content) {
   1.774 -        if (content.length() == 0) return false;
   1.775 -        if (content.charAt(0) == '$' || content.charAt(0) == '<') return false;
   1.776 -        String stringValue = content.toString();
   1.777 -        if (stringValue.contentEquals("java") || "lang".equals(stringValue)) return false;
   1.778 -        return true;
   1.779 -    }
   1.780 -
   1.781 -    private static class CollectIdentifiers<R, P> extends CancellableTreeScanner<R, P> {
   1.782 -
   1.783 -        private final Set<String> identifiers;
   1.784 -
   1.785 -        public CollectIdentifiers(Set<String> identifiers, AtomicBoolean cancel) {
   1.786 -            super(cancel);
   1.787 -            this.identifiers = identifiers;
   1.788 -        }
   1.789 -
   1.790 -        private void addIdentifier(Name ident) {
   1.791 -            if (!isIdentifierAcceptable(ident)) return;
   1.792 -            identifiers.add(ident.toString());
   1.793 -        }
   1.794 -
   1.795 -        @Override
   1.796 -        public R visitMemberSelect(MemberSelectTree node, P p) {
   1.797 -            addIdentifier(node.getIdentifier());
   1.798 -            return super.visitMemberSelect(node, p);
   1.799 -        }
   1.800 -
   1.801 -        @Override
   1.802 -        public R visitIdentifier(IdentifierTree node, P p) {
   1.803 -            addIdentifier(node.getName());
   1.804 -            return super.visitIdentifier(node, p);
   1.805 -        }
   1.806 -
   1.807 -        @Override
   1.808 -        public R visitClass(ClassTree node, P p) {
   1.809 -            if (node.getSimpleName().length() == 0) {
   1.810 -                return scan(Utilities.filterHidden(null, node.getMembers()), p);
   1.811 -            }
   1.812 -            addIdentifier(node.getSimpleName());
   1.813 -            return super.visitClass(node, p);
   1.814 -        }
   1.815 -
   1.816 -        @Override
   1.817 -        public R visitMethod(MethodTree node, P p) {
   1.818 -            addIdentifier(node.getName());
   1.819 -            return super.visitMethod(node, p);
   1.820 -        }
   1.821 -
   1.822 -        @Override
   1.823 -        public R visitVariable(VariableTree node, P p) {
   1.824 -            addIdentifier(node.getName());
   1.825 -            return super.visitVariable(node, p);
   1.826 -        }
   1.827 -
   1.828 -    }
   1.829 -}