Coverage Report - org.argouml.util.osdep.OSXAdapter
 
Classes in this File Line Coverage Branch Coverage Complexity
OSXAdapter
0%
0/59
0%
0/26
2.636
OSXAdapter$1
0%
0/8
0%
0/2
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  
 }