Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
OSXAdapter |
|
| 2.6363636363636362;2.636 | ||||
OSXAdapter$1 |
|
| 2.6363636363636362;2.636 |
1 | /* CHECKSTYLE:OFF - imported code | |
2 | ||
3 | File: OSXAdapter.java | |
4 | ||
5 | Abstract: Hooks existing preferences/about/quit functionality from an | |
6 | existing Java app into handlers for the Mac OS X application menu. | |
7 | Uses a Proxy object to dynamically implement the | |
8 | com.apple.eawt.ApplicationListener interface and register it with the | |
9 | com.apple.eawt.Application object. This allows the complete project | |
10 | to be both built and run on any platform without any stubs or | |
11 | placeholders. Useful for developers looking to implement Mac OS X | |
12 | features while supporting multiple platforms with minimal impact. | |
13 | | |
14 | Version: 2.0 | |
15 | ||
16 | Disclaimer: IMPORTANT: This Apple software is supplied to you by | |
17 | Apple Inc. ("Apple") in consideration of your agreement to the | |
18 | following terms, and your use, installation, modification or | |
19 | redistribution of this Apple software constitutes acceptance of these | |
20 | terms. If you do not agree with these terms, please do not use, | |
21 | install, modify or redistribute this Apple software. | |
22 | ||
23 | In consideration of your agreement to abide by the following terms, and | |
24 | subject to these terms, Apple grants you a personal, non-exclusive | |
25 | license, under Apple's copyrights in this original Apple software (the | |
26 | "Apple Software"), to use, reproduce, modify and redistribute the Apple | |
27 | Software, with or without modifications, in source and/or binary forms; | |
28 | provided that if you redistribute the Apple Software in its entirety and | |
29 | without modifications, you must retain this notice and the following | |
30 | text and disclaimers in all such redistributions of the Apple Software. | |
31 | Neither the name, trademarks, service marks or logos of Apple Inc. | |
32 | may be used to endorse or promote products derived from the Apple | |
33 | Software without specific prior written permission from Apple. Except | |
34 | as expressly stated in this notice, no other rights or licenses, express | |
35 | or implied, are granted by Apple herein, including but not limited to | |
36 | any patent rights that may be infringed by your derivative works or by | |
37 | other works in which the Apple Software may be incorporated. | |
38 | ||
39 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE | |
40 | MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION | |
41 | THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS | |
42 | FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND | |
43 | OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. | |
44 | ||
45 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL | |
46 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
47 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
48 | INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, | |
49 | MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED | |
50 | AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), | |
51 | STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE | |
52 | POSSIBILITY OF SUCH DAMAGE. | |
53 | ||
54 | Copyright � 2003-2007 Apple, Inc., All Rights Reserved | |
55 | ||
56 | */ | |
57 | ||
58 | package org.argouml.util.osdep; | |
59 | ||
60 | import java.lang.reflect.InvocationHandler; | |
61 | import java.lang.reflect.InvocationTargetException; | |
62 | import java.lang.reflect.Method; | |
63 | import java.lang.reflect.Proxy; | |
64 | ||
65 | import org.apache.log4j.Logger; | |
66 | ||
67 | /** | |
68 | * Hooks existing preferences/about/quit functionality from an existing Java app | |
69 | * into handlers for the Mac OS X application menu. Uses a Proxy object to | |
70 | * dynamically implement the com.apple.eawt.ApplicationListener interface and | |
71 | * register it with the com.apple.eawt.Application object. This allows the | |
72 | * complete project to be both built and run on any platform without any stubs | |
73 | * or placeholders. Useful for developers looking to implement Mac OS X features | |
74 | * while supporting multiple platforms with minimal impact.<p> | |
75 | * | |
76 | * Modified by Tom Morris for ArgoUML as follows: | |
77 | * - changed package | |
78 | * - added Javadoc | |
79 | * - disabled checkstyle warnings | |
80 | * - switched error logging to use log4j instead of println | |
81 | * | |
82 | * @author Apple, Inc. | |
83 | * @version 2.0 | |
84 | */ | |
85 | public class OSXAdapter implements InvocationHandler { | |
86 | ||
87 | 0 | private static final Logger LOG = Logger.getLogger(OSXAdapter.class); |
88 | ||
89 | protected Object targetObject; | |
90 | protected Method targetMethod; | |
91 | protected String proxySignature; | |
92 | ||
93 | static Object macOSXApplication; | |
94 | ||
95 | /** | |
96 | * Pass this method an Object and Method equipped to perform application | |
97 | * shutdown logic The method passed should return a boolean stating whether | |
98 | * or not the quit should occur | |
99 | * | |
100 | * @param target object containing the method | |
101 | * @param quitHandler method to handle quit | |
102 | */ | |
103 | public static void setQuitHandler(Object target, Method quitHandler) { | |
104 | 0 | setHandler(new OSXAdapter("handleQuit", target, quitHandler)); |
105 | 0 | } |
106 | ||
107 | /** | |
108 | * Set handler for About action. It will be called when the About menu item | |
109 | * is selected from the application menu. | |
110 | * | |
111 | * @param target object containing the method | |
112 | * @param aboutHandler method to invoke to handle About menu item | |
113 | */ | |
114 | public static void setAboutHandler(Object target, Method aboutHandler) { | |
115 | 0 | boolean enableAboutMenu = (target != null && aboutHandler != null); |
116 | 0 | if (enableAboutMenu) { |
117 | 0 | setHandler(new OSXAdapter("handleAbout", target, aboutHandler)); |
118 | } | |
119 | // If we're setting a handler, enable the About menu item by calling | |
120 | // com.apple.eawt.Application reflectively | |
121 | try { | |
122 | 0 | Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class }); |
123 | 0 | enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) }); |
124 | 0 | } catch (Exception ex) { |
125 | 0 | LOG.error("OSXAdapter could not access the About Menu", ex); |
126 | 0 | } |
127 | 0 | } |
128 | ||
129 | /** | |
130 | * Set handler for Mac preferences item. It will be called when the | |
131 | * Preferences menu item is selected from the application menu. | |
132 | * | |
133 | * @param target object containing the method | |
134 | * @param prefsHandler method to handle preferences action | |
135 | */ | |
136 | public static void setPreferencesHandler(Object target, Method prefsHandler) { | |
137 | 0 | boolean enablePrefsMenu = (target != null && prefsHandler != null); |
138 | 0 | if (enablePrefsMenu) { |
139 | 0 | setHandler(new OSXAdapter("handlePreferences", target, prefsHandler)); |
140 | } | |
141 | // If we're setting a handler, enable the Preferences menu item by calling | |
142 | // com.apple.eawt.Application reflectively | |
143 | try { | |
144 | 0 | Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class }); |
145 | 0 | enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) }); |
146 | 0 | } catch (Exception ex) { |
147 | 0 | LOG.error("OSXAdapter could not access the About Menu"); |
148 | 0 | ex.printStackTrace(); |
149 | 0 | } |
150 | 0 | } |
151 | ||
152 | ||
153 | /** | |
154 | * Pass this method an Object and a Method equipped to handle document | |
155 | * events from the Finder Documents are registered with the Finder via the | |
156 | * CFBundleDocumentTypes dictionary in the application bundle's Info.plist | |
157 | * | |
158 | * @param target object containing method | |
159 | * @param fileHandler method to invoke to open a new file | |
160 | */ | |
161 | public static void setFileHandler(Object target, Method fileHandler) { | |
162 | 0 | setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) { |
163 | // Override OSXAdapter.callTarget to send information on the | |
164 | // file to be opened | |
165 | public boolean callTarget(Object appleEvent) { | |
166 | 0 | if (appleEvent != null) { |
167 | try { | |
168 | 0 | Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null); |
169 | 0 | String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null); |
170 | 0 | this.targetMethod.invoke(this.targetObject, new Object[] { filename }); |
171 | 0 | } catch (Exception ex) { |
172 | ||
173 | 0 | } |
174 | } | |
175 | 0 | return true; |
176 | } | |
177 | }); | |
178 | 0 | } |
179 | ||
180 | /** | |
181 | * Creates a Proxy object from the passed OSXAdapter and adds it as an | |
182 | * ApplicationListener | |
183 | * | |
184 | * @param adapter an instance of this class | |
185 | */ | |
186 | public static void setHandler(OSXAdapter adapter) { | |
187 | try { | |
188 | 0 | Class applicationClass = Class.forName("com.apple.eawt.Application"); |
189 | 0 | if (macOSXApplication == null) { |
190 | 0 | macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null); |
191 | } | |
192 | 0 | Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); |
193 | 0 | Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass }); |
194 | // Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener | |
195 | 0 | Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[] { applicationListenerClass }, adapter); |
196 | 0 | addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy }); |
197 | 0 | } catch (ClassNotFoundException cnfe) { |
198 | 0 | LOG.error("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")"); |
199 | 0 | } catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods |
200 | 0 | LOG.error("Mac OS X Adapter could not talk to EAWT:"); |
201 | 0 | ex.printStackTrace(); |
202 | 0 | } |
203 | 0 | } |
204 | ||
205 | /** | |
206 | * Each OSXAdapter has the name of the EAWT method it intends to listen for | |
207 | * (handleAbout, for example), the Object that will ultimately perform the | |
208 | * task, and the Method to be called on that Object | |
209 | * | |
210 | * @param proxySignature | |
211 | * @param target | |
212 | * @param handler | |
213 | */ | |
214 | 0 | protected OSXAdapter(String proxySignature, Object target, Method handler) { |
215 | 0 | this.proxySignature = proxySignature; |
216 | 0 | this.targetObject = target; |
217 | 0 | this.targetMethod = handler; |
218 | 0 | } |
219 | ||
220 | ||
221 | /** | |
222 | * Override this method to perform any operations on the event that comes | |
223 | * with the various callbacks. See setFileHandler above for an example | |
224 | * | |
225 | * @param appleEvent the AppleEvent | |
226 | * @return boolean result of method invocation | |
227 | * @throws InvocationTargetException | |
228 | * @throws IllegalAccessException | |
229 | */ | |
230 | public boolean callTarget(Object appleEvent) | |
231 | throws InvocationTargetException, IllegalAccessException { | |
232 | 0 | Object result = targetMethod.invoke(targetObject, (Object[])null); |
233 | 0 | if (result == null) { |
234 | 0 | return true; |
235 | } | |
236 | 0 | return Boolean.valueOf(result.toString()).booleanValue(); |
237 | } | |
238 | ||
239 | /** | |
240 | * InvocationHandler implementation This is the entry point for our proxy | |
241 | * object; it is called every time an ApplicationListener method is invoked | |
242 | * | |
243 | * @param proxy | |
244 | * @param method | |
245 | * @param args | |
246 | * @return null | |
247 | * @throws Throwable | |
248 | * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, | |
249 | * java.lang.reflect.Method, java.lang.Object[]) | |
250 | */ | |
251 | public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { | |
252 | 0 | if (isCorrectMethod(method, args)) { |
253 | 0 | boolean handled = callTarget(args[0]); |
254 | 0 | setApplicationEventHandled(args[0], handled); |
255 | } | |
256 | // All of the ApplicationListener methods are void; return null regardless of what happens | |
257 | 0 | return null; |
258 | } | |
259 | ||
260 | /** | |
261 | * Compare the method that was called to the intended method when the | |
262 | * OSXAdapter instance was created (e.g. handleAbout, handleQuit, | |
263 | * handleOpenFile, etc.) | |
264 | * | |
265 | * @param method method to be invoked | |
266 | * @param args argumnets | |
267 | * @return a boolean result of the method invocation | |
268 | */ | |
269 | protected boolean isCorrectMethod(Method method, Object[] args) { | |
270 | 0 | return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1); |
271 | } | |
272 | ||
273 | /** | |
274 | * It is important to mark the ApplicationEvent as handled and cancel the | |
275 | * default behavior This method checks for a boolean result from the proxy | |
276 | * method and sets the event accordingly | |
277 | * | |
278 | * @param event | |
279 | * @param handled | |
280 | */ | |
281 | protected void setApplicationEventHandled(Object event, boolean handled) { | |
282 | 0 | if (event != null) { |
283 | try { | |
284 | 0 | Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class }); |
285 | // If the target method returns a boolean, use that as a hint | |
286 | 0 | setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) }); |
287 | 0 | } catch (Exception ex) { |
288 | 0 | LOG.error("OSXAdapter was unable to handle an ApplicationEvent: " + event, ex); |
289 | 0 | } |
290 | } | |
291 | 0 | } |
292 | } |