Clover Coverage Report - XWiki Rendering - Parent POM 4.0-SNAPSHOT (Aggregated)
Coverage timestamp: Mon Mar 12 2012 18:03:13 CET
../../../../../img/srcFileCovDistChart9.png 55% of files have more coverage
24   165   14   2.67
8   70   0.58   9
9     1.56  
1    
 
  ListenerChain       Line # 38 24 0% 14 4 90.2% 0.902439
 
  (978)
 
1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.rendering.listener.chaining;
21   
22    import java.util.ArrayList;
23    import java.util.HashMap;
24    import java.util.List;
25    import java.util.Map;
26    import java.util.Stack;
27   
28    /**
29    * Stores information about the listeners in the chain and the order in which they need to be called. Also sports a
30    * feature that allows pushing and popping listeners that are stackable. This feature is useful since listeners can hold
31    * stateful information and sometimes you may need to push new versions of them to start with new state information. For
32    * example this is used in the XWiki Syntax Renderer when group event is found to start the rendering for that group
33    * using reset state information.
34    *
35    * @version $Id$
36    * @since 1.8RC1
37    */
 
38    public class ListenerChain
39    {
40    /**
41    * The full list of chaining listeners. For each of them we have a stack since the ones that implement the
42    * {@link StackableChainingListener} interface can be stacked.
43    */
44    private Map<Class< ? extends ChainingListener>, Stack<ChainingListener>> listeners =
45    new HashMap<Class< ? extends ChainingListener>, Stack<ChainingListener>>();
46   
47    /**
48    * The ordered list of listeners. We only allow one instance per listener class name so we just need to store the
49    * class object and then the instance can be found in {@link #listeners}.
50    */
51    private List<Class< ? extends ChainingListener>> nextListeners =
52    new ArrayList<Class< ? extends ChainingListener>>();
53   
54    /**
55    * @param listener the chaining listener to add to the chain. If an instance of that listener is already present
56    * then we stack the new instance instead.
57    */
 
58  5215 toggle public void addListener(ChainingListener listener)
59    {
60    // If there's already an entry for that listener then push it on the existing stack
61    // and don't add the listener as an additional listener in the list (since it's already
62    // in there). We need to take these steps since the push() methods below will create
63    // new instances of listeners which will add themselves in the chain automatically.
64  5215 Stack<ChainingListener> stack = this.listeners.get(listener.getClass());
65  5215 if (stack == null) {
66  5215 stack = new Stack<ChainingListener>();
67  5215 this.listeners.put(listener.getClass(), stack);
68  5215 this.nextListeners.add(listener.getClass());
69    }
70  5215 stack.push(listener);
71    }
72   
73    /**
74    * @param listenerClass the listener to remove from the chain
75    */
 
76  0 toggle public void removeListener(Class< ? extends ChainingListener> listenerClass)
77    {
78  0 this.listeners.remove(listenerClass);
79  0 this.nextListeners.remove(listenerClass);
80    }
81   
82    /**
83    * @param listenerClass the listener for which we need to find the next listener in the chain
84    * @return the next listener in the chain
85    */
 
86  60434 toggle public ChainingListener getNextListener(Class< ? extends ChainingListener> listenerClass)
87    {
88  60434 ChainingListener next = null;
89  60434 int pos = indexOf(listenerClass);
90  60434 if (pos > -1 && this.nextListeners.size() > pos + 1) {
91  57655 next = this.listeners.get(this.nextListeners.get(pos + 1)).peek();
92    }
93  60434 return next;
94    }
95   
96    /**
97    * @param listenerClass the listener class for which we want to find the listener instance
98    * @return the listener instance corresponding to the passed class. Note that the last instance of the stack is
99    * returned
100    */
 
101  2883 toggle public ChainingListener getListener(Class< ? extends ChainingListener> listenerClass)
102    {
103  2883 return this.listeners.get(listenerClass).peek();
104    }
105   
106    /**
107    * @param listenerClass the listener class for which to find the position in the chain
108    * @return the position in the chain (first position is 0)
109    */
 
110  63130 toggle public int indexOf(Class< ? extends ChainingListener> listenerClass)
111    {
112  63130 return this.nextListeners.indexOf(listenerClass);
113    }
114   
115    /**
116    * Create a new instance of the passed chaining listener if it's stackable (ie it implements the
117    * {@link org.xwiki.rendering.listener.chaining.StackableChainingListener} interface. This allows creating a
118    * clean state when some sub rendering has to be done with some new state.
119    *
120    * @param listenerClass the listener class for which to create a new instance (if stackable)
121    */
 
122  198 toggle public void pushListener(Class< ? extends ChainingListener> listenerClass)
123    {
124  198 if (StackableChainingListener.class.isAssignableFrom(listenerClass)) {
125  99 Stack<ChainingListener> stack = this.listeners.get(listenerClass);
126  99 stack.push(((StackableChainingListener) stack.peek()).createChainingListenerInstance());
127    }
128    }
129   
130    /**
131    * Create new instances of all chaining listeners that are stackable (ie that implement the
132    * {@link org.xwiki.rendering.listener.chaining.StackableChainingListener} interface. This allows creating a
133    * clean state when some sub rendering has to be done with some new state.
134    */
 
135  33 toggle public void pushAllStackableListeners()
136    {
137  33 for (Class< ? extends ChainingListener> listenerClass : this.listeners.keySet()) {
138  198 pushListener(listenerClass);
139    }
140    }
141   
142    /**
143    * Remove all pushed stackable listeners to go back to the previous state
144    * (see {@link #pushAllStackableListeners()}.
145    */
 
146  33 toggle public void popAllStackableListeners()
147    {
148  33 for (Class< ? extends ChainingListener> listenerClass : this.listeners.keySet()) {
149  198 popListener(listenerClass);
150    }
151    }
152   
153    /**
154    * Remove the last instance corresponding to the passed listener class if it's stackable, in order to go
155    * back to the previous state.
156    *
157    * @param listenerClass the class of the chaining listener to pop
158    */
 
159  198 toggle public void popListener(Class< ? extends ChainingListener> listenerClass)
160    {
161  198 if (StackableChainingListener.class.isAssignableFrom(listenerClass)) {
162  99 this.listeners.get(listenerClass).pop();
163    }
164    }
165    }