sandbox/java.hints/spi.java.hints/src/org/netbeans/modules/java/hints/spiimpl/pm/NFABasedBulkSearch.java
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 -}