Package org.gudy.azureus2.platform.macosx.access.cocoa

Source Code of org.gudy.azureus2.platform.macosx.access.cocoa.CocoaJavaBridge$RunnableDispatcher

package org.gudy.azureus2.platform.macosx.access.cocoa;

/*
* Created on 27-Mar-2005
* Created by James Yeh
* Copyright (C) 2004-2005 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/

import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.AEThread;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.platform.macosx.NativeInvocationBridge;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;

/**
* NOTE: This is only used for OSX 10.4 and non-up-to-date 10.5,10.6.
* OSX 10.5 Update 6 and OSX 10.6 Update 1 provides revealInFinder and moveToTrash
* functionality.
* <p>
* <p>Performs PlatformManager tasks using Cocoa-Java (FoundationKit only)</p>
* <p>For now, operations are performed using NSAppleScript, rather than using NSWorkspace.
* This is still significantly faster than calling the cmd-line osascript.</p>
* @version 2.1 Apr 2, 2005
*/
public final class CocoaJavaBridge extends NativeInvocationBridge
{
    /**
     * The path the Cocoa-Java class files are located at
     */
    protected static final String CLASS_PATH = "/system/library/java";

    private static final String REVEAL_SCRIPT_FORMAT = "tell application \"System Events\"\ntell application \"{0}\"\nactivate\nreveal (posix file \"{1}\" as alias)\nend tell\nend tell";

    private static final String DEL_SCRIPT_FORMAT = "tell application \"Finder\" to move (posix file \"{0}\" as alias) to the trash";

    /**
     * Main NSAutoreleasePool
     */
    private int mainPool;

    protected AEMonitor classMon = new AEMonitor("CocoaJavaBridge:C");
    private AEMonitor scriptMon = new AEMonitor("CocoaJavaBridge:S");

    protected boolean isDisposed = false;

    protected RunnableDispatcher scriptDispatcher;
   
    private Class claNSAppleEventDescriptor;

    private Class<?> claNSAutoreleasePool;

    private Method methPush;

    private Method methPop;

    private Method methNSAppleEventDescriptor_descriptorWithBoolean;

    private Class<?> claNSAppleScript;

    private Class<?> claNSMutableDictionary;

    private Method methNSAppleScript_execute;

    private String NSAppleScript_AppleScriptErrorMessage;

    private Method methNSMutableDictionary_objectForKey;

    public CocoaJavaBridge() throws Throwable
    {
        try
        {
            classMon.enter();

            claNSMutableDictionary = Class.forName("com.apple.cocoa.foundation.NSMutableDictionary");
            methNSMutableDictionary_objectForKey = claNSMutableDictionary.getMethod("objectForKey", Object.class);
           
            claNSAppleEventDescriptor = Class.forName("com.apple.cocoa.foundation.NSAppleEventDescriptor");
            methNSAppleEventDescriptor_descriptorWithBoolean = claNSAppleEventDescriptor.getMethod("descriptorWithBoolean", new Class [] { boolean.class });

            claNSAutoreleasePool = Class.forName("com.apple.cocoa.foundation.NSAutoreleasePool");
            methPush = claNSAutoreleasePool.getMethod("push", new Class [0]);
            methPop = claNSAutoreleasePool.getMethod("pop", new Class [] { int.class });

            claNSAppleScript = Class.forName("com.apple.cocoa.foundation.NSAppleScript");
            methNSAppleScript_execute = claNSAppleScript.getMethod("execute", new Class[] { claNSMutableDictionary });
            NSAppleScript_AppleScriptErrorMessage = (String) claNSAppleScript.getField("AppleScriptErrorMessage").get(null);


            //mainPool = NSAutoreleasePool.push();
            mainPool = NSAutoreleasePool_push();

            scriptDispatcher = new RunnableDispatcher();
        }
        finally
        {
            classMon.exit();
        }
    }
   
    private int NSAutoreleasePool_push() throws Throwable
    {
      return ((Number) methPush.invoke(null)).intValue();
    }

    private void NSAutoreleasePool_pop(int i) throws Throwable
    {
      methPop.invoke(null, i);
    }
   
    private Object new_NSAppleScript(String s) throws Throwable {
      return claNSAppleScript.getConstructor(new Class[] { String.class }).newInstance(s);
    }
   
    private Object NSAppleScript_execute(Object NSAppleScript, Object NSMutableDictionary) throws Throwable {
      return methNSAppleScript_execute.invoke(NSAppleScript, NSMutableDictionary);
    }
   
    private Object new_NSMutableDictionary() throws Throwable {
      return claNSMutableDictionary.newInstance();
    }
   
    private Object NSMutableDictionary_objectForKey(Object NSMutableDictionary, String s) throws Throwable {
      return methNSMutableDictionary_objectForKey.invoke(NSMutableDictionary, s);
    }

    // interface implementation

    /**
     * {@inheritDoc}
     */
    protected boolean performRecoverableFileDelete(File path)
    {
        if(!path.exists()){
            return false;
        }
       
        Object result;
        try {
          result = executeScriptWithAsync(DEL_SCRIPT_FORMAT, new Object[]{path.getAbsolutePath()});
        } catch (Throwable t) {
          Debug.out(t);
          return false;
        }
       
          // quick hack here for people where things take a while - too scared to make it a
          // sync call as I don't know the code...
               
        if ( result != null ){

            final int sleep = 25;
           
            int sleep_to_go = 2500;

          while( path.exists()){
           
            if ( sleep_to_go <= 0 ){
             
              break;
            }
           
            try{
              Thread.sleep( sleep );
             
              sleep_to_go -= sleep;
             
            }catch( Throwable e ){
              break;
            }
          }
       
          if ( path.exists()){
         
            Debug.outNoStack( "Gave up waiting for delete to complete for " + path );
          }
        }
       
        return( result != null );
    }

    /**
     * {@inheritDoc}
     */
  protected boolean showInFinder(File path, String fileBrowserApp) {
    if (!path.exists())
      return false;

    Object /*NSAppleEventDescriptor*/ result = null;
    try {
      int pool = NSAutoreleasePool_push();
      try {
        result = executeScriptWithAsync(REVEAL_SCRIPT_FORMAT, new Object[] {
          fileBrowserApp,
          path.getAbsolutePath()
        });
      } finally {
        NSAutoreleasePool_pop(pool);
      }
    } catch (Throwable t) {
      return false;
    }
    return (result != null);
  }

    /**
     * {@inheritDoc}
     */
    protected boolean isEnabled()
    {
      return claNSAutoreleasePool != null;
    }

    // class utility methods

    /**
     * <p>Executes a new instance of NSAppleScript</p>
     * <p>The method is wrapped in an autorelease pool and an AEMonitor. If there are
     * no format parameters, MessageFormat is not used to parse the format string, and
     * the format string will be treated as the source itself.</p>
     * @see MessageFormat#format(String, Object...)
     * @see NSAppleScript#execute(com.apple.cocoa.foundation.NSMutableDictionary)
     */
    protected final Object /*NSAppleEventDescriptor*/ executeScript(String scriptFormat, Object[] params) throws Throwable
    {
        try
        {
            scriptMon.enter();

            int pool = NSAutoreleasePool_push();
            long start = System.currentTimeMillis();

            String src;
            if(params == null || params.length == 0)
            {
                src = scriptFormat;
            }
            else
            {
                src = MessageFormat.format(scriptFormat, params);
            }

            Debug.outNoStack("Executing: \n" + src);

            Object /*NSAppleScript*/ scp = new_NSAppleScript(src);
            Object /*NSAppleEventDescriptor*/ result =  NSAppleScript_execute(scp, new_NSMutableDictionary());

            Debug.outNoStack(MessageFormat.format("Elapsed time: {0}ms\n", new Object[]{new Long(System.currentTimeMillis() - start)}));
            NSAutoreleasePool_pop(pool);
            return result;
        }
        finally
        {
            scriptMon.exit();
        }
    }

    /**
     * <p>Executes a new instance of NSAppleScript in a forked AEThread</p>
     * <p>This method always returns a "true" event descriptor. Callbacks are currently unsupported
     * , so in the event of an error, the logger is autuomatically notified.</p>
     * <p>The thread's runSupport method is wrapped in an autorelease pool. If there are
     * no format parameters, MessageFormat is not used to parse the format string, and
     * the format string will be treated as the source itself.</p>
     * @see org.gudy.azureus2.core3.util.AEThread#runSupport()
     * @see MessageFormat#format(String, Object...)
     * @see NSAppleScript#execute(com.apple.cocoa.foundation.NSMutableDictionary)
     * @return NSAppleEventDescriptor.descriptorWithBoolean(true)
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
  protected final Object /*NSAppleEventDescriptor*/ executeScriptWithNewThread(
    final String scriptFormat, final Object[] params) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
  {
    Thread worker = new AEThread("ScriptObject", true) {
      public void runSupport() {
        try {
          int pool = NSAutoreleasePool_push();
          long start = System.currentTimeMillis();

          String src;
          if (params == null || params.length == 0) {
            src = scriptFormat;
          } else {
            src = MessageFormat.format(scriptFormat, params);
          }   

          Debug.outNoStack("Executing: \n" + src);

          Object /*NSMutableDictionary*/ errorInfo = new_NSMutableDictionary();
          if (NSAppleScript_execute(new_NSAppleScript(src), errorInfo) == null) {
            Debug.out(String.valueOf(NSMutableDictionary_objectForKey(errorInfo, NSAppleScript_AppleScriptErrorMessage)));
            //logWarning(String.valueOf(errorInfo.objectForKey(NSAppleScript.AppleScriptErrorBriefMessage)));
          }

          Debug.outNoStack(MessageFormat.format("Elapsed time: {0}ms\n",
              new Object[] {
                new Long(System.currentTimeMillis() - start)
              }));
          NSAutoreleasePool_pop(pool);
        } catch (Throwable e) {
          Debug.out(e);
        }
      }
    };

    worker.setPriority(Thread.NORM_PRIORITY - 1);
    worker.start();

    return methNSAppleEventDescriptor_descriptorWithBoolean.invoke(null, true);
    //return NSAppleEventDescriptor.descriptorWithBoolean(true);
  }

    /**
     * <p>Asynchronously executes a new instance of NSAppleScript</p>
     * <p>This method always returns a "true" event descriptor. Callbacks are currently unsupported
     * , so in the event of an error, the logger is autuomatically notified.</p>
     * <p>The thread's runSupport method is wrapped in an autorelease pool. If there are
     * no format parameters, MessageFormat is not used to parse the format string, and
     * the format string will be treated as the source itself.</p>
     * @see org.gudy.azureus2.core3.util.AEThread#runSupport()
     * @see MessageFormat#format(String, Object...)
     * @see NSAppleScript#execute(com.apple.cocoa.foundation.NSMutableDictionary)
     * @return NSAppleEventDescriptor.descriptorWithBoolean(true)
     * @throws InvocationTargetException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    protected final Object /*NSAppleEventDescriptor*/ executeScriptWithAsync(final String scriptFormat, final Object[] params) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException
    {
        final AERunnable worker = new AERunnable()
        {
      public void runSupport() {
        try {
          int pool = NSAutoreleasePool_push();
          long start = System.currentTimeMillis();

          String src;
          if (params == null || params.length == 0) {
            src = scriptFormat;
          } else {
            src = MessageFormat.format(scriptFormat, params);
          }

          Debug.outNoStack("Executing: \n" + src);

          Object /*NSMutableDictionary*/ errorInfo = new_NSMutableDictionary();
          if (NSAppleScript_execute(new_NSAppleScript(src), errorInfo) == null) {
            Debug.out(String.valueOf(NSMutableDictionary_objectForKey(errorInfo, NSAppleScript_AppleScriptErrorMessage)));
            //logWarning(String.valueOf(errorInfo.objectForKey(NSAppleScript.AppleScriptErrorBriefMessage)));
          }

          Debug.outNoStack(MessageFormat.format("Elapsed time: {0}ms\n",
              new Object[] {
                new Long(System.currentTimeMillis() - start)
              }));
          NSAutoreleasePool_pop(pool);
        } catch (Throwable t) {
          Debug.out(t);
        }
      }
    };

        AEThread t = new AEThread("ScriptObject", true)
        {
            public void runSupport()
            {
                scriptDispatcher.exec(worker);
            }
        };
        t.setPriority(Thread.NORM_PRIORITY - 1);
        t.start();

        return methNSAppleEventDescriptor_descriptorWithBoolean.invoke(null, true);
        //return NSAppleEventDescriptor.descriptorWithBoolean(true);
    }

    /**
     * Logs a warning message to Logger. The class monitor is used.
     * @param message A warning message
     */
    private void logWarning(String message)
    {
        try
        {
            classMon.enter();
            Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_WARNING, message));
        }
        finally
        {
            classMon.exit();
        }
    }

    // disposal

    /**
     * {@inheritDoc}
     */
    protected void dispose()
    {
        try
        {
            classMon.enter();
            if(!isDisposed)
            {
                Debug.outNoStack("Disposing Native PlatformManager...");
                try {
                  NSAutoreleasePool_pop(mainPool);
                } catch (Throwable e) {
                }
                isDisposed = true;
                Debug.outNoStack("Done");
            }
        }
        finally
        {
            classMon.exit();
        }
    }

    /**
     * {@inheritDoc}
     */
    protected void finalize() throws Throwable
    {
        dispose();
        super.finalize();
    }

    /**
     * A dispatch object to help facilitate asychronous script execution (from the main thread) in a more
     * predictable fashion.
     */
    private static class RunnableDispatcher
    {
        /**
         * Executes a Runnable object while synchronizing the RunnableDispatcher instance.
         * @param runnable A Runnable
         */
        private void exec(Runnable runnable)
        {
            synchronized(this)
            {
                runnable.run();
            }
        }
    }
}
TOP

Related Classes of org.gudy.azureus2.platform.macosx.access.cocoa.CocoaJavaBridge$RunnableDispatcher

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.