/******************************************************************************* * Copyright (c) 2000, 2009 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.ASTNode; 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. */ 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(); } }