File Source: Apple.java
/*
P/P * Method: com.dmdirc.addons.ui_swing.Apple__static_init
*/
1 /*
2 * Copyright (c) 2006-2009 Chris Smith, Shane Mc Cormack, Gregory Holmes
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23 package com.dmdirc.addons.ui_swing;
24
25 import com.dmdirc.addons.ui_swing.components.MenuBar;
26 import com.dmdirc.actions.ActionManager;
27 import com.dmdirc.actions.CoreActionType;
28 import com.dmdirc.actions.interfaces.ActionType;
29 import com.dmdirc.config.IdentityManager;
30 import com.dmdirc.interfaces.ActionListener;
31 import com.dmdirc.util.IrcAddress;
32 import com.dmdirc.util.InvalidAddressException;
33
34 import com.dmdirc.logger.ErrorLevel;
35 import com.dmdirc.logger.Logger;
36
37 import java.awt.event.ActionEvent;
38
39 import java.lang.reflect.InvocationHandler;
40 import java.lang.reflect.Method;
41 import java.lang.reflect.Field;
42 import java.lang.reflect.Proxy;
43 import java.lang.reflect.InvocationTargetException;
44
45 import javax.swing.UIManager;
46 import javax.swing.JMenu;
47 import javax.swing.JMenuItem;
48
49 import java.util.ArrayList;
50
51 /**
52 * Integrate DMDirc with OS X better.
53 */
54 public final class Apple implements InvocationHandler, ActionListener {
55
56 /**
57 * Dummy interface for ApplicationEvent from the Apple UI on non-Apple platforms.
58 * http://developer.apple.com/documentation/Java/Reference/1.5.0/appledoc/api/com/apple/eawt/ApplicationEvent.html
59 */
60 public interface ApplicationEvent {
61
62 /**
63 * Provides the filename associated with a particular AppleEvent.
64 *
65 * @return The filename associated with a particular AppleEvent.
66 */
67 String getFilename();
68
69 /**
70 * Whether or not this event is handled.
71 *
72 * @return True if the event is handled, false otherwise
73 */
74 boolean isHandled();
75
76 /**
77 * Sets the handled state of this event.
78 *
79 * @param handled The new 'handled' state for this event.
80 */
81 void setHandled(boolean handled);
82
83 /**
84 * Retrieves the source of this event.
85 *
86 * @return This event's source
87 */
88 Object getSource();
89
90 /**
91 * Get a string representation of this object.
92 *
93 * @return A string representation of this object.
94 */
95 @Override
96 String toString();
97 }
98
99 /** The singleton instance of Apple. */
100 private static Apple me;
101 /** The "Application" object used to do stuff on OS X. */
102 private static Object application;
103 /** The "NSApplication" object used to do cocoa stuff on OS X. */
104 private static Object nsApplication;
105 /** Are we listening? */
106 private boolean isListener = false;
107 /** The MenuBar for the application. */
108 private MenuBar menuBar = null;
109 /** Has the CLIENT_OPENED action been called? */
110 private volatile boolean clientOpened = false;
111 /** Store any addresses that are opened before CLIENT_OPENED. */
112 private volatile ArrayList<IrcAddress> addresses =
113 new ArrayList<IrcAddress>();
114
115 /**
116 * Get the "Apple" instance.
117 *
118 * @return Apple instance.
119 */
120 public static Apple getApple() {
/*
P/P * Method: Apple getApple()
*
* Preconditions:
* init'ed(me)
*
* Postconditions:
* me == One-of{old me, &new Apple(getApple#1)}
* me != null
* return_value == me
* new Apple(getApple#1) num objects <= 1
* new Apple(getApple#1).addresses == &new ArrayList(Apple#1)
* new Apple(getApple#1).clientOpened == 0
* new Apple(getApple#1).isListener == 0
* new Apple(getApple#1).menuBar == null
* new ArrayList(Apple#1) num objects <= 1
*
* Test Vectors:
* me: Inverse{null}, Addr_Set{null}
*/
121 if (me == null) {
122 me = new Apple();
123 }
124 return me;
125 }
126
127 /**
128 * Create the Apple class.
129 * <p>This attempts to:</p>
130 *
131 * <ul>
132 * <li>load the JNI library</li>
133 * <li>register the callback</li>
134 * <li>register a CLIENT_OPENED listener</li>
135 * </ul>
136 */
/*
P/P * Method: void com.dmdirc.addons.ui_swing.Apple()
*
* Presumptions:
* init'ed(com.dmdirc.actions.CoreActionType.CLIENT_OPENED)
* init'ed(com.dmdirc.logger.ErrorLevel.MEDIUM)
*
* Postconditions:
* this.addresses == &new ArrayList(Apple#1)
* this.clientOpened == 0
* this.isListener == 0
* this.menuBar == null
* new ArrayList(Apple#1) num objects == 1
*/
137 private Apple() {
138 if (isApple()) {
139 try {
140 System.loadLibrary("DMDirc-Apple");
141 registerOpenURLCallback();
142 ActionManager.addListener(this, CoreActionType.CLIENT_OPENED);
143 } catch (UnsatisfiedLinkError ule) {
144 Logger.userError(ErrorLevel.MEDIUM,
145 "Unable to load JNI library.", ule);
146 }
147 }
148 }
149
150 /**
151 * Get the "Application" object
152 *
153 * @return Object that on OSX will be an "Application"
154 */
155 public static Object getApplication() {
/*
P/P * Method: Object getApplication()
*
* Preconditions:
* init'ed(application)
*
* Presumptions:
* java.lang.Class:forName(...)@158 != null
* java.lang.Class:getMethod(...)@159 != null
*
* Postconditions:
* init'ed(application)
* return_value == application
*
* Test Vectors:
* application: Inverse{null}, Addr_Set{null}
*/
156 if (application == null && isApple()) {
157 try {
158 final Class<?> app = Class.forName("com.apple.eawt.Application");
159 final Method method = app.getMethod("getApplication",
160 new Class[0]);
161 application = method.invoke(null, new Object[0]);
162 } catch (ClassNotFoundException ex) { // Probably not on OS X, do nothing.
163 } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
164 } catch (SecurityException ex) { // Probably not on OS X, do nothing.
165 } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
166 } catch (IllegalArgumentException ex) { // Probably not on OS X, do nothing.
167 } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
168 }
169 }
170 return application;
171 }
172
173 /**
174 * Get the "NSApplication" object
175 *
176 * @return Object that on OSX will be an "NSApplication"
177 */
178 public static Object getNSApplication() {
/*
P/P * Method: Object getNSApplication()
*
* Preconditions:
* init'ed(nsApplication)
*
* Presumptions:
* java.lang.Class:forName(...)@181 != null
* java.lang.Class:getMethod(...)@183 != null
*
* Postconditions:
* init'ed(nsApplication)
* return_value == nsApplication
*
* Test Vectors:
* nsApplication: Inverse{null}, Addr_Set{null}
*/
179 if (nsApplication == null && isApple()) {
180 try {
181 final Class<?> app = Class.forName(
182 "com.apple.cocoa.application.NSApplication");
183 final Method method = app.getMethod("sharedApplication",
184 new Class[0]);
185 nsApplication = method.invoke(null, new Object[0]);
186 } catch (ClassNotFoundException ex) { // Probably not on OS X, do nothing.
187 } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
188 } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
189 } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
190 }
191 }
192 return nsApplication;
193 }
194
195 /**
196 * Are we on OS X?
197 *
198 * @return true if we are running on OS X
199 */
200 public static boolean isApple() {
/*
P/P * Method: bool isApple()
*
* Postconditions:
* init'ed(return_value)
*/
201 return (System.getProperty("mrj.version") != null);
202 }
203
204 /**
205 * Are we using the OS X look and feel?
206 *
207 * @return true if we are using the OS X look and feel
208 */
209 public static boolean isAppleUI() {
/*
P/P * Method: bool isAppleUI()
*
* Presumptions:
* java.lang.Class:getName(...)@210 != null
* java.lang.Object:getClass(...)@210 != null
* javax.swing.UIManager:getLookAndFeel(...)@210 != null
*
* Postconditions:
* init'ed(return_value)
*/
210 return isApple() && UIManager.getLookAndFeel().getClass().getName().
211 equals("apple.laf.AquaLookAndFeel");
212 }
213
214 /**
215 * Set some OS X only UI settings.
216 */
217 public void setUISettings() {
/*
P/P * Method: void setUISettings()
*
* Presumptions:
* com.dmdirc.config.IdentityManager:getGlobalConfig(...)@223 != null
*/
218 if (!isApple()) {
219 return;
220 }
221
222 // Set some Apple OS X related stuff from http://tinyurl.com/6xwuld
223 final String aaText = IdentityManager.getGlobalConfig().getOptionBool(
224 "ui", "antialias") ? "on" : "off";
225
226 System.setProperty("apple.awt.antialiasing", aaText);
227 System.setProperty("apple.awt.textantialiasing", aaText);
228 System.setProperty("apple.awt.showGrowBox", "true");
229 System.setProperty("com.apple.mrj.application.apple.menu.about.name",
230 "DMDirc");
231 System.setProperty("apple.laf.useScreenMenuBar", "true");
232 System.setProperty("com.apple.mrj.application.growbox.intrudes", "false");
233 System.setProperty("com.apple.mrj.application.live-resize", "true");
234 }
235
236 /**
237 * Request user attention (Bounce the dock).
238 *
239 * @param isCritical If this is false, the dock icon only bounces once,
240 * otherwise it will bounce until clicked on.
241 */
242 public void requestUserAttention(final boolean isCritical) {
/*
P/P * Method: void requestUserAttention(bool)
*
* Presumptions:
* java.lang.Class:getField(...)@248 != null
* java.lang.Class:getMethod(...)@251 != null
* init'ed(java.lang.Integer.TYPE)
* java.lang.Object:getClass(...)@248 != null
* java.lang.Object:getClass(...)@251 != null
*
* Postconditions:
* possibly_updated(nsApplication)
*
* Test Vectors:
* isCritical: {0}, {1}
*/
243 if (!isApple()) {
244 return;
245 }
246
247 try {
248 final Field type = isCritical ? getNSApplication().getClass().
249 getField("UserAttentionRequestCritical") : getNSApplication().
250 getClass().getField("Informational");
251 final Method method = getNSApplication().getClass().getMethod(
252 "requestUserAttention", new Class[]{Integer.TYPE});
253 method.invoke(getNSApplication(), new Object[]{type.get(null)});
254 } catch (NoSuchFieldException ex) { // Probably not on OS X, do nothing.
255 } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
256 } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
257 } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
258 }
259 }
260
261 /**
262 * Set this up as a listener for the Apple Events
263 *
264 * @return True if the listener was added, else false.
265 */
266 public boolean setListener() {
/*
P/P * Method: bool setListener()
*
* Preconditions:
* (soft) init'ed(this.isListener)
*
* Presumptions:
* init'ed(java.lang.Boolean.TRUE)
* init'ed(java.lang.Boolean.TYPE)
* java.lang.Class:getMethod(...)@277 != null
* java.lang.Class:getMethod(...)@283 != null
* java.lang.Class:getMethod(...)@287 != null
* ...
*
* Postconditions:
* possibly_updated(application)
* init'ed(return_value)
* init'ed(this.isListener)
*
* Test Vectors:
* this.isListener: {0}, {1}
*/
267 if (!isApple() || isListener) {
268 return false;
269 }
270
271 try {
272 final Class listenerClass = Class.forName(
273 "com.apple.eawt.ApplicationListener");
274 final Object listener = Proxy.newProxyInstance(getClass().
275 getClassLoader(), new Class[]{listenerClass}, this);
276
277 Method method = getApplication().getClass().getMethod(
278 "addApplicationListener", new Class[]{listenerClass});
279 method.invoke(getApplication(), listener);
280
281 isListener = true;
282
283 method = getApplication().getClass().getMethod(
284 "setEnabledPreferencesMenu", new Class[]{Boolean.TYPE});
285 method.invoke(getApplication(), new Object[]{Boolean.TRUE});
286
287 method =
288 getApplication().getClass().getMethod("setEnabledAboutMenu",
289 new Class[]{Boolean.TYPE});
290 method.invoke(getApplication(), new Object[]{Boolean.TRUE});
291 return true;
292 } catch (ClassNotFoundException ex) { // Probably not on OS X, do nothing.
293 } catch (NoSuchMethodException ex) { // Probably not on OS X, do nothing.
294 } catch (IllegalAccessException ex) { // Probably not on OS X, do nothing.
295 } catch (InvocationTargetException ex) { // Probably not on OS X, do nothing.
296 }
297 return false;
298 }
299
300 /**
301 * {@inheritDoc}
302 *
303 * @throws Throwable Throws stuff on errors
304 */
305 @Override
306 public Object invoke(final Object proxy, final Method method,
307 final Object[] args) throws Throwable {
/*
P/P * Method: Object invoke(Object, Method, Object[])
*
* Preconditions:
* (soft) args != null
* (soft) init'ed(args[0])
* (soft) method != null
*
* Presumptions:
* java.lang.Class:getMethod(...)@325 != null
* java.lang.Object:getClass(...)@313 != null
* java.lang.Object:getClass(...)@325 != null
* java.lang.reflect.Method:getName(...)@329 != null
*
* Postconditions:
* init'ed(return_value)
*/
308 if (!isApple()) {
309 return null;
310 }
311
312 try {
313 final ApplicationEvent event = (ApplicationEvent) Proxy.
314 newProxyInstance(getClass().getClassLoader(), new Class[]{
/*
P/P * Method: void com.dmdirc.addons.ui_swing.Apple$1(Apple, Object[])
*
* Postconditions:
* this.val$args == Param_2
* init'ed(this.val$args)
*/
315 ApplicationEvent.class}, new InvocationHandler() {
316
317 /** {@inheritDoc} */
318 @Override
319 public Object invoke(final Object p, final Method m,
320 final Object[] a) throws Throwable {
/*
P/P * Method: Object invoke(Object, Method, Object[])
*
* Preconditions:
* m != null
* this.val$args != null
* this.val$args.length >= 1
* this.val$args[0] != null
*
* Presumptions:
* java.lang.Class:getMethod(...)@321 != null
* java.lang.Object:getClass(...)@321 != null
*
* Postconditions:
* init'ed(return_value)
*/
321 return args[0].getClass().getMethod(m.getName(), m.
322 getParameterTypes()).invoke(args[0], a);
323 }
324 });
325 Method thisMethod = this.getClass().getMethod(method.getName(),
326 new Class[]{ApplicationEvent.class});
327 return thisMethod.invoke(this, event);
328 } catch (NoSuchMethodException e) {
329 if (method.getName().equals("equals") && args.length == 1) {
330 return Boolean.valueOf(proxy == args[0]);
331 }
332 }
333
334 return null;
335 }
336
337 /**
338 * Set the MenuBar.
339 * This will unset all menu mnemonics aswell if on the OSX ui.
340 *
341 * @param menuBar MenuBar to use to send events to,
342 */
343 public void setMenuBar(final MenuBar menuBar) {
/*
P/P * Method: void setMenuBar(MenuBar)
*
* Preconditions:
* (soft) menuBar != null
*
* Postconditions:
* this.menuBar == menuBar
* this.menuBar != null
*
* Test Vectors:
* com.dmdirc.addons.ui_swing.components.MenuBar:getMenu(...)@347: Addr_Set{null}, Inverse{null}
* javax.swing.JMenu:getItem(...)@351: Addr_Set{null}, Inverse{null}
*/
344 this.menuBar = menuBar;
345 if (isAppleUI()) {
346 for (int i = 0; i < menuBar.getMenuCount(); i++) {
347 final JMenu menu = menuBar.getMenu(i);
348 if (menu != null) {
349 menu.setMnemonic(0);
350 for (int j = 0; j < menu.getItemCount(); j++) {
351 final JMenuItem menuItem = menu.getItem(j);
352 if (menuItem != null) {
353 menuItem.setMnemonic(0);
354 }
355 }
356 }
357 }
358 }
359 }
360
361 /**
362 * Handle an event using the menuBar
363 *
364 * @param name The name of the event according to the menubar
365 * @param event The ApplicationEvent we are handingle
366 */
367 public void handleMenuBarEvent(final String name,
368 final ApplicationEvent event) {
/*
P/P * Method: void handleMenuBarEvent(String, Apple$ApplicationEvent)
*
* Preconditions:
* (soft) event != null
* (soft) init'ed(this.menuBar)
* (soft) this.menuBar.controller != null
* (soft) this.menuBar.controller.mainFrameCreated != null
* (soft) this.menuBar.controller.me != null
* (soft) this.menuBar.mainFrame != null
*
* Postconditions:
* possibly_updated(this.menuBar.mainFrame.exitCode)
*
* Test Vectors:
* this.menuBar: Inverse{null}, Addr_Set{null}
*/
369 if (!isApple() || menuBar == null) {
370 return;
371 }
372 final ActionEvent actionEvent = new ActionEvent(this,
373 ActionEvent.ACTION_PERFORMED, name);
374
375 menuBar.actionPerformed(actionEvent);
376 event.setHandled(true);
377 }
378
379 /**
380 * This is called when Quit is selected from the Application menu
381 *
382 * @param event an ApplicationEvent object
383 */
384 public void handleQuit(final ApplicationEvent event) {
/*
P/P * Method: void handleQuit(Apple$ApplicationEvent)
*
* Preconditions:
* (soft) event != null
* (soft) init'ed(this.menuBar)
* (soft) this.menuBar.controller != null
* (soft) this.menuBar.controller.mainFrameCreated != null
* (soft) this.menuBar.controller.me != null
* (soft) this.menuBar.mainFrame != null
*
* Postconditions:
* possibly_updated(this.menuBar.mainFrame.exitCode)
*/
385 handleMenuBarEvent("Exit", event);
386 }
387
388 /**
389 * This is called when About is selected from the Application menu
390 *
391 * @param event an ApplicationEvent object
392 */
393 public void handleAbout(final ApplicationEvent event) {
/*
P/P * Method: void handleAbout(Apple$ApplicationEvent)
*
* Preconditions:
* (soft) event != null
* (soft) init'ed(this.menuBar)
* (soft) this.menuBar.controller != null
* (soft) this.menuBar.controller.mainFrameCreated != null
* (soft) this.menuBar.controller.me != null
* (soft) this.menuBar.mainFrame != null
*
* Postconditions:
* possibly_updated(this.menuBar.mainFrame.exitCode)
*/
394 handleMenuBarEvent("About", event);
395 }
396
397 /**
398 * This is called when Preferences is selected from the Application menu
399 *
400 * @param event an ApplicationEvent object
401 */
402 public void handlePreferences(final ApplicationEvent event) {
/*
P/P * Method: void handlePreferences(Apple$ApplicationEvent)
*
* Preconditions:
* (soft) event != null
* (soft) init'ed(this.menuBar)
* (soft) this.menuBar.controller != null
* (soft) this.menuBar.controller.mainFrameCreated != null
* (soft) this.menuBar.controller.me != null
* (soft) this.menuBar.mainFrame != null
*
* Postconditions:
* possibly_updated(this.menuBar.mainFrame.exitCode)
*/
403 handleMenuBarEvent("Preferences", event);
404 }
405
406 /**
407 * This is called when the Application is opened
408 *
409 * @param event an ApplicationEvent object
410 */
411 public void handleOpenApplication(final ApplicationEvent event) {
/*
P/P * Method: void handleOpenApplication(Apple$ApplicationEvent)
*/
412 }
413
414 /**
415 * This is called when the application is asked to open a file
416 *
417 * @param event an ApplicationEvent object
418 */
419 public void handleOpenFile(final ApplicationEvent event) {
/*
P/P * Method: void handleOpenFile(Apple$ApplicationEvent)
*/
420 }
421
422 /**
423 * This is called when asked to print
424 *
425 * @param event an ApplicationEvent object
426 */
427 public void handlePrintFile(final ApplicationEvent event) {
/*
P/P * Method: void handlePrintFile(Apple$ApplicationEvent)
*/
428 }
429
430 /**
431 * This is called when the application is reopened
432 *
433 * @param event an ApplicationEvent object
434 */
435 public void handleReopenApplication(final ApplicationEvent event) {
/*
P/P * Method: void handleReopenApplication(Apple$ApplicationEvent)
*/
436 }
437
438 /** {@inheritDoc} */
439 @Override
440 public void processEvent(final ActionType type, final StringBuffer format,
441 final Object... arguments) {
/*
P/P * Method: void processEvent(ActionType, StringBuffer, Object[])
*
* Preconditions:
* (soft) this.addresses != null
*
* Presumptions:
* init'ed(com.dmdirc.actions.CoreActionType.CLIENT_OPENED)
* java.util.ArrayList:iterator(...)@445 != null
* java.util.Iterator:next(...)@445 != null
*
* Postconditions:
* possibly_updated(this.clientOpened)
*
* Test Vectors:
* java.util.Iterator:hasNext(...)@445: {0}, {1}
*/
442 if (type == CoreActionType.CLIENT_OPENED) {
443 synchronized (addresses) {
444 clientOpened = true;
445 for (IrcAddress addr : addresses) {
446 addr.connect();
447 }
448 addresses.clear();
449 }
450 }
451 }
452
453 /**
454 * Callback from JNI library.
455 * If called before the client has finished opening, the URL will be added to
456 * a list that will be connected to once the CLIENT_OPENED action is called.
457 * Otherwise we connect right away.
458 *
459 * @param url The irc url to connect to.
460 */
461 public void handleOpenURL(final String url) {
/*
P/P * Method: void handleOpenURL(String)
*
* Preconditions:
* (soft) this.addresses != null
*
* Presumptions:
* java.lang.Thread:currentThread(...)@472 != null
* java.lang.Thread:currentThread(...)@474 != null
*
* Test Vectors:
* java.lang.Thread:getContextClassLoader(...)@472: Inverse{null}, Addr_Set{null}
* this.clientOpened: {1}, {0}
*/
462 if (isApple()) {
463 try {
464 synchronized (addresses) {
465 final IrcAddress addr = new IrcAddress(url);
466 if (!clientOpened) {
467 addresses.add(addr);
468 } else {
469 // When the JNI callback is called there is no
470 // ContextClassLoader set, which causes an NPE in
471 // IconManager if no servers have been connected to yet.
472 if (Thread.currentThread().getContextClassLoader() ==
473 null) {
474 Thread.currentThread().setContextClassLoader(ClassLoader.
475 getSystemClassLoader());
476 }
477 addr.connect();
478 }
479 }
480 } catch (InvalidAddressException iae) {
481 }
482 }
483 }
484
485 /**
486 * Register the getURL Callback.
487 *
488 * @return 0 on success, 1 on failure.
489 */
490 private synchronized final native int registerOpenURLCallback();
491 }
SofCheck Inspector Build Version : 2.17854
| Apple.java |
2009-Jun-25 01:54:24 |
| Apple.class |
2009-Sep-02 17:04:15 |
| Apple$1.class |
2009-Sep-02 17:04:15 |
| Apple$ApplicationEvent.class |
2009-Sep-02 17:04:15 |