blob: e089e633d8be24a045c910009236bbde41702383 [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.core.search.matching2;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A set of matches and possible matches, which need to be resolved.
*/
public abstract class MatchingNodeSet<E>
implements IMatchingNodeSet<E>, Comparator<E> {
/**
* Map of matching ast nodes that don't need to be resolved to their
* accuracy level. Each node is removed as it is reported.
*/
private Map<E, MatchLevel> matchingNodes = new HashMap<>();
// sourceRange -> node
private HashtableOfLong<E> matchingNodesKeys = new HashtableOfLong<>();
/**
* Set of possible matching ast nodes. They need to be resolved to determine
* if they really match the search pattern.
*/
private Set<E> possibleMatchingNodes = new HashSet<>();
// sourceRange -> node
private HashtableOfLong<E> possibleMatchingNodesKeys = new HashtableOfLong<>();
@Override
public MatchLevel addMatch(E node, MatchLevel matchLevel) {
switch (matchLevel) {
case POSSIBLE_MATCH:
addPossibleMatch(node);
break;
case INACCURATE_MATCH:
addTrustedMatch(node, MatchLevel.INACCURATE_MATCH);
break;
case ACCURATE_MATCH:
addTrustedMatch(node, MatchLevel.ACCURATE_MATCH);
break;
}
return matchLevel;
}
public void addPossibleMatch(E 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);
E existing = this.possibleMatchingNodesKeys.get(key);
if (existing != null && existing.getClass().equals(node.getClass()))
this.possibleMatchingNodes.remove(existing);
// add node to set
this.possibleMatchingNodes.add(node);
this.possibleMatchingNodesKeys.put(key, node);
}
private void addTrustedMatch(E node, MatchLevel 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);
E existing = this.matchingNodesKeys.get(key);
if (existing != null && existing.getClass().equals(node.getClass()))
this.matchingNodes.remove(existing);
// map node to its accuracy level
this.matchingNodes.put(node, level);
this.matchingNodesKeys.put(key, node);
}
protected boolean hasPossibleNodes(int start, int end) {
for (E node : possibleMatchingNodes) {
if (checkRange(node, start, end)) {
return true;
}
}
for (E node : matchingNodes.keySet()) {
if (checkRange(node, start, end)) {
return true;
}
}
return false;
}
/**
* Returns the matching nodes that are in the given range in the source
* order.
*/
public List<E> matchingNodes(int start, int end) {
List<E> nodes = null;
for (E node : matchingNodes.keySet()) {
if (checkRange(node, start, end)) {
if (nodes == null)
nodes = new ArrayList<>();
nodes.add(node);
}
}
if (nodes == null)
return Collections.emptyList();
Collections.sort(nodes, this);
return nodes;
}
/**
* Returns all the matching nodes
*/
public List<E> matchingNodes() {
final List<E> nodes = new ArrayList<>(matchingNodes.keySet());
Collections.sort(nodes, this);
return nodes;
}
public boolean removePossibleMatch(E node) {
long key = computeNodeKey(node);
E existing = this.possibleMatchingNodesKeys.get(key);
if (existing == null)
return false;
this.possibleMatchingNodesKeys.put(key, null);
return this.possibleMatchingNodes.remove(node);
}
protected abstract long computeNodeKey(E node);
protected abstract boolean checkRange(E node, int start, int end);
public MatchLevel removeTrustedMatch(E node) {
long key = computeNodeKey(node);
E existing = this.matchingNodesKeys.get(key);
if (existing == null)
return null;
this.matchingNodesKeys.put(key, null);
return this.matchingNodes.remove(node);
}
@Override
public String toString() {
StringBuilder result = new StringBuilder();
result.append("Exact matches:"); //$NON-NLS-1$
List<E> nodes = new ArrayList<>(matchingNodes.keySet());
Collections.sort(nodes, this);
for (E node : nodes) {
result.append("\n\t"); //$NON-NLS-1$
result.append(matchingNodes.get(node));
result.append(' ');
result.append(describeNode(node));
}
result.append("\nPossible matches:"); //$NON-NLS-1$
nodes.clear();
nodes.addAll(possibleMatchingNodes);
Collections.sort(nodes, this);
for (E node : nodes) {
result.append("\nPOSSIBLE_MATCH: "); //$NON-NLS-1$
result.append(describeNode(node));
}
return result.toString();
}
protected String describeNode(E node) {
return node.toString();
}
public void clear() {
matchingNodes.clear();
matchingNodesKeys.clear();
clearPossibleMatchingNodes();
// possibleMatchingNodes.clear();
// possibleMatchingNodesKeys.clear();
}
public void clearPossibleMatchingNodes() {
possibleMatchingNodes.clear();
possibleMatchingNodesKeys.clear();
}
public Collection<E> getPossibleMatchingNodes() {
return possibleMatchingNodes;
}
public int countMatchingNodes() {
return matchingNodes.size();
}
public int countPossibleMatchingNodes() {
return possibleMatchingNodes.size();
}
/**
* @return
*/
public boolean isEmpty() {
return matchingNodes.isEmpty() && possibleMatchingNodes.isEmpty();
}
}