blob: 0bb46280e1ae26d754a4976563ac6236526442b4 [file] [log] [blame]
/*******************************************************************************
* 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();
}
}