This document represents the API specification for Cytoscape 3.0 using a Java Swing front-end.

Cytoscape Apps

Cytoscape 3 is build on the OSGi framework. In keeping with the underlying framework, extending Cytoscape involves adding an OSGi "bundle" that registers some set of services. For Cytoscape, we have made the process of creating and deploying extensions a little easier. Cytoscape defines two different types of extensions (called Apps in Cytoscape terminology): a "Simple App" and an OSGi Bundle App. The are only a couple of major differences between a "Simple App" and a "Bundle App". First, all "Simple Apps" are loaded using the same class loader. This means that it will be much more difficult for "Simple App" that use the same external library (not provided by the Cytoscape core) to co-exist. And second, "Simple Apps" and "Bundle Apps" access services in the Cytoscape core slightly differently.

Simple App

A Cytoscape "Simple App" will generally be an extension of {@link org.cytoscape.app.swing.AbstractCySwingApp} if you intend to use the swing version of Cytoscape, or {@link org.cytoscape.app.AbstractCyApp} if you don't need any swing functions. Apps that inherit from {@link org.cytoscape.app.AbstractCyApp} can access most of the Cytoscape core functionality through the protected field {@link org.cytoscape.app.AbstractCyApp#adapter}. This field provides methods to access to most of the manager and factory classes in Cytoscape (see {@link org.cytoscape.app.CyAppAdapter}. Apps that inherit from {@link org.cytoscape.app.swing.AbstractCySwingApp} can access the same classes using the {@link org.cytoscape.app.swing.AbstractCySwingApp#swingAdapter}, which also provides access to some swing-specific classes ({@link org.cytoscape.application.swing.CySwingApplication}, {@link org.cytoscape.work.swing.DialogTaskManager}, and {@link org.cytoscape.work.swing.PanelTaskManager}). In either case, the implementation must call super in it's constructor to make these methods available:

public class MyApp extends AbstractCyApp {
    public MyApp(CyAppAdapter adapter) {
       super(adapter);
       // app code here
    }
}

Bundle App

A "Bundle App" is a little different in that none of the services are provided -- the app is responsible for requesting specific services from the OSGi framework. All "Bundle Apps" should have a CyActivator class that extends {@link org.cytoscape.service.util.AbstractCyActivator}. Implementations are required to provide the "start" method, which takes an OSGi BundleContext as an argument. In the sample below, the start method gets a service and registers two services that it provides:

public class CyActivator extends AbstractCyActivator {
  public CyActivator() {
    super();
  }

  public void start(BundleContext bc) {
    CySwingApplication cytoscapeDesktopService = getService(bc,CySwingApplication.class);

    MyCytoPanel myCytoPanel = new MyCytoPanel();
    Sample02 sample02Action = new Sample02(cytoscapeDesktopService,myCytoPanel);

    registerService(bc,myCytoPanel,CytoPanelComponent.class, new Properties());
    registerService(bc,sample02Action,CyAction.class, new Properties());
  }
}
Both approaches will provide access to all Cytoscape core services, however, when publishing a service (e.g. a listener) in the "Simple App" you will need to call the {@link org.cytoscape.app.CyAppAdapter#getCyServiceRegistrar} anyways in order to call the requisite {@link org.cytoscape.service.util.CyServiceRegistrar#registerService} method.

Accessing Cytoscape services

As briefly described above, accessing Cytoscape tasks and factories depends largely on the approach ("Simple App" vs. "Bundle App") that you are taking. In the case of a "Simple App", you will probabily just call the appropriate {@link org.cytoscape.app.CyAppAdapter} method. In the case of a "Bundle App", it's a simple matter of calling {@link org.cytoscape.service.util.AbstractCyActivator#getService(BundleContext bc, java.lang.Class)} with the bundle context that was provided as an argument to the start method, and the class of the service you want to use. For example, to create a new {@link org.cytoscape.model.CyNetwork}, you could:

public class CyActivator extends AbstractCyActivator {
  public CyActivator() {
    super();
  }

  public void start(BundleContext bc) {
    CyNetworkFactory factory = getService(bc, CyNetworkFactor.class);
    // call CyNetworkFactory's createNetwork method, as appropriate
  }
}
Providing services to Cytoscape (and other Apps) is also relatively straightforward:

MyService service = new MyService();
registerService(bc, service, MyService.class, new Properties());
Now, any app wanting to use "MyService" can do so by doing a "getService". The {@link java.util.Properties} object can be passed to the service listener (if there is one for this class). (For examples on how this can be used in Cytoscape, see Adding a new context menu item below).

Getting started using the API

The Cytoscape 3 core provides significant functionality to the App developer. Currently, the Cytoscape core API has 58 packages: some of which have relatively few interfaces and class (e.g. {@link org.cytoscape.service.util}) and some of which have significantly more (e.g. {@link org.cytoscape.model}). The best way to get starting using the Cytoscape API is to make sure you have a thorough understanding of the core Cytoscape model: {@link org.cytoscape.model}. This package provides all of the basic network and table concepts used throughout Cytoscape.

The next package to understand is Cytoscape's view-model: {@link org.cytoscape.view.model}. The view-model provides view-level mechanisms for the underlying model, including the way to set various visual properties (see {@link org.cytoscape.view.model.View#setVisualProperty}). After developing an understanding of the model and view-model, you should peruse the hints given below and then let the needs of your App drive your exploration of the rest of the API.

Hints

Writing a listener

Listeners in Cytoscape 3 are a little different from "traditional" Java listeners in that listeners are not "added" to an object: e.g. there is no equivilent to Java Swing's "addPropertyChangeListener" method. Instead, in Cytoscape 3, you implement the listener and register it as a service. So, in the example below, the App (a Bundle App in this case) creates and registers a listener to {@link org.cytoscape.model.events.NetworkAddedEvent}s:

public class CyActivator extends AbstractCyActivator {
  public CyActivator() {
    super();
  }

  public void start(BundleContext bc) {
    MyNetworkAddedListener listener = new MyNetworkAddedListener();
    registerService(bc, listener, NetworkAddedListener.class, new Properties());
  }
}
And the code for the actual listener:

public class MyNetworkAddedListener implements NetworkAddedListener {
  public MyNetworkAddedListener(/* Whatever context your app needs */) {
  }

  public void handleEvent({@link org.cytoscape.model.events.NetworkAddedEvent} e) {
    CyNetwork addedNetwork = e.getNetwork();
    // Respond as appropriate
  }
}
Note that you must provide the type of the class or interface you want to register (NetworkAddedListener, in this case).

Using Tasks and Task Factories

In Cytoscape 3, the basic unit of work is a {@link org.cytoscape.work.Task}. (Due to some unfortunate early mistakes in naming, {@link org.cytoscape.work.Task} is part of {@link org.cytoscape.work} package. The {@link org.cytoscape.task} package provides a list of core task types and some useful core tasks.) There three important interfaces that you should be familiar with if you are going to implement your own Task: {@link org.cytoscape.work.Task}, {@link org.cytoscape.work.TaskIterator}, and {@link org.cytoscape.work.TaskFactory}. In order to make your Task available to Cytoscape, you will need to provide a {@link org.cytoscape.work.TaskFactory}. The {@link org.cytoscape.work.TaskFactory} provides two important methods. The most critical is {@link org.cytoscape.work.TaskFactory#createTaskIterator}, which is called by Cytoscape to get a {@link org.cytoscape.work.TaskIterator}, which should include at least your {@link org.cytoscape.work.Task}. As the name implies, though, a {@link org.cytoscape.work.TaskIterator} can include a number of tasks, which will be executed sequentially. The other method, {@link org.cytoscape.work.TaskFactory#isReady} is called to determine if your {@link org.cytoscape.work.Task} can be executed. For example, if your {@link org.cytoscape.work.Task} requires the currently selected nodes as input, and nothing is selected, this method should return false.

The {@link org.cytoscape.work.Task} itself has two methods: {@link org.cytoscape.work.Task#run}, which is used to execute the task asynchronously in a separate thread, and {@link org.cytoscape.work.Task#cancel}, which is used to signal the Task that it should cancel its activity and return. The {@link org.cytoscape.work.Task#run} method has one argument: a {@link org.cytoscape.work.TaskMonitor}, which is used to provide feedback to the user during the operation of the task.

Cytoscape provides a large number of existing {@link org.cytoscape.work.Task}, and {@link org.cytoscape.work.TaskFactory} interface extensions as well as many abstract base classes. You should take care, however, about which base class you use and how you register the task factory since generally, the Cytoscape core is listening for the registration of certain types of TaskFactories to add them to appropriate menus.

Something about Tunables? TunableSetter? Properties?

Adding a new menu item

One common use for a TaskFactory is to add a menu item to one of Cytoscape's standard menus. For example assume we want to add a new item to the Cytoscape App menu. We would add to our CyActivator:

NetworkTaskFactory myTaskFactory = new MyTaskFactory();
Properties myNetworkTaskFactoryProps = new Properties();  
myNetworkTaskFactoryProps.setProperty(ENABLE_FOR, "network");
myNetworkTaskFactoryProps.setProperty(PREFERRED_MENU,"App.MyApp.");
myNetworkTaskFactoryProps.setProperty(MENU_GRAVITY,"1.0");
myNetworkTaskFactoryProps.setProperty(TITLE,"My Application Task");
registerService(bc,myTaskFactory,NetworkTaskFactory.class, myNetworkTaskFactoryProps);
This will put a new menu item under App: "App→MyApp→My Application Task". A couple of things to note. First, the properties are all defined as static strings in {@link org.cytoscape.work.ServiceProperties}, and you should always use the symbolic name. Second, note the "MENU_GRAVITY" property key. Cytoscape's menus arrange the order of items in the menu using this "MENU_GRAVITY" property. Generally speaking, larger is further down the menu menu.

Adding a new context menu item

Adding a node or edge context menu is essentially the same as adding a menu item shown above:

NodeViewTaskFactory myTaskFactory = new MyTaskFactory();
Properties myNodeViewTaskFactoryProps = new Properties();  
myNodeViewTaskFactoryProps.setProperty(ENABLE_FOR, "selectedNodesOrEdges");
myNodeViewTaskFactoryProps.setProperty(PREFERRED_MENU,"App.MyApp.");
myNodeViewTaskFactoryProps.setProperty(MENU_GRAVITY,"1.0");
myNodeViewTaskFactoryProps.setProperty(IN_TOOL_BAR,"false");
myNodeViewTaskFactoryProps.setProperty(TITLE,"My Application Task");
registerService(bc,myTaskFactory,NodeViewTaskFactory.class, myNodeViewTaskFactoryProps);
As with the menu item above, the Cytoscape core will "notice" that this TaskFactory has been registered and what it's type is. When the user does a right-click over a node, all TaskFactories of that NodeViewTaskFactory type will be called.