blob: 8c148af61aab9db281b7b766efb972f831322723 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011 VMware Inc.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* SpringSource, a division of VMware - initial API and implementation and/or initial documentation
*******************************************************************************/
package org.eclipse.equinox.internal.region.hook;
import java.util.*;
import org.eclipse.equinox.region.*;
/**
* {@link RegionDigraphVisitorBase} is an abstract base class for {@link RegionDigraphVisitor} implementations in the
* framework hooks.
* <p />
*
* <strong>Concurrent Semantics</strong><br />
* This class is thread safe.
*/
abstract class RegionDigraphVisitorBase<C> implements RegionDigraphVisitor {
private final Collection<C> allCandidates;
private final Stack<Set<C>> allowedStack = new Stack<Set<C>>();
private final Stack<Collection<C>> filteredStack = new Stack<Collection<C>>();
private Object monitor = new Object();
private Set<C> allowed;
protected RegionDigraphVisitorBase(Collection<C> candidates) {
this.allCandidates = candidates;
synchronized (this.monitor) {
this.allowed = new HashSet<C>();
}
}
Collection<C> getAllowed() {
synchronized (this.monitor) {
return this.allowed;
}
}
private void allow(C candidate) {
synchronized (this.monitor) {
this.allowed.add(candidate);
}
}
private void allow(Collection<C> candidates) {
synchronized (this.monitor) {
this.allowed.addAll(candidates);
}
}
private void pushAllowed() {
synchronized (this.monitor) {
this.allowedStack.push(this.allowed);
this.allowed = new HashSet<C>();
}
}
private Collection<C> popAllowed() {
synchronized (this.monitor) {
Collection<C> a = this.allowed;
this.allowed = this.allowedStack.pop();
return a;
}
}
private void pushFiltered(Collection<C> filtered) {
synchronized (this.monitor) {
this.filteredStack.push(filtered);
}
}
private Collection<C> popFiltered() {
synchronized (this.monitor) {
return this.filteredStack.isEmpty() ? allCandidates : this.filteredStack.pop();
}
}
private Collection<C> peekFiltered() {
synchronized (this.monitor) {
return this.filteredStack.isEmpty() ? allCandidates : this.filteredStack.peek();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(Region region) {
Collection<C> candidates = peekFiltered();
for (C candidate : candidates) {
if (contains(region, candidate)) {
allow(candidate);
}
}
if (allowed.containsAll(candidates)) {
// there is no need to traverse edges of this region,
// it contains all the remaining filtered candidates
return false;
}
return true;
}
/**
* Determines whether the given region contains the given candidate.
*
* @param region the {@link Region}
* @param candidate the candidate
* @return <code>true</code> if and only if the given region contains the given candidate
*/
protected abstract boolean contains(Region region, C candidate);
/**
* {@inheritDoc}
*/
@Override
public boolean preEdgeTraverse(RegionFilter regionFilter) {
// Find the candidates filtered by the previous edge
Collection<C> candidates = new ArrayList<C>(peekFiltered());
// remove any candidates contained in the current region
candidates.removeAll(allowed);
// apply the filter across remaining candidates
filter(candidates, regionFilter);
if (candidates.isEmpty())
return false; // this filter does not apply; avoid traversing this edge
// push the filtered candidates for the next region
pushFiltered(candidates);
// push the allowed
pushAllowed();
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void postEdgeTraverse(RegionFilter regionFilter) {
popFiltered();
Collection<C> candidates = popAllowed();
allow(candidates);
}
private void filter(Collection<C> candidates, RegionFilter filter) {
Iterator<C> i = candidates.iterator();
while (i.hasNext()) {
C candidate = i.next();
if (!isAllowed(candidate, filter)) {
i.remove();
}
}
}
/**
* Determines whether the given candidate is allowed by the given {@link RegionFilter}.
*
* @param candidate the candidate
* @param filter the filter
* @return <code>true</code> if and only if the given candidate is allowed by the given filter
*/
protected abstract boolean isAllowed(C candidate, RegionFilter filter);
}