1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.rendering.listener.chaining

File ListenerChain.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

8
24
9
1
165
71
14
0.58
2.67
9
1.56

Classes

Class Line # Actions
ListenerChain 39 24 0% 14 4
0.90243990.2%
 

Contributing tests

This file is covered by 1462 tests. .

Source view

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.ArrayDeque;
23    import java.util.ArrayList;
24    import java.util.Deque;
25    import java.util.HashMap;
26    import java.util.List;
27    import java.util.Map;
28   
29    /**
30    * Stores information about the listeners in the chain and the order in which they need to be called. Also sports a
31    * feature that allows pushing and popping listeners that are stackable. This feature is useful since listeners can hold
32    * stateful information and sometimes you may need to push new versions of them to start with new state information. For
33    * example this is used in the XWiki Syntax Renderer when group event is found to start the rendering for that group
34    * using reset state information.
35    *
36    * @version $Id: a11f38401e80fe8ab62c5565a32e9227d3a26f78 $
37    * @since 1.8RC1
38    */
 
39    public class ListenerChain
40    {
41    /**
42    * The full list of chaining listeners. For each of them we have a stack since the ones that implement the
43    * {@link StackableChainingListener} interface can be stacked.
44    */
45    private Map<Class<? extends ChainingListener>, Deque<ChainingListener>> listeners =
46    new HashMap<Class<? extends ChainingListener>, Deque<ChainingListener>>();
47   
48    /**
49    * The ordered list of listeners. We only allow one instance per listener class name so we just need to store the
50    * class object and then the instance can be found in {@link #listeners}.
51    */
52    private List<Class<? extends ChainingListener>> nextListeners =
53    new ArrayList<Class<? extends ChainingListener>>();
54   
55    /**
56    * @param listener the chaining listener to add to the chain. If an instance of that listener is already present
57    * then we stack the new instance instead.
58    */
 
59  449987 toggle public void addListener(ChainingListener listener)
60    {
61    // If there's already an entry for that listener then push it on the existing stack
62    // and don't add the listener as an additional listener in the list (since it's already
63    // in there). We need to take these steps since the push() methods below will create
64    // new instances of listeners which will add themselves in the chain automatically.
65  449987 Deque<ChainingListener> stack = this.listeners.get(listener.getClass());
66  449984 if (stack == null) {
67  449985 stack = new ArrayDeque<ChainingListener>();
68  449982 this.listeners.put(listener.getClass(), stack);
69  449987 this.nextListeners.add(listener.getClass());
70    }
71  450003 stack.push(listener);
72    }
73   
74    /**
75    * @param listenerClass the listener to remove from the chain
76    */
 
77  0 toggle public void removeListener(Class<? extends ChainingListener> listenerClass)
78    {
79  0 this.listeners.remove(listenerClass);
80  0 this.nextListeners.remove(listenerClass);
81    }
82   
83    /**
84    * @param listenerClass the listener for which we need to find the next listener in the chain
85    * @return the next listener in the chain
86    */
 
87  5053580 toggle public ChainingListener getNextListener(Class<? extends ChainingListener> listenerClass)
88    {
89  5053584 ChainingListener next = null;
90  5053584 int pos = indexOf(listenerClass);
91  5053587 if (pos > -1 && this.nextListeners.size() > pos + 1) {
92  4845758 next = this.listeners.get(this.nextListeners.get(pos + 1)).peek();
93    }
94  5053651 return next;
95    }
96   
97    /**
98    * @param listenerClass the listener class for which we want to find the listener instance
99    * @return the listener instance corresponding to the passed class. Note that the last instance of the stack is
100    * returned
101    */
 
102  55610 toggle public ChainingListener getListener(Class<? extends ChainingListener> listenerClass)
103    {
104  55610 return this.listeners.get(listenerClass).peek();
105    }
106   
107    /**
108    * @param listenerClass the listener class for which to find the position in the chain
109    * @return the position in the chain (first position is 0)
110    */
 
111  5268870 toggle public int indexOf(Class<? extends ChainingListener> listenerClass)
112    {
113  5268870 return this.nextListeners.indexOf(listenerClass);
114    }
115   
116    /**
117    * Create a new instance of the passed chaining listener if it's stackable (ie it implements the
118    * {@link org.xwiki.rendering.listener.chaining.StackableChainingListener} interface. This allows creating a clean
119    * state when some sub rendering has to be done with some new state.
120    *
121    * @param listenerClass the listener class for which to create a new instance (if stackable)
122    */
 
123  174 toggle public void pushListener(Class<? extends ChainingListener> listenerClass)
124    {
125  174 if (StackableChainingListener.class.isAssignableFrom(listenerClass)) {
126  87 Deque<ChainingListener> stack = this.listeners.get(listenerClass);
127  87 stack.push(((StackableChainingListener) stack.peek()).createChainingListenerInstance());
128    }
129    }
130   
131    /**
132    * Create new instances of all chaining listeners that are stackable (ie that implement the
133    * {@link org.xwiki.rendering.listener.chaining.StackableChainingListener} interface. This allows creating a clean
134    * state when some sub rendering has to be done with some new state.
135    */
 
136  29 toggle public void pushAllStackableListeners()
137    {
138  29 for (Class<? extends ChainingListener> listenerClass : this.listeners.keySet()) {
139  174 pushListener(listenerClass);
140    }
141    }
142   
143    /**
144    * Remove all pushed stackable listeners to go back to the previous state (see {@link #pushAllStackableListeners()}.
145    */
 
146  29 toggle public void popAllStackableListeners()
147    {
148  29 for (Class<? extends ChainingListener> listenerClass : this.listeners.keySet()) {
149  174 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 back to the
155    * previous state.
156    *
157    * @param listenerClass the class of the chaining listener to pop
158    */
 
159  174 toggle public void popListener(Class<? extends ChainingListener> listenerClass)
160    {
161  174 if (StackableChainingListener.class.isAssignableFrom(listenerClass)) {
162  87 this.listeners.get(listenerClass).pop();
163    }
164    }
165    }