1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.jcurl.demo.tactics.old;
21
22 import java.awt.event.ActionEvent;
23 import java.awt.event.InputEvent;
24 import java.awt.event.KeyEvent;
25 import java.lang.annotation.Documented;
26 import java.lang.annotation.ElementType;
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.lang.annotation.Target;
30 import java.lang.reflect.InvocationTargetException;
31 import java.lang.reflect.Method;
32 import java.util.HashMap;
33 import java.util.Map;
34 import java.util.TreeMap;
35 import java.util.Map.Entry;
36 import java.util.concurrent.Executor;
37 import java.util.regex.Matcher;
38 import java.util.regex.Pattern;
39
40 import javax.swing.AbstractAction;
41 import javax.swing.Action;
42 import javax.swing.JMenu;
43 import javax.swing.JMenuItem;
44 import javax.swing.KeyStroke;
45
46 import org.apache.commons.logging.Log;
47 import org.jcurl.core.log.JCLoggerFactory;
48 import org.jcurl.core.ui.TaskExecutor.ForkableFlex;
49
50
51
52
53
54 public class ActionRegistry {
55
56
57
58
59
60
61 @Target(ElementType.METHOD)
62 @Retention(RetentionPolicy.RUNTIME)
63 @Documented
64 @interface JCAction {
65
66
67
68
69
70 String accelerator() default "";
71
72
73 int idx();
74
75
76 boolean separated() default false;
77
78
79 String title();
80 }
81
82
83
84
85
86
87
88 @Target(ElementType.TYPE)
89 @Retention(RetentionPolicy.RUNTIME)
90 @Documented
91 @interface JCMenu {
92
93 String value();
94 }
95
96 private static final ActionRegistry instance = new ActionRegistry();
97 private static final Pattern KeyPat = Pattern
98 .compile("((?:(?:CTRL|ALT|SHIFT)-)*)([1-4a-zA-Z]+)");
99 private static final Log log = JCLoggerFactory
100 .getLogger(ActionRegistry.class);
101 private static final Pattern MneFinder = Pattern.compile(".*[&]([^&]).*");
102 private static final Pattern MneStripper = Pattern.compile("[&](.)");
103 private static final Map<String, Integer> str2key = new TreeMap<String, Integer>();
104
105 static {
106 str2key.put("UP", KeyEvent.VK_UP);
107 str2key.put("DOWN", KeyEvent.VK_DOWN);
108 str2key.put("LEFT", KeyEvent.VK_LEFT);
109 str2key.put("RIGHT", KeyEvent.VK_RIGHT);
110 str2key.put("ADD", KeyEvent.VK_ADD);
111 str2key.put("SUBTRACT", KeyEvent.VK_SUBTRACT);
112 str2key.put("PLUS", KeyEvent.VK_PLUS);
113 str2key.put("MINUS", KeyEvent.VK_MINUS);
114 str2key.put("HOME", KeyEvent.VK_HOME);
115 str2key.put("END", KeyEvent.VK_END);
116 str2key.put("PGUP", KeyEvent.VK_PAGE_UP);
117 str2key.put("PGDN", KeyEvent.VK_PAGE_DOWN);
118 str2key.put("F1", KeyEvent.VK_F1);
119 str2key.put("F2", KeyEvent.VK_F2);
120 str2key.put("F3", KeyEvent.VK_F3);
121 str2key.put("F4", KeyEvent.VK_F4);
122 str2key.put("F5", KeyEvent.VK_F5);
123 str2key.put("F6", KeyEvent.VK_F6);
124 str2key.put("F7", KeyEvent.VK_F7);
125 str2key.put("F8", KeyEvent.VK_F8);
126 str2key.put("F9", KeyEvent.VK_F9);
127 str2key.put("F10", KeyEvent.VK_F10);
128 str2key.put("F11", KeyEvent.VK_F11);
129 str2key.put("F12", KeyEvent.VK_F12);
130 }
131
132 public static ActionRegistry getInstance() {
133 return instance;
134 }
135
136 public static void invoke(final Class<?> controller, final String method) {
137 getInstance().findAction(controller, method).actionPerformed(null);
138 }
139
140 public static void invoke(final Class<?> controller, final String method,
141 final Class<? extends Executor> exec) {
142 new ForkableFlex() {
143 public void run() {
144 getInstance().findAction(controller, method).actionPerformed(
145 null);
146 }
147 }.fork(exec);
148 }
149
150
151 private final Map<Class<?>, Map<Method, Action>> c2a = new HashMap<Class<?>, Map<Method, Action>>();
152
153 private final Map<Object, Map<Method, Action>> i2a = new HashMap<Object, Map<Method, Action>>();
154
155 private void addMenuItem(final Object controller, final JMenu ret,
156 final Method m) {
157 final JCAction a = m.getAnnotation(JCAction.class);
158 if (a.separated())
159 ret.addSeparator();
160 final JMenuItem mi = new JMenuItem();
161 mi.setAction(findAction(controller, m));
162 {
163
164 final Character mne = findMnemonic(a.title());
165 if (mne != null)
166 mi.setMnemonic(mne);
167 }
168 ret.add(mi);
169 }
170
171
172 private Action createAction(final Object controller, final Method m,
173 final JCAction a) {
174 final Action ac = new AbstractAction() {
175 private static final long serialVersionUID = 2349356576661476730L;
176
177 public void actionPerformed(final ActionEvent e) {
178 try {
179 m.invoke(controller, (Object[]) null);
180 } catch (final IllegalArgumentException e1) {
181 throw new RuntimeException("Unhandled", e1);
182 } catch (final IllegalAccessException e1) {
183 throw new RuntimeException("Unhandled", e1);
184 } catch (final InvocationTargetException e1) {
185 throw new RuntimeException("Unhandled", e1);
186 }
187 }
188 };
189 ac.putValue(Action.NAME, stripMnemonic(a.title()));
190 ac.putValue(Action.ACCELERATOR_KEY, findAccelerator(a.accelerator()));
191 if (false) {
192 final Character mne = findMnemonic(a.title());
193 if (mne != null)
194 ac.putValue(Action.MNEMONIC_KEY, KeyStroke.getKeyStroke(mne));
195 }
196 ac.setEnabled(false);
197 return ac;
198 }
199
200 public JMenu createJMenu(final Object controller) {
201 final Class<?> clz = controller.getClass();
202 final JCMenu ca = clz.getAnnotation(JCMenu.class);
203 if (ca == null)
204 return null;
205
206 final Map<Integer, Method> sorted = new TreeMap<Integer, Method>();
207 for (final Method me : clz.getMethods()) {
208 final JCAction ma = me.getAnnotation(JCAction.class);
209 if (ma == null || ma.idx() < 0)
210 continue;
211 if (sorted.put(ma.idx(), me) != null)
212 throw new IllegalStateException(ma.idx() + ": " + me.toString());
213 }
214
215 final JMenu ret = new JMenu(stripMnemonic(ca.value()));
216 {
217 final Character mne = findMnemonic(ca.value());
218 if (mne != null)
219 ret.setMnemonic(mne);
220 }
221
222 for (final Entry<Integer, Method> elem : sorted.entrySet())
223 addMenuItem(controller, ret, elem.getValue());
224 return ret;
225 }
226
227 KeyStroke findAccelerator(final String acc) {
228 if (acc == null || "".equals(acc))
229 return null;
230 final Matcher m = KeyPat.matcher(acc);
231 if (m.matches()) {
232 int modifiers = 0;
233 {
234 final String gr = m.group(1);
235 if (!"".equals(gr)) {
236 final String[] mod = gr.split("-");
237 for (final String mm : mod)
238 if ("CTRL".equals(mm))
239 modifiers |= InputEvent.CTRL_MASK;
240 else if ("ALT".equals(mm))
241 modifiers |= InputEvent.ALT_MASK;
242 else if ("SHIFT".equals(mm))
243 modifiers |= InputEvent.SHIFT_MASK;
244 else
245 throw new IllegalStateException(mm);
246 }
247 }
248 if (m.group(2).length() == 1)
249 return KeyStroke.getKeyStroke(m.group(2).charAt(0), modifiers);
250 final Integer kc = str2key.get(m.group(2));
251 if (kc == null)
252 throw new IllegalStateException(m.group(2));
253 return KeyStroke.getKeyStroke(kc.intValue(), modifiers);
254 } else {
255
256 final KeyStroke k = KeyStroke.getKeyStroke(acc);
257 if (k == null)
258 throw new IllegalArgumentException(acc);
259 return k;
260 }
261 }
262
263
264
265
266
267
268 public Action findAction(final Class<?> controller, final String method) {
269 try {
270 return findAction(controller.getMethod(method, (Class<?>[]) null));
271 } catch (final Exception e) {
272 throw new IllegalArgumentException(controller.getName() + "::"
273 + method, e);
274 }
275 }
276
277 private Action findAction(final Map<Method, Action> m, final Method method) {
278 if (m == null)
279 throw new IllegalArgumentException(method.getDeclaringClass()
280 .getName());
281 final Action ret = m.get(method);
282 if (ret == null)
283 throw new IllegalArgumentException(method.toString());
284 return ret;
285 }
286
287
288
289
290
291
292 public Action findAction(final Method method) {
293 return findAction(c2a.get(method.getDeclaringClass()), method);
294 }
295
296
297
298
299
300
301 public Action findAction(final Object controller, final Method method) {
302 return findAction(i2a.get(controller), method);
303 }
304
305
306
307
308
309
310 public Action findAction(final Object controller, final String method)
311 throws IllegalArgumentException {
312 try {
313 return findAction(controller, controller.getClass().getMethod(
314 method, (Class<?>[]) null));
315 } catch (final Exception e) {
316 throw new IllegalArgumentException(controller.getClass().getName()
317 + "::" + method, e);
318 }
319 }
320
321 Character findMnemonic(final CharSequence acc) {
322 final Matcher m = MneFinder.matcher(acc);
323 if (!m.matches())
324 return null;
325 return m.group(1).charAt(0);
326 }
327
328 public Object registerController(final Object controller) {
329 final Class<?> clz = controller.getClass();
330 for (final Method me : clz.getMethods()) {
331 final JCAction ma = me.getAnnotation(JCAction.class);
332 if (ma == null)
333 continue;
334 Map<Method, Action> m = i2a.get(controller);
335 if (m == null) {
336 i2a.put(controller, m = new HashMap<Method, Action>());
337 if (c2a.put(clz, m) != null)
338 throw new IllegalStateException(clz.getName());
339 }
340 if (m.put(me, createAction(controller, me, ma)) != null)
341 throw new IllegalStateException(me.toString());
342 }
343 return controller;
344 }
345
346
347 String stripMnemonic(final CharSequence a) {
348 return MneStripper.matcher(a).replaceAll("$1");
349 }
350 }