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}