Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
CommandManager |
|
| 2.8181818181818183;2.818 |
1 | /* | |
2 | * Copyright (c) 2009, Swiss Federal Department of Defence Civil Protection and Sport | |
3 | * (http://www.vbs.admin.ch) | |
4 | * Copyright (c) 2009, Ergon Informatik AG (http://www.ergon.ch) | |
5 | * All rights reserved. | |
6 | * | |
7 | * Licensed under the Open Permis License which accompanies this distribution, | |
8 | * and is available at http://www.openpermis.org/BSDlicenceKent.txt | |
9 | */ | |
10 | package org.openpermis.editor.policy.command; | |
11 | ||
12 | import java.beans.PropertyChangeListener; | |
13 | import java.beans.PropertyChangeSupport; | |
14 | import java.util.ArrayList; | |
15 | import java.util.List; | |
16 | ||
17 | import org.slf4j.Logger; | |
18 | import org.slf4j.LoggerFactory; | |
19 | ||
20 | import org.openpermis.policy.bean.PolicyBean; | |
21 | ||
22 | /** | |
23 | * Basic implementation of a command dispatcher. | |
24 | * @since 0.1.0 | |
25 | */ | |
26 | public class CommandManager | |
27 | implements CommandDispatcher | |
28 | { | |
29 | ||
30 | //---- Static | |
31 | ||
32 | /** | |
33 | * The logger object of this class. | |
34 | * @since 0.1.0 | |
35 | */ | |
36 | 1 | private static final Logger LOGGER = |
37 | LoggerFactory.getLogger(CommandManager.class); | |
38 | ||
39 | //---- State | |
40 | ||
41 | /** | |
42 | * The policy this command dispatcher operates on. | |
43 | * @since 0.1.0 | |
44 | */ | |
45 | private final PolicyBean policy; | |
46 | ||
47 | /** | |
48 | * The stack holding executed commands. | |
49 | * @since 0.1.0 | |
50 | */ | |
51 | private final List<Command> undoStack; | |
52 | ||
53 | /** | |
54 | * The stack holding reverted commands. | |
55 | * @since 0.1.0 | |
56 | */ | |
57 | private final List<Command> redoStack; | |
58 | ||
59 | /** | |
60 | * Beans property change support. | |
61 | * @since 0.1.0 | |
62 | */ | |
63 | private final PropertyChangeSupport changeSupport; | |
64 | ||
65 | //---- Constructors | |
66 | ||
67 | /** | |
68 | * Creates a new command dispatcher for the specified policy. | |
69 | * @param policy the policy the dispatcher operates on. | |
70 | * @since 0.1.0 | |
71 | */ | |
72 | 7 | public CommandManager (PolicyBean policy) { |
73 | 7 | this.policy = policy; |
74 | 7 | this.undoStack = new ArrayList<Command>(); |
75 | 7 | this.redoStack = new ArrayList<Command>(); |
76 | 7 | this.changeSupport = new PropertyChangeSupport(this); |
77 | 7 | } |
78 | ||
79 | //---- JavaBean | |
80 | ||
81 | /** | |
82 | * Registers the specified property change listener. | |
83 | * @param listener the listener to register. | |
84 | * @since 0.1.0 | |
85 | */ | |
86 | public void addPropertyChangeListener (PropertyChangeListener listener) { | |
87 | 7 | this.changeSupport.addPropertyChangeListener(listener); |
88 | 7 | } |
89 | ||
90 | /** | |
91 | * Removes the specified property change listener. | |
92 | * @param listener the listener to remove. | |
93 | * @since 0.1.0 | |
94 | */ | |
95 | public void removePropertyChangeListener (PropertyChangeListener listener) { | |
96 | 0 | this.changeSupport.removePropertyChangeListener(listener); |
97 | 0 | } |
98 | ||
99 | /** | |
100 | * Support for reporting bound property changes for Object properties. | |
101 | * <p>Use this method to inform registered property change listeners of a change in | |
102 | * a bound property.</p> | |
103 | * <p>This method will not throw any exceptions.</p> | |
104 | * @param property the property whose value has changed. | |
105 | * @param oldValue the previous value of the property. | |
106 | * @param newValue the new value of the property. | |
107 | * @since 0.1.0 | |
108 | */ | |
109 | protected void sureFirePropertyChange (String property, boolean oldValue, boolean newValue) { | |
110 | try { | |
111 | 25 | this.changeSupport.firePropertyChange(property, oldValue, newValue); |
112 | 0 | } catch (Exception e) { |
113 | // SureFire, we discard listener problems. | |
114 | 25 | } |
115 | 25 | } |
116 | ||
117 | //---- Methods | |
118 | ||
119 | /** | |
120 | * Check if there is at least one command to undo. | |
121 | * @return {@code true} if there is a command to undo. | |
122 | * @since 0.1.0 | |
123 | */ | |
124 | public synchronized boolean isUndoAvailable () { | |
125 | 0 | return !this.undoStack.isEmpty(); |
126 | } | |
127 | ||
128 | /** | |
129 | * Returns the name of the first command on the undo stack. | |
130 | * @return the name or {@code null} if there is none. | |
131 | * @since 0.1.0 | |
132 | */ | |
133 | public synchronized String getUndoName () { | |
134 | 0 | if (this.undoStack.isEmpty()) { |
135 | 0 | return null; |
136 | } | |
137 | 0 | return this.undoStack.get(this.undoStack.size() - 1).getName(); |
138 | } | |
139 | ||
140 | /** | |
141 | * Performs an undo of the last command executed. | |
142 | * @return {@code true} if a command was executed successfully. | |
143 | * @since 0.1.0 | |
144 | */ | |
145 | public synchronized boolean undo () { | |
146 | 55 | if (this.undoStack.isEmpty()) { |
147 | 27 | return false; |
148 | } | |
149 | 28 | final Command command = this.undoStack.remove(this.undoStack.size() - 1); |
150 | 28 | if (this.undoStack.isEmpty()) { |
151 | 4 | sureFirePropertyChange("undoAvailable", true, false); |
152 | } | |
153 | try { | |
154 | 28 | command.undo(this.policy); |
155 | 28 | this.redoStack.add(command); |
156 | 28 | if (this.redoStack.size() == 1) { |
157 | 7 | sureFirePropertyChange("redoAvailable", false, true); |
158 | } | |
159 | 28 | return true; |
160 | 0 | } catch (Exception e) { |
161 | 0 | LOGGER.warn("Error undoing command [" + command.getName() + "].", e); |
162 | 0 | return false; |
163 | } | |
164 | } | |
165 | ||
166 | /** | |
167 | * Check if there is at least one command to undo. | |
168 | * @return {@code true} if there is a command to undo. | |
169 | * @since 0.1.0 | |
170 | */ | |
171 | public synchronized boolean isRedoAvailable () { | |
172 | 0 | return !this.redoStack.isEmpty(); |
173 | } | |
174 | ||
175 | /** | |
176 | * Returns the name of the first command on the redo stack. | |
177 | * @return the name or {@code null} if there is none. | |
178 | * @since 0.1.0 | |
179 | */ | |
180 | public synchronized String getRedoName () { | |
181 | 0 | if (this.redoStack.isEmpty()) { |
182 | 0 | return null; |
183 | } | |
184 | 0 | return this.redoStack.get(this.redoStack.size() - 1).getName(); |
185 | } | |
186 | ||
187 | /** | |
188 | * Executes the last command that was undone again. | |
189 | * @return {@code true} if a command was executed successfully. | |
190 | * @since 0.1.0 | |
191 | */ | |
192 | public boolean redo () { | |
193 | 29 | if (this.redoStack.isEmpty()) { |
194 | 15 | return false; |
195 | } | |
196 | 14 | final Command command = this.redoStack.remove(this.redoStack.size() - 1); |
197 | 14 | if (this.redoStack.isEmpty()) { |
198 | 2 | sureFirePropertyChange("redoAvailable", true, false); |
199 | } | |
200 | try { | |
201 | 14 | command.execute(this.policy); |
202 | 14 | this.undoStack.add(command); |
203 | 14 | if (this.undoStack.size() == 1) { |
204 | 3 | sureFirePropertyChange("undoAvailable", false, true); |
205 | } | |
206 | 14 | return true; |
207 | 0 | } catch (Exception e) { |
208 | 0 | LOGGER.warn("Error redoing command [" + command.getName() + "].", e); |
209 | 0 | return false; |
210 | } | |
211 | } | |
212 | ||
213 | //---- CommandDispatcher | |
214 | ||
215 | /** | |
216 | * @since 0.1.0 | |
217 | */ | |
218 | public synchronized void execute (Command command) { | |
219 | try { | |
220 | 72 | command.execute(this.policy); |
221 | 42 | this.undoStack.add(command); |
222 | 42 | if (this.undoStack.size() == 1) { |
223 | 7 | sureFirePropertyChange("undoAvailable", false, true); |
224 | } | |
225 | 42 | if (!this.redoStack.isEmpty()) { |
226 | 2 | this.redoStack.clear(); |
227 | 2 | sureFirePropertyChange("redoAvailable", true, false); |
228 | } | |
229 | 30 | } catch (Exception e) { |
230 | 30 | LOGGER.warn("Error executing command [" + command.getName() + "].", e); |
231 | 42 | } |
232 | 72 | } |
233 | ||
234 | } |