
org.apache.tapestry5.ioc.internal.util.Orderer Maven / Gradle / Ivy
The newest version!
// Copyright 2006, 2007, 2009 The Apache Software Foundation
//
// Licensed 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.tapestry5.ioc.internal.util;
import org.apache.tapestry5.commons.util.CollectionFactory;
import org.apache.tapestry5.ioc.IdMatcher;
import org.apache.tapestry5.ioc.Orderable;
import org.apache.tapestry5.ioc.internal.IdMatcherImpl;
import org.apache.tapestry5.ioc.internal.OrIdMatcher;
import org.slf4j.Logger;
import static org.apache.tapestry5.commons.util.CollectionFactory.newList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Used to order objects into an "execution" order. Each object must have a unique id. It may specify a list of
* constraints which identify the ordering of the objects.
*/
public class Orderer
{
private final OneShotLock lock = new OneShotLock();
private final Logger logger;
private final boolean exceptionWhenDuplicateId;
private final List orderables = CollectionFactory.newList();
private final Map> idToOrderable = CollectionFactory.newCaseInsensitiveMap();
private final Map> dependencyNodesById = CollectionFactory.newCaseInsensitiveMap();
// Special node that is always dead last: all other nodes are a dependency
// of the trailer.
private DependencyNode trailer;
interface DependencyLinker
{
void link(DependencyNode source, DependencyNode target);
}
// before: source is added as a dependency of target, so source will
// appear before target.
final DependencyLinker _before = new DependencyLinker()
{
@Override
public void link(DependencyNode source, DependencyNode target)
{
target.addDependency(source);
}
};
// after: target is added as a dependency of source, so source will appear
// after target.
final DependencyLinker _after = new DependencyLinker()
{
@Override
public void link(DependencyNode source, DependencyNode target)
{
source.addDependency(target);
}
};
public Orderer(Logger logger)
{
this(logger, false);
}
public Orderer(Logger logger, boolean exceptionWhenDuplicateId)
{
this.logger = logger;
this.exceptionWhenDuplicateId = exceptionWhenDuplicateId;
}
/**
* Adds an object to be ordered.
*
* @param orderable
*/
public void add(Orderable orderable)
{
lock.check();
String id = orderable.getId();
if (idToOrderable.containsKey(id))
{
final String message = UtilMessages.duplicateOrderer(id);
if (exceptionWhenDuplicateId) {
throw new IllegalArgumentException(message);
}
else {
logger.warn(message);
return;
}
}
orderables.add(orderable);
idToOrderable.put(id, orderable);
}
public void override(Orderable orderable)
{
lock.check();
String id = orderable.getId();
Orderable existing = idToOrderable.get(id);
if (existing == null)
throw new IllegalArgumentException(
String.format("Override for object '%s' is invalid as it does not match an existing object.", id));
orderables.remove(existing);
orderables.add(orderable);
idToOrderable.put(id, orderable);
}
/**
* Adds an object to be ordered.
*
* @param id unique, qualified id for the target
* @param target the object to be ordered (or null as a placeholder)
* @param constraints optional, variable constraints
* @see #add(Orderable)
*/
public void add(String id, T target, String... constraints)
{
lock.check();
add(new Orderable(id, target, constraints));
}
public void override(String id, T target, String... constraints)
{
lock.check();
override(new Orderable(id, target, constraints));
}
public List getOrdered()
{
lock.lock();
initializeGraph();
List result = newList();
for (Orderable orderable : trailer.getOrdered())
{
T target = orderable.getTarget();
// Nulls are placeholders that are skipped.
if (target != null) result.add(target);
}
return result;
}
private void initializeGraph()
{
trailer = new DependencyNode(logger, new Orderable("*-trailer-*", null));
addNodes();
addDependencies();
}
private void addNodes()
{
for (Orderable orderable : orderables)
{
DependencyNode node = new DependencyNode(logger, orderable);
dependencyNodesById.put(orderable.getId(), node);
trailer.addDependency(node);
}
}
private void addDependencies()
{
for (Orderable orderable : orderables)
{
addDependencies(orderable);
}
}
private void addDependencies(Orderable orderable)
{
String sourceId = orderable.getId();
for (String constraint : orderable.getConstraints())
{
addDependencies(sourceId, constraint);
}
}
private void addDependencies(String sourceId, String constraint)
{
int colonx = constraint.indexOf(':');
String type = colonx > 0 ? constraint.substring(0, colonx) : null;
DependencyLinker linker = null;
if ("after".equals(type))
linker = _after;
else if ("before".equals(type)) linker = _before;
if (linker == null)
{
logger.warn(UtilMessages.constraintFormat(constraint, sourceId));
return;
}
String patternList = constraint.substring(colonx + 1);
linkNodes(sourceId, patternList, linker);
}
private void linkNodes(String sourceId, String patternList, DependencyLinker linker)
{
Collection> nodes = findDependencies(sourceId, patternList);
DependencyNode source = dependencyNodesById.get(sourceId);
for (DependencyNode target : nodes)
{
linker.link(source, target);
}
}
private Collection> findDependencies(String sourceId, String patternList)
{
IdMatcher matcher = buildMatcherForPattern(patternList);
Collection> result = newList();
for (String id : dependencyNodesById.keySet())
{
if (sourceId.equals(id)) continue;
if (matcher.matches(id)) result.add(dependencyNodesById.get(id));
}
return result;
}
private IdMatcher buildMatcherForPattern(String patternList)
{
List matchers = newList();
for (String pattern : patternList.split(","))
{
IdMatcher matcher = new IdMatcherImpl(pattern.trim());
matchers.add(matcher);
}
return matchers.size() == 1 ? matchers.get(0) : new OrIdMatcher(matchers);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy