001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.ioc.internal.util;
016:
017: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
018: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
019:
020: import java.util.Collection;
021: import java.util.List;
022: import java.util.Map;
023:
024: import org.apache.commons.logging.Log;
025: import org.apache.tapestry.ioc.IdMatcher;
026: import org.apache.tapestry.ioc.Orderable;
027: import org.apache.tapestry.ioc.internal.IdMatcherImpl;
028: import org.apache.tapestry.ioc.internal.OrIdMatcher;
029:
030: /**
031: * Used to order objects into an "execution" order. Each object must have a unique id. It may
032: * specify a list of constraints which identify the ordering of the objects.
033: */
034: public class Orderer<T> {
035: private final OneShotLock _lock = new OneShotLock();
036:
037: private final Log _log;
038:
039: private final List<Orderable> _orderables = newList();
040:
041: private final Map<String, Orderable<T>> _orderablesById = newCaseInsensitiveMap();
042:
043: private final Map<String, DependencyNode<T>> _dependencyNodesById = newCaseInsensitiveMap();
044:
045: // Special node that is always dead last: all other nodes are a dependency
046: // of the trailer.
047:
048: private DependencyNode<T> _trailer;
049:
050: interface DependencyLinker<T> {
051: void link(DependencyNode<T> source, DependencyNode<T> target);
052: }
053:
054: // before: source is added as a dependency of target, so source will
055: // appear before target.
056:
057: final DependencyLinker<T> _before = new DependencyLinker<T>() {
058: public void link(DependencyNode<T> source,
059: DependencyNode<T> target) {
060: target.addDependency(source);
061: }
062: };
063:
064: // after: target is added as a dependency of source, so source will appear
065: // after target.
066:
067: final DependencyLinker<T> _after = new DependencyLinker<T>() {
068: public void link(DependencyNode<T> source,
069: DependencyNode<T> target) {
070: source.addDependency(target);
071: }
072: };
073:
074: public Orderer(Log log) {
075: _log = log;
076: }
077:
078: /**
079: * Adds an object to be ordered.
080: *
081: * @param orderable
082: */
083: public void add(Orderable<T> orderable) {
084: _lock.check();
085:
086: String id = orderable.getId();
087:
088: if (_orderablesById.containsKey(id)) {
089: _log.warn(UtilMessages.duplicateOrderer(id), null);
090: return;
091: }
092:
093: _orderables.add(orderable);
094:
095: _orderablesById.put(id, orderable);
096: }
097:
098: /**
099: * Adds an object to be ordered.
100: *
101: * @param id
102: * unique, qualified id for the target
103: * @param target
104: * the object to be ordered (or null as a placeholder)
105: * @param constraints
106: * optional, variable constraints
107: * @see #add(Orderable)
108: */
109:
110: public void add(String id, T target, String... constraints) {
111: _lock.check();
112:
113: add(new Orderable<T>(id, target, constraints));
114: }
115:
116: public List<T> getOrdered() {
117: _lock.lock();
118:
119: initializeGraph();
120:
121: List<T> result = newList();
122:
123: for (Orderable<T> orderable : _trailer.getOrdered()) {
124: T target = orderable.getTarget();
125:
126: // Nulls are placeholders that are skipped.
127:
128: if (target != null)
129: result.add(target);
130: }
131:
132: return result;
133: }
134:
135: private void initializeGraph() {
136: _trailer = new DependencyNode<T>(_log, new Orderable<T>(
137: "*-trailer-*", null));
138:
139: addNodes();
140:
141: addDependencies();
142: }
143:
144: private void addNodes() {
145: for (Orderable<T> orderable : _orderables) {
146: DependencyNode<T> node = new DependencyNode<T>(_log,
147: orderable);
148:
149: _dependencyNodesById.put(orderable.getId(), node);
150:
151: _trailer.addDependency(node);
152: }
153: }
154:
155: private void addDependencies() {
156: for (Orderable<T> orderable : _orderables) {
157: addDependencies(orderable);
158: }
159: }
160:
161: private void addDependencies(Orderable<T> orderable) {
162: String sourceId = orderable.getId();
163:
164: for (String constraint : orderable.getConstraints()) {
165: addDependencies(sourceId, constraint);
166: }
167: }
168:
169: private void addDependencies(String sourceId, String constraint) {
170: int colonx = constraint.indexOf(':');
171:
172: String type = colonx > 0 ? constraint.substring(0, colonx)
173: : null;
174:
175: DependencyLinker<T> linker = null;
176:
177: if ("after".equals(type))
178: linker = _after;
179: else if ("before".equals(type))
180: linker = _before;
181:
182: if (linker == null) {
183: _log.warn(UtilMessages.constraintFormat(constraint,
184: sourceId));
185: return;
186: }
187:
188: String patternList = constraint.substring(colonx + 1);
189:
190: linkNodes(sourceId, patternList, linker);
191: }
192:
193: private void linkNodes(String sourceId, String patternList,
194: DependencyLinker<T> linker) {
195: Collection<DependencyNode<T>> nodes = findDependencies(
196: sourceId, patternList);
197:
198: DependencyNode<T> source = _dependencyNodesById.get(sourceId);
199:
200: for (DependencyNode<T> target : nodes) {
201: linker.link(source, target);
202: }
203: }
204:
205: private Collection<DependencyNode<T>> findDependencies(
206: String sourceId, String patternList) {
207: IdMatcher matcher = buildMatcherForPattern(patternList);
208:
209: Collection<DependencyNode<T>> result = newList();
210:
211: for (String id : _dependencyNodesById.keySet()) {
212: if (sourceId.equals(id))
213: continue;
214:
215: if (matcher.matches(id))
216: result.add(_dependencyNodesById.get(id));
217: }
218:
219: return result;
220: }
221:
222: private IdMatcher buildMatcherForPattern(String patternList) {
223: List<IdMatcher> matchers = newList();
224:
225: for (String pattern : patternList.split(",")) {
226: IdMatcher matcher = new IdMatcherImpl(pattern.trim());
227:
228: matchers.add(matcher);
229: }
230:
231: return matchers.size() == 1 ? matchers.get(0)
232: : new OrIdMatcher(matchers);
233: }
234: }
|