blob: 698db1ba175c83a8ce8842170c71fff41ff87da6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.karaf.features.internal.region;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.equinox.region.Region;
import org.eclipse.equinox.region.RegionDigraphVisitor;
import org.eclipse.equinox.region.RegionFilter;
/**
* {@link AbstractRegionDigraphVisitor} is an abstract base class for {@link RegionDigraphVisitor} implementations
*/
public abstract class AbstractRegionDigraphVisitor<C> implements RegionDigraphVisitor {
private final Collection<C> allCandidates;
private final Deque<Set<C>> allowedDeque = new ArrayDeque<Set<C>>();
private final Deque<Collection<C>> filteredDeque = new ArrayDeque<Collection<C>>();
private Set<C> allowed = new HashSet<C>();
public AbstractRegionDigraphVisitor(Collection<C> candidates) {
this.allCandidates = candidates;
}
public Collection<C> getAllowed() {
return allowed;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(Region region) {
Collection<C> candidates = filteredDeque.isEmpty() ? allCandidates : filteredDeque.peek();
for (C candidate : candidates) {
if (contains(region, candidate)) {
allowed.add(candidate);
}
}
// there is no need to traverse edges of this region,
// it contains all the remaining filtered candidates
return !allowed.containsAll(candidates);
}
/**
* {@inheritDoc}
*/
@Override
public boolean preEdgeTraverse(RegionFilter regionFilter) {
// Find the candidates filtered by the previous edge
Collection<C> filtered = filteredDeque.isEmpty() ? allCandidates : filteredDeque.peek();
Collection<C> candidates = new ArrayList<C>(filtered);
// remove any candidates contained in the current region
candidates.removeAll(allowed);
// apply the filter across remaining candidates
Iterator<C> i = candidates.iterator();
while (i.hasNext()) {
C candidate = i.next();
if (!isAllowed(candidate, regionFilter)) {
i.remove();
}
}
if (candidates.isEmpty()) {
return false; // this filter does not apply; avoid traversing this edge
}
// push the filtered candidates for the next region
filteredDeque.push(candidates);
// push the allowed
allowedDeque.push(allowed);
allowed = new HashSet<C>();
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void postEdgeTraverse(RegionFilter regionFilter) {
filteredDeque.poll();
Collection<C> candidates = allowed;
allowed = allowedDeque.pop();
allowed.addAll(candidates);
}
/**
* 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);
/**
* 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);
}