/******************************************************************************* * Copyright (c) 2000, 2013 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 * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.core.search.matching; import java.util.ArrayList; import org.eclipse.jdt.core.search.SearchMatch; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.util.HashtableOfLong; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.core.util.Util; /** * A set of matches and possible matches, which need to be resolved. */ @SuppressWarnings({"rawtypes", "unchecked"}) 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. */ SimpleLookupTable matchingNodes = new SimpleLookupTable(3); // node -> accuracy private HashtableOfLong matchingNodesKeys = new HashtableOfLong(3); // sourceRange -> node static Integer EXACT_MATCH = new Integer(SearchMatch.A_ACCURATE); static Integer POTENTIAL_MATCH = new Integer(SearchMatch.A_INACCURATE); static Integer ERASURE_MATCH = new Integer(SearchPattern.R_ERASURE_MATCH); /** * Tell whether locators need to resolve or not for current matching node set. */ public boolean mustResolve; /** * Set of possible matching ast nodes. They need to be resolved * to determine if they really match the search pattern. */ SimpleSet possibleMatchingNodesSet = new SimpleSet(7); private HashtableOfLong possibleMatchingNodesKeys = new HashtableOfLong(7); public MatchingNodeSet(boolean mustResolvePattern) { super(); this.mustResolve = mustResolvePattern; } public int addMatch(ASTNode node, int matchLevel) { int maskedLevel = matchLevel & PatternLocator.MATCH_LEVEL_MASK; switch (maskedLevel) { case PatternLocator.INACCURATE_MATCH: if (matchLevel != maskedLevel) { addTrustedMatch(node, new Integer(SearchMatch.A_INACCURATE+(matchLevel & PatternLocator.FLAVORS_MASK))); } else { addTrustedMatch(node, POTENTIAL_MATCH); } break; case PatternLocator.POSSIBLE_MATCH: addPossibleMatch(node); break; case PatternLocator.ERASURE_MATCH: if (matchLevel != maskedLevel) { addTrustedMatch(node, new Integer(SearchPattern.R_ERASURE_MATCH+(matchLevel & PatternLocator.FLAVORS_MASK))); } else { addTrustedMatch(node, ERASURE_MATCH); } break; case PatternLocator.ACCURATE_MATCH: if (matchLevel != maskedLevel) { addTrustedMatch(node, new Integer(SearchMatch.A_ACCURATE+(matchLevel & PatternLocator.FLAVORS_MASK))); } else { addTrustedMatch(node, EXACT_MATCH); } break; } 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 = (((long) node.sourceStart) << 32) + node.sourceEnd; 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 = (((long) node.sourceStart) << 32) + node.sourceEnd; 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. */ protected 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 = new Util.Comparer() { public int compare(Object o1, Object o2) { return ((ASTNode) o1).sourceStart - ((ASTNode) o2).sourceStart; } }; Util.sort(result, comparer); return result; } public Object removePossibleMatch(ASTNode node) { long key = (((long) node.sourceStart) << 32) + node.sourceEnd; ASTNode existing = (ASTNode) this.possibleMatchingNodesKeys.get(key); if (existing == null) return null; this.possibleMatchingNodesKeys.put(key, null); return this.possibleMatchingNodesSet.remove(node); } public Object removeTrustedMatch(ASTNode node) { long key = (((long) node.sourceStart) << 32) + node.sourceEnd; ASTNode existing = (ASTNode) this.matchingNodesKeys.get(key); if (existing == null) return null; this.matchingNodesKeys.put(key, null); return this.matchingNodes.removeKey(node); } 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); } 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); } return result.toString(); } }