001/** 002 * Copyright (C) 2014 Universidade de Aveiro, DETI/IEETA, Bioinformatics Group - http://bioinformatics.ua.pt/ 003 * 004 * This file is part of Dicoogle/dicoogle. 005 * 006 * Dicoogle/dicoogle is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Dicoogle/dicoogle is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Dicoogle. If not, see <http://www.gnu.org/licenses/>. 018 */ 019/* 020 021File: OSXAdapter.java 022 023Abstract: Hooks existing preferences/about/quit functionality from an 024 existing Java app into handlers for the Mac OS X application menu. 025 Uses a Proxy object to dynamically implement the 026 com.apple.eawt.ApplicationListener interface and register it with the 027 com.apple.eawt.Application object. This allows the complete project 028 to be both built and run on any platform without any stubs or 029 placeholders. Useful for developers looking to implement Mac OS X 030 features while supporting multiple platforms with minimal impact. 031 032Version: 2.0 033 034Disclaimer: IMPORTANT: This Apple software is supplied to you by 035Apple Inc. ("Apple") in consideration of your agreement to the 036following terms, and your use, installation, modification or 037redistribution of this Apple software constitutes acceptance of these 038terms. If you do not agree with these terms, please do not use, 039install, modify or redistribute this Apple software. 040 041In consideration of your agreement to abide by the following terms, and 042subject to these terms, Apple grants you a personal, non-exclusive 043license, under Apple's copyrights in this original Apple software (the 044"Apple Software"), to use, reproduce, modify and redistribute the Apple 045Software, with or without modifications, in source and/or binary forms; 046provided that if you redistribute the Apple Software in its entirety and 047without modifications, you must retain this notice and the following 048text and disclaimers in all such redistributions of the Apple Software. 049Neither the name, trademarks, service marks or logos of Apple Inc. 050may be used to endorse or promote products derived from the Apple 051Software without specific prior written permission from Apple. Except 052as expressly stated in this notice, no other rights or licenses, express 053or implied, are granted by Apple herein, including but not limited to 054any patent rights that may be infringed by your derivative works or by 055other works in which the Apple Software may be incorporated. 056 057The Apple Software is provided by Apple on an "AS IS" basis. APPLE 058MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION 059THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS 060FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND 061OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. 062 063IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL 064OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 065SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 066INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, 067MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED 068AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), 069STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 070POSSIBILITY OF SUCH DAMAGE. 071 072Copyright � 2003-2007 Apple, Inc., All Rights Reserved 073 074*/ 075 076package pt.ua.dicoogle.rGUI.client.UIHelper; 077 078import java.lang.reflect.*; 079import java.util.HashMap; 080 081@Deprecated 082public class OSXAdapter implements InvocationHandler { 083 084 protected Object targetObject; 085 protected Method targetMethod; 086 protected String proxySignature; 087 088 static Object macOSXApplication; 089 090 // Pass this method an Object and Method equipped to perform application shutdown logic 091 // The method passed should return a boolean stating whether or not the quit should occur 092 public static void setQuitHandler(Object target, Method quitHandler) { 093 setHandler(new OSXAdapter("handleQuit", target, quitHandler)); 094 } 095 096 // Pass this method an Object and Method equipped to display application info 097 // They will be called when the About menu item is selected from the application menu 098 public static void setAboutHandler(Object target, Method aboutHandler) { 099 boolean enableAboutMenu = (target != null && aboutHandler != null); 100 if (enableAboutMenu) { 101 setHandler(new OSXAdapter("handleAbout", target, aboutHandler)); 102 } 103 // If we're setting a handler, enable the About menu item by calling 104 // com.apple.eawt.Application reflectively 105 try { 106 Method enableAboutMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledAboutMenu", new Class[] { boolean.class }); 107 enableAboutMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enableAboutMenu) }); 108 } catch (Exception ex) { 109 System.err.println("OSXAdapter could not access the About Menu"); 110 ex.printStackTrace(); 111 } 112 } 113 114 // Pass this method an Object and a Method equipped to display application options 115 // They will be called when the Preferences menu item is selected from the application menu 116 public static void setPreferencesHandler(Object target, Method prefsHandler) { 117 boolean enablePrefsMenu = (target != null && prefsHandler != null); 118 if (enablePrefsMenu) { 119 setHandler(new OSXAdapter("handlePreferences", target, prefsHandler)); 120 } 121 // If we're setting a handler, enable the Preferences menu item by calling 122 // com.apple.eawt.Application reflectively 123 try { 124 Method enablePrefsMethod = macOSXApplication.getClass().getDeclaredMethod("setEnabledPreferencesMenu", new Class[] { boolean.class }); 125 enablePrefsMethod.invoke(macOSXApplication, new Object[] { Boolean.valueOf(enablePrefsMenu) }); 126 } catch (Exception ex) { 127 System.err.println("OSXAdapter could not access the About Menu"); 128 ex.printStackTrace(); 129 } 130 } 131 132 // Pass this method an Object and a Method equipped to handle document events from the Finder 133 // Documents are registered with the Finder via the CFBundleDocumentTypes dictionary in the 134 // application bundle's Info.plist 135 public static void setFileHandler(Object target, Method fileHandler) { 136 setHandler(new OSXAdapter("handleOpenFile", target, fileHandler) { 137 // Override OSXAdapter.callTarget to send information on the 138 // file to be opened 139 public boolean callTarget(Object appleEvent) { 140 if (appleEvent != null) { 141 try { 142 Method getFilenameMethod = appleEvent.getClass().getDeclaredMethod("getFilename", (Class[])null); 143 String filename = (String) getFilenameMethod.invoke(appleEvent, (Object[])null); 144 this.targetMethod.invoke(this.targetObject, new Object[] { filename }); 145 } catch (Exception ex) { 146 147 } 148 } 149 return true; 150 } 151 }); 152 } 153 154 // setHandler creates a Proxy object from the passed OSXAdapter and adds it as an ApplicationListener 155 public static void setHandler(OSXAdapter adapter) { 156 try { 157 Class applicationClass = Class.forName("com.apple.eawt.Application"); 158 if (macOSXApplication == null) { 159 macOSXApplication = applicationClass.getConstructor((Class[])null).newInstance((Object[])null); 160 } 161 Class applicationListenerClass = Class.forName("com.apple.eawt.ApplicationListener"); 162 Method addListenerMethod = applicationClass.getDeclaredMethod("addApplicationListener", new Class[] { applicationListenerClass }); 163 // Create a proxy object around this handler that can be reflectively added as an Apple ApplicationListener 164 Object osxAdapterProxy = Proxy.newProxyInstance(OSXAdapter.class.getClassLoader(), new Class[] { applicationListenerClass }, adapter); 165 addListenerMethod.invoke(macOSXApplication, new Object[] { osxAdapterProxy }); 166 } catch (ClassNotFoundException cnfe) { 167 System.err.println("This version of Mac OS X does not support the Apple EAWT. ApplicationEvent handling has been disabled (" + cnfe + ")"); 168 } catch (Exception ex) { // Likely a NoSuchMethodException or an IllegalAccessException loading/invoking eawt.Application methods 169 System.err.println("Mac OS X Adapter could not talk to EAWT:"); 170 ex.printStackTrace(); 171 } 172 } 173 174 // Each OSXAdapter has the name of the EAWT method it intends to listen for (handleAbout, for example), 175 // the Object that will ultimately perform the task, and the Method to be called on that Object 176 protected OSXAdapter(String proxySignature, Object target, Method handler) { 177 this.proxySignature = proxySignature; 178 this.targetObject = target; 179 this.targetMethod = handler; 180 } 181 182 // Override this method to perform any operations on the event 183 // that comes with the various callbacks 184 // See setFileHandler above for an example 185 public boolean callTarget(Object appleEvent) throws InvocationTargetException, IllegalAccessException { 186 Object result = targetMethod.invoke(targetObject, (Object[])null); 187 if (result == null) { 188 return true; 189 } 190 return Boolean.valueOf(result.toString()).booleanValue(); 191 } 192 193 // InvocationHandler implementation 194 // This is the entry point for our proxy object; it is called every time an ApplicationListener method is invoked 195 public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { 196 if (isCorrectMethod(method, args)) { 197 boolean handled = callTarget(args[0]); 198 setApplicationEventHandled(args[0], handled); 199 } 200 // All of the ApplicationListener methods are void; return null regardless of what happens 201 return null; 202 } 203 204 // Compare the method that was called to the intended method when the OSXAdapter instance was created 205 // (e.g. handleAbout, handleQuit, handleOpenFile, etc.) 206 protected boolean isCorrectMethod(Method method, Object[] args) { 207 return (targetMethod != null && proxySignature.equals(method.getName()) && args.length == 1); 208 } 209 210 // It is important to mark the ApplicationEvent as handled and cancel the default behavior 211 // This method checks for a boolean result from the proxy method and sets the event accordingly 212 protected void setApplicationEventHandled(Object event, boolean handled) { 213 if (event != null) { 214 try { 215 Method setHandledMethod = event.getClass().getDeclaredMethod("setHandled", new Class[] { boolean.class }); 216 // If the target method returns a boolean, use that as a hint 217 setHandledMethod.invoke(event, new Object[] { Boolean.valueOf(handled) }); 218 } catch (Exception ex) { 219 System.err.println("OSXAdapter was unable to handle an ApplicationEvent: " + event); 220 ex.printStackTrace(); 221 } 222 } 223 } 224}