| /******************************************************************************* |
| * Copyright (c) 2000, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-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.internal.compiler.ast.*; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfLong; |
| import org.eclipse.jdt.internal.core.util.SimpleLookupTable; |
| import org.eclipse.jdt.internal.core.util.SimpleSet; |
| import org.eclipse.jdt.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. |
| */ |
| 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); |
| |
| /** |
| * 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 int addMatch(ASTNode node, int matchLevel) { |
| switch (matchLevel) { |
| case PatternLocator.INACCURATE_MATCH: |
| addTrustedMatch(node, false); |
| break; |
| case PatternLocator.POSSIBLE_MATCH: |
| addPossibleMatch(node); |
| break; |
| case PatternLocator.ACCURATE_MATCH: |
| addTrustedMatch(node, true); |
| } |
| 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) { |
| // 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, isExact ? EXACT_MATCH : POTENTIAL_MATCH); |
| 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$ |
| result.append(valueTable[i] == EXACT_MATCH |
| ? "ACCURATE_MATCH: " //$NON-NLS-1$ |
| : "INACCURATE_MATCH: "); //$NON-NLS-1$ |
| 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(); |
| } |
| } |