Save This Page
Home » tapestry-src-5.0.19 » org.apache.tapestry5.ioc.internal.util » [javadoc | source]
    1   // Copyright 2006, 2007 The Apache Software Foundation
    2   //
    3   // Licensed under the Apache License, Version 2.0 (the "License");
    4   // you may not use this file except in compliance with the License.
    5   // You may obtain a copy of the License at
    6   //
    7   //     http://www.apache.org/licenses/LICENSE-2.0
    8   //
    9   // Unless required by applicable law or agreed to in writing, software
   10   // distributed under the License is distributed on an "AS IS" BASIS,
   11   // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   12   // See the License for the specific language governing permissions and
   13   // limitations under the License.
   14   
   15   package org.apache.tapestry5.ioc.internal.util;
   16   
   17   import org.apache.tapestry5.ioc.IdMatcher;
   18   import org.apache.tapestry5.ioc.Orderable;
   19   import org.apache.tapestry5.ioc.internal.IdMatcherImpl;
   20   import org.apache.tapestry5.ioc.internal.OrIdMatcher;
   21   import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newList;
   22   import org.slf4j.Logger;
   23   
   24   import java.util.Collection;
   25   import java.util.List;
   26   import java.util.Map;
   27   
   28   /**
   29    * Used to order objects into an "execution" order. Each object must have a unique id. It may specify a list of
   30    * constraints which identify the ordering of the objects.
   31    */
   32   public class Orderer<T>
   33   {
   34       private final OneShotLock lock = new OneShotLock();
   35   
   36       private final Logger logger;
   37   
   38       private final List<Orderable> orderables = CollectionFactory.newList();
   39   
   40       private final Map<String, Orderable<T>> idToOrderable = CollectionFactory.newCaseInsensitiveMap();
   41   
   42       private final Map<String, DependencyNode<T>> dependencyNodesById = CollectionFactory.newCaseInsensitiveMap();
   43   
   44       // Special node that is always dead last: all other nodes are a dependency
   45       // of the trailer.
   46   
   47       private DependencyNode<T> trailer;
   48   
   49       interface DependencyLinker<T>
   50       {
   51           void link(DependencyNode<T> source, DependencyNode<T> target);
   52       }
   53   
   54       // before: source is added as a dependency of target, so source will
   55       // appear before target.
   56   
   57       final DependencyLinker<T> _before = new DependencyLinker<T>()
   58       {
   59           public void link(DependencyNode<T> source, DependencyNode<T> target)
   60           {
   61               target.addDependency(source);
   62           }
   63       };
   64   
   65       // after: target is added as a dependency of source, so source will appear
   66       // after target.
   67   
   68       final DependencyLinker<T> _after = new DependencyLinker<T>()
   69       {
   70           public void link(DependencyNode<T> source, DependencyNode<T> target)
   71           {
   72               source.addDependency(target);
   73           }
   74       };
   75   
   76       public Orderer(Logger logger)
   77       {
   78           this.logger = logger;
   79       }
   80   
   81       /**
   82        * Adds an object to be ordered.
   83        *
   84        * @param orderable
   85        */
   86       public void add(Orderable<T> orderable)
   87       {
   88           lock.check();
   89   
   90           String id = orderable.getId();
   91   
   92           if (idToOrderable.containsKey(id))
   93           {
   94               logger.warn(UtilMessages.duplicateOrderer(id));
   95               return;
   96           }
   97   
   98           orderables.add(orderable);
   99   
  100           idToOrderable.put(id, orderable);
  101       }
  102   
  103       /**
  104        * Adds an object to be ordered.
  105        *
  106        * @param id          unique, qualified id for the target
  107        * @param target      the object to be ordered (or null as a placeholder)
  108        * @param constraints optional, variable constraints
  109        * @see #add(Orderable)
  110        */
  111   
  112       public void add(String id, T target, String... constraints)
  113       {
  114           lock.check();
  115   
  116           add(new Orderable<T>(id, target, constraints));
  117       }
  118   
  119       public List<T> getOrdered()
  120       {
  121           lock.lock();
  122   
  123           initializeGraph();
  124   
  125           List<T> result = newList();
  126   
  127           for (Orderable<T> orderable : trailer.getOrdered())
  128           {
  129               T target = orderable.getTarget();
  130   
  131               // Nulls are placeholders that are skipped.
  132   
  133               if (target != null) result.add(target);
  134           }
  135   
  136           return result;
  137       }
  138   
  139       private void initializeGraph()
  140       {
  141           trailer = new DependencyNode<T>(logger, new Orderable<T>("*-trailer-*", null));
  142   
  143           addNodes();
  144   
  145           addDependencies();
  146       }
  147   
  148       private void addNodes()
  149       {
  150           for (Orderable<T> orderable : orderables)
  151           {
  152               DependencyNode<T> node = new DependencyNode<T>(logger, orderable);
  153   
  154               dependencyNodesById.put(orderable.getId(), node);
  155   
  156               trailer.addDependency(node);
  157           }
  158       }
  159   
  160       private void addDependencies()
  161       {
  162           for (Orderable<T> orderable : orderables)
  163           {
  164               addDependencies(orderable);
  165           }
  166       }
  167   
  168       private void addDependencies(Orderable<T> orderable)
  169       {
  170           String sourceId = orderable.getId();
  171   
  172           for (String constraint : orderable.getConstraints())
  173           {
  174               addDependencies(sourceId, constraint);
  175           }
  176       }
  177   
  178       private void addDependencies(String sourceId, String constraint)
  179       {
  180           int colonx = constraint.indexOf(':');
  181   
  182           String type = colonx > 0 ? constraint.substring(0, colonx) : null;
  183   
  184           DependencyLinker<T> linker = null;
  185   
  186           if ("after".equals(type))
  187               linker = _after;
  188           else if ("before".equals(type)) linker = _before;
  189   
  190           if (linker == null)
  191           {
  192               logger.warn(UtilMessages.constraintFormat(constraint, sourceId));
  193               return;
  194           }
  195   
  196           String patternList = constraint.substring(colonx + 1);
  197   
  198           linkNodes(sourceId, patternList, linker);
  199       }
  200   
  201       private void linkNodes(String sourceId, String patternList, DependencyLinker<T> linker)
  202       {
  203           Collection<DependencyNode<T>> nodes = findDependencies(sourceId, patternList);
  204   
  205           DependencyNode<T> source = dependencyNodesById.get(sourceId);
  206   
  207           for (DependencyNode<T> target : nodes)
  208           {
  209               linker.link(source, target);
  210           }
  211       }
  212   
  213       private Collection<DependencyNode<T>> findDependencies(String sourceId, String patternList)
  214       {
  215           IdMatcher matcher = buildMatcherForPattern(patternList);
  216   
  217           Collection<DependencyNode<T>> result = newList();
  218   
  219           for (String id : dependencyNodesById.keySet())
  220           {
  221               if (sourceId.equals(id)) continue;
  222   
  223               if (matcher.matches(id)) result.add(dependencyNodesById.get(id));
  224           }
  225   
  226           return result;
  227       }
  228   
  229       private IdMatcher buildMatcherForPattern(String patternList)
  230       {
  231           List<IdMatcher> matchers = newList();
  232   
  233           for (String pattern : patternList.split(","))
  234           {
  235               IdMatcher matcher = new IdMatcherImpl(pattern.trim());
  236   
  237               matchers.add(matcher);
  238           }
  239   
  240           return matchers.size() == 1 ? matchers.get(0) : new OrIdMatcher(matchers);
  241       }
  242   }

Save This Page
Home » tapestry-src-5.0.19 » org.apache.tapestry5.ioc.internal.util » [javadoc | source]