/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * *******************************************************************************/ package org.eclipse.dltk.internal.core.search.matching; import java.util.ArrayList; import org.eclipse.dltk.ast.ASTNode; import org.eclipse.dltk.compiler.util.HashtableOfLong; import org.eclipse.dltk.compiler.util.SimpleLookupTable; import org.eclipse.dltk.compiler.util.SimpleSet; import org.eclipse.dltk.core.DLTKCore; import org.eclipse.dltk.core.search.SearchMatch; import org.eclipse.dltk.core.search.SearchPattern; import org.eclipse.dltk.core.search.matching.PatternLocator; import org.eclipse.dltk.internal.core.util.Util; /** * A set of matches and possible matches, which need to be resolved. */ public class MatchingNodeSet { /** * Map of matching ast nodes that don't need to be resolved to their * accuracy level. Each node is removed as it is reported. */ public SimpleLookupTable matchingNodes = new SimpleLookupTable(3); // node // -> // accuracy private HashtableOfLong matchingNodesKeys = new HashtableOfLong(3); // sourceRange // -> // node static final Integer EXACT_MATCH = Integer.valueOf(SearchMatch.A_ACCURATE); static final Integer POTENTIAL_MATCH = Integer.valueOf(SearchMatch.A_INACCURATE); static final Integer ERASURE_MATCH = Integer.valueOf(SearchPattern.R_ERASURE_MATCH); /** * Set of possible matching ast nodes. They need to be resolved to determine * if they really match the search pattern. */ public SimpleSet possibleMatchingNodesSet = new SimpleSet(7); private HashtableOfLong possibleMatchingNodesKeys = new HashtableOfLong(7); public MatchingNodeSet() { super(); } public int addMatch(ASTNode node, int matchLevel) { switch (matchLevel & PatternLocator.NODE_SET_MASK) { case PatternLocator.INACCURATE_MATCH: addTrustedMatch(node, POTENTIAL_MATCH); break; case PatternLocator.POSSIBLE_MATCH: addPossibleMatch(node); break; case PatternLocator.ERASURE_MATCH: addTrustedMatch(node, ERASURE_MATCH); break; case PatternLocator.ACCURATE_MATCH: addTrustedMatch(node, EXACT_MATCH); } return matchLevel; } public void addPossibleMatch(ASTNode node) { // remove existing node at same position from set // (case of recovery that created the same node several time // see http://bugs.eclipse.org/bugs/show_bug.cgi?id=29366) long key = computeNodeKey(node); ASTNode existing = (ASTNode) this.possibleMatchingNodesKeys.get(key); if (existing != null && existing.getClass().equals(node.getClass())) this.possibleMatchingNodesSet.remove(existing); // add node to set this.possibleMatchingNodesSet.add(node); this.possibleMatchingNodesKeys.put(key, node); } public void addTrustedMatch(ASTNode node, boolean isExact) { addTrustedMatch(node, isExact ? EXACT_MATCH : POTENTIAL_MATCH); } void addTrustedMatch(ASTNode node, Integer level) { // remove existing node at same position from set // (case of recovery that created the same node several time // see http://bugs.eclipse.org/bugs/show_bug.cgi?id=29366) long key = computeNodeKey(node); ASTNode existing = (ASTNode) this.matchingNodesKeys.get(key); if (existing != null && existing.getClass().equals(node.getClass())) this.matchingNodes.removeKey(existing); // map node to its accuracy level this.matchingNodes.put(node, level); this.matchingNodesKeys.put(key, node); } protected boolean hasPossibleNodes(int start, int end) { Object[] nodes = this.possibleMatchingNodesSet.values; for (int i = 0, l = nodes.length; i < l; i++) { ASTNode node = (ASTNode) nodes[i]; if (node != null && start <= node.sourceStart() && node.sourceEnd() <= end) return true; } nodes = this.matchingNodes.keyTable; for (int i = 0, l = nodes.length; i < l; i++) { ASTNode node = (ASTNode) nodes[i]; if (node != null && start <= node.sourceStart() && node.sourceEnd() <= end) return true; } return false; } /** * Returns the matching nodes that are in the given range in the source * order. */ public ASTNode[] matchingNodes(int start, int end) { ArrayList nodes = null; Object[] keyTable = this.matchingNodes.keyTable; for (int i = 0, l = keyTable.length; i < l; i++) { ASTNode node = (ASTNode) keyTable[i]; if (node != null && start <= node.sourceStart() && node.sourceEnd() <= end) { if (nodes == null) nodes = new ArrayList(); nodes.add(node); } } if (nodes == null) return null; ASTNode[] result = new ASTNode[nodes.size()]; nodes.toArray(result); // sort nodes by source starts Util.Comparer comparer = (o1, o2) -> ((ASTNode) o1).sourceStart() - ((ASTNode) o2).sourceStart(); Util.sort(result, comparer); return result; } public Object removePossibleMatch(ASTNode node) { long key = computeNodeKey(node); ASTNode existing = (ASTNode) this.possibleMatchingNodesKeys.get(key); if (existing == null) return null; this.possibleMatchingNodesKeys.put(key, null); return this.possibleMatchingNodesSet.remove(node); } private long computeNodeKey(ASTNode node) { return ((((long) node.sourceStart()) << 32) + node.sourceEnd()); } public Object removeTrustedMatch(ASTNode node) { long key = computeNodeKey(node); ASTNode existing = (ASTNode) this.matchingNodesKeys.get(key); if (existing == null) return null; this.matchingNodesKeys.put(key, null); return this.matchingNodes.removeKey(node); } @Override public String toString() { // TODO (jerome) should show both tables StringBuffer result = new StringBuffer(); result.append("Exact matches:"); //$NON-NLS-1$ Object[] keyTable = this.matchingNodes.keyTable; Object[] valueTable = this.matchingNodes.valueTable; for (int i = 0, l = keyTable.length; i < l; i++) { ASTNode node = (ASTNode) keyTable[i]; if (node == null) continue; result.append("\n\t"); //$NON-NLS-1$ switch (((Integer) valueTable[i]).intValue()) { case SearchMatch.A_ACCURATE: result.append("ACCURATE_MATCH: "); //$NON-NLS-1$ break; case SearchMatch.A_INACCURATE: result.append("INACCURATE_MATCH: "); //$NON-NLS-1$ break; case SearchPattern.R_ERASURE_MATCH: result.append("ERASURE_MATCH: "); //$NON-NLS-1$ break; } // node.print(0, result); if (DLTKCore.DEBUG) { System.err.println("TODO: Add node print..."); //$NON-NLS-1$ } } result.append("\nPossible matches:"); //$NON-NLS-1$ Object[] nodes = this.possibleMatchingNodesSet.values; for (int i = 0, l = nodes.length; i < l; i++) { ASTNode node = (ASTNode) nodes[i]; if (node == null) continue; result.append("\nPOSSIBLE_MATCH: "); //$NON-NLS-1$ // node.print(0, result); if (DLTKCore.DEBUG) { System.err.println("TODO: Add node print..."); //$NON-NLS-1$ } } return result.toString(); } }