macdevcenter.com
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Making Cocoa-Java Apps Scriptable
Pages: 1, 2

Adding a Command

We already have something that looks like a simple text editor, so we'll continue by providing an export command. For the sake of clarity, we will use print statements in place of real disk-based operations, but the AppleScript interface side will be the same either way.



As usual, we need to alter the Suite and Terminology files to tell AppleScript about the new export command.

  1. Open MySuite.scriptSuite.
  2. Add the following section to the MyDocument class:

    "SupportedCommands" = {
    	"Export" = "handleExport:";
    };
  3. Add a Commands section after the Classes section by entering the following:

    "Commands" = {
    	"Export" = {
    		"AppleEventClassCode" = "CJst";
    		"AppleEventCode" = "Expt";
    		"CommandClass" = "NSScriptCommand";
    	};
    };

These two new sections tell the system that the MyDocument class can handle the command Export and that this is done via the method handleExport(). Note that even though we will be implementing handleExport as a Java method, the Objective-C style colon must still be used in the scriptSuite file.

The CommandClass value is used to specify a class that is capable of representing a script statement programmatically. This must be an NSScriptCommand or one of its subclasses. With the changes we've made to the scriptSuite, the default behaviour of NSScriptCommand will cause the specified method (handleExport()) to be called on the designated object (MyDocument). That's exactly what we want in this instance, so there's no need to do anything more sophisticated at this point.

Before moving on to implement the handleExport method, we need to describe the AppleScript syntax for the command.

  1. Open the MySuite.scriptTerminology file.
  2. After the Classes section, add a Commands section to describe the Export command:

    "Commands" = {
    	"Export" = {
    		"Description" = "My Custom Export command";
    		"Name" = "export";
    	};		
    };

As a quick check, rebuild the app and restart Script Editor to open the dictionary again. It should now contain the export command.

Now to implement the export command:

  1. Open the MyDocument.java file.
  2. Add the handleExport method:

    public void handleExport( NSScriptCommand inCommand ) {
    	System.out.println( "handleExport called" );
    }
  3. Rebuild.
Completed Files :MySuite.scriptSuite, MySuite.scriptTerminology, MyDocument.java

With those changes in place, we can now try out our new command.

tell application "ScriptableCJ"
	
  -- Open Console to see the results
  tell application "Console" to activate
	
  export front document
	
end tell

Example Output

After running this script, you should find "handleExport called" written out to the console. Easy, wasn't it?

Custom Commands

Earlier, when we added a command, we used the default behaviour of NSScriptCommand to pass the information along to the Document object. So what happens when you'd like to provide a custom command, but there are no suitable pre-existing objects to pass control to? In such instances, a subclass of NSScriptCommand may be used. And that's what we'll do next.

To illustrate this concept, we will provide an Import command implemented using a class derived from NSScriptCommand.

As with the other examples, we start by adding the command to the scriptSuite.

  1. Open the MySuite.scriptSuite file.
  2. Add the import command directly after the export command:

    "Import" = {
    	"AppleEventClassCode" = "CJst";
    	"AppleEventCode" = "Impt";
    	"CommandClass" = "ImportScriptCommand";
    };

Now specify the syntax for the new command:

  1. Open the MySuite.scriptTerminology file.
  2. In the commands section, describe the import command.

    "Import" = {
    	"Description" = "My Custom import command";
    	"Name" = "import";
    };

Now implement the ImportScriptCommand class, which will handle the import command without requiring a direct object to be specified.

  1. Create a new Java class called ImportScriptCommand.java, derived from NSScriptCommand:
    import com.apple.cocoa.foundation.*;
    
    public class ImportScriptCommand extends NSScriptCommand {
    }
  2. Add a constructor. Check the documentation for NSScriptCommand for more information.

    public ImportScriptCommand( NSScriptCommandDescription inCommandDescription ) {
    	super( inCommandDescription );
    }
  3. Override the performDefaultImplementation() method to carry out our custom operation.

    public Object performDefaultImplementation() {
    	System.out.println( "Import Called" );
    
    	return "Import was indeed called";
    }
Completed Files :MySuite.scriptSuite, MySuite.scriptTerminology, MyDocument.java

Now we're ready to rebuild and test the changes with a little script.

tell application "ScriptableCJ"
	import
end tell

Example Output

The result of this script, as displayed by Script Editor, will be the highly descriptive:

"Import was indeed called"

Tips

That's it. We've quickly covered most of the normal operations at this point, but before we finish, just a few things to watch out for:

  • When you create a new application, Script Finder will not be aware of it or its dictionary until after the application has been run at least once.

  • Script Editor has become better at noticing changes. However, if you open the dictionary in Script Editor and then make changes in XCode, Script Editor may not pick up the changes straight away and you may need to restart it.

  • Opening a dictionary causes the associated application to be launched. Unfortunately, rebuilding and launching your app under XCode may then leave you with two versions of the app running simultaneously--confusing for any scripts you want to test.

  • Trailing semicolons: if you do end up writing scriptSuite or scriptTerminology files using the Key=Value style, then don't forget the semicolons at the end of a statement, and after every curly bracket (bar the last one). I can't remember the number of times I've had problems getting things to work, only to notice that I've left out a semicolon somewhere.

  • If the .scriptSuite or .scriptTerminology extension is correct, then the file will be syntax-coloured in XCode. If you've made a mistake with the extension, then the file will appear in monochrome.

  • Finally, check out the . This, and all of the other documentation links throughout the article, assume that you have installed the developer tools and docs.

Summing Up

The aim of this article was to demonstrate that providing an AppleScript interface for Cocoa-Java apps is very similar to the operations required for Objective-C-based Cocoa. As you can see from the above examples, there's practically no difference (even down to using Objective-C method-name syntax). So when in doubt, just use the documentation for Objective-C.

Happy scripting!

Mike Butler worked for 7 years as a software developer with Apple and has just recently launched a Mac Localisation Tools Development company called TripleSpin.


Return to MacDevCenter.com.