Filtering actions and property pages by project nature

This sample demonstrates how to add a custom project nature to a subproject. The presence or absence of the project nature can then be used to control the visibility of menu items.

Sample scenario

Create a z/OS® project and MVS subproject according to the instructions in the following topics:

To add the sample project nature to the subproject, right-click the subproject and select the API Sample Actions > Add Project Nature action.

Subproject nature definition

Subproject natures in Developer for z/OS are similar in concept to IProjectNature in base Eclipse. They provide a means to distinguish between remote subprojects based on the function that is to be enabled for each subproject.

To define a custom project nature for a remote subproject, you must create a class that implements the ILogicalSubProjectNature interface. Thus the SampleNature class contains the following code:

public class SampleNature implements ILogicalSubProjectNature {
   ......
}

The ILogicalSubProjectNature interface is relatively simple. In SampleNature, you add a private field project for the project property.

The complete source code can be found in SampleNature.java.

To complete the definition of the project nature, declare the SampleNature class in the plug-in manifest as follows:

   <extension
         id=“samplenature”
         name=“
         point=“com.ibm.ftt.projects.core.natures”>
      <nature class=“com.ibm.ftt.api.samples.natures.SampleNature”/>
   </extension>

This snippet defines a project nature with the ID com.ibm.ftt.api.samples.samplenature, which is obtained by appending the extension ID samplenature to the ID of the defining plug-in com.ibm.ftt.api.samples.

Adding a project nature

The Add Project Nature menu item is implemented by the AddSubProjectNatureAction class. Its run(IAction) method adds the SampleNature by passing the ID of the nature to the addNatureId(String) method of ILogicalSubProject.

  try {
			getSubProject().addNatureId("com.ibm.ftt.api.samples.samplenature");
		} catch (CoreException e) {
			// TODO: in production code exception should be handled for real
			e.printStackTrace();
		}

The complete source code can be found in AddSubProjectNatureAction.java.

The action is contributed by using standard Eclipse contribution mechanisms.

      <objectContribution
            adaptable=“false”
            objectClass=“com.ibm.ftt.projects.core.logical.ILogicalSubProject”
            id=“com.ibm.ftt.api.samples.addnature”>
         <action
               label=“
               class=“com.ibm.ftt.api.samples.natures.AddSubProjectNatureAction”
               id=“com.ibm.ftt.api.samples.addnatureaction”/>
      </objectContribution>

Removing a project nature and contributing menu items filtered by project natures

The Remove Project Nature menu item is implemented by the RemoveSubProjectNatureAction class. Its run(IAction) method removes the SampleNature by passing the ID of the nature to the removeNatureId(String) method of ILogicalSubProject.

  try {
			getSubProject().removeNatureId(“com.ibm.ftt.api.samples.samplenature”);
		} catch (CoreException e) {
			// TODO: in production code exception should be handled for real
			e.printStackTrace();
		}

The complete source code can be found at RemoveSubProjectAction.java.

The action is contributed by using standard Eclipse contribution mechanisms, similar to the Add Project Nature menu item, with the additional use of action filters. This contribution causes the Remove Project Nature action to be displayed only if the SampleNature is added to the project in question.

      <objectContribution
            adaptable=“false”
            objectClass=“com.ibm.ftt.projects.core.logical.ILogicalSubProject”
            id=“com.ibm.ftt.api.samples.removenature”>
         <filter
               value=“com.ibm.ftt.api.samples.samplenature”
               name=“projectNature”/>
         <action
               label=“
               class=“com.ibm.ftt.api.samples.natures.RemoveSubProjectNatureAction”
               id=“com.ibm.ftt.api.samples.removenatureaction”/>
      </objectContribution>

Creating a property group

This sample demonstrates how to programmatically create a property group that has values for predefined properties and properties that are registered with Developer for z/OS.

Sample scenario

To create a property group, select the API Sample Actions -> Create Property Group action from the menu of an MVS Files node in the Remote Systems view.

Statements are displayed when the property group is created and the property values are set. Also, if any error conditions are detected, error messages are displayed as well. They are displayed in the standard output that is associated with the workbench. This information can be found in the Console view of the workbench.

Note: The standard output for a workbench started with the standard Eclipse icon might not be visible depending on the runtime settings for the workbench.

The following illustration shows the results of successfully creating the property group and setting the values:

Artwork for createPropertyGroupResults

Retrieving the property group container for the system

The following code snippet from the CreatePropertyGroupAction class demonstrates how to retrieve the property group container for the selected system:
  
if (selectedItem == null) {
  System.err.println("Create Property Group Action - Selected resource must be an MVS File subsystem.");
  return;
}
if (selectedItem instanceof ZOSSystemReference) {
  System.out.println("");
  System.out.println("-----------------------");
  System.out.println("Beginning Create Property Group Action...");
  ZOSSystemReference reference = (ZOSSystemReference) selectedItem;
  ZOSSystemImage system = (ZOSSystemImage) reference.getReferent();
  // Get the property group container for this system.
  ZOSPropertyGroupManager manager = ZOSPropertyGroupManager.getZOSPropertyGroupManager();
  IPropertyGroupContainer container = manager.getPropertyGroupContainer(system.getName());
  if (container == null) {
    System.err.println("Create Property Group Action - No container for system: " + system.getName());
    return;
  }

It starts by verifying that the selectedItem is an instance of the ZOSSystemReference class. If so, it retrieves the ZOSSystemImage from the ZOSSystemReference by using the getReferent method.

For each system connection, there is a property group container. The property group container for a system name can be retrieved by using the getPropertyGroupContainer method of the ZOSPropertyGroupManager class.

Creating a property group

This sample from the CreatePropertyGroupAction class demonstrates how to create a property group that contains a category instance from a user-defined category and a category instance from a predefined category.
 
IPropertyGroup group = null;
try {
  group = container.createPropertyGroup(PROPERTY_GROUP_NAME, "");
  System.out.println(PROPERTY_GROUP_NAME + " created.");
} catch (DuplicatePropertyGroupException e1) {
  System.err.println("Create Property Group Action - Property group already exists.");
  return;
}

The property group is created by using the createPropertyGroup method. If the property group exists, a DuplicatePropertyGroupException is thrown and an error message is printed.

Adding a user-defined category to a property group

The following code snippet demonstrates how to create a category instance for a user-defined category and add it to a property group:
// Create an instance of the user-defined category, set properties,
// and add the instance to the property group. These properties will not appear
// in the UI, but they are available through the API.
try {
  ICategory category = manager.getCategory("SAMPLE_CATEGORY");
  if (category == null) {
    System.err.println("Create Property Group Action - Could not find category SAMPLE_CATEGORY.");
    return;
  }
  ICategoryInstance instance = category.makeInstance();
  instance.setValue("PROPERTY_1", "value1");
  System.out.println("PROPERTY_1 set to value1");
  instance.setValue("PROPERTY_2", "value2");
  System.out.println("PROPERTY_2 set to value2");
  group.addCategoryInstance(instance);
} catch (UnregisteredPropertyException e) {
  System.err.println("Create Property Group Action - Unregistered property: " + e.getName());
  return;
} catch (DuplicateInstanceException e) {
  System.err.println("Create Property Group Action - Duplicate instance of category: " + e.getCategoryInstance().getCategory().getName());
  return;
}
An instance of the SAMPLE_CATEGORY category is created by the previous code snippet. This category is registered with Developer for z/OS using the following Eclipse extension point:
<extension point="com.ibm.ftt.properties.api.category">
  <category name="SAMPLE_CATEGORY">
    <property name="PROPERTY_1" />
    <property name="PROPERTY_2" />
  </category>
</extension>

These property values are not displayed in the Developer for z/OS user interface because the user interface is not extensible, but they are available through the API.

Adding a predefined category to a property group

The following code snippet demonstrates how to create a category instance for a predefined category and add it to a property group.
// Create an instance of a pre-defined category, set properties,
// and add the instance to the property group. The values are not
// realistic values, this is just an example of how to work with
// pre-defined properties.
try {
  ICategory category = manager.getCategory(IPropertyGroupConstants.JCL_OPTIONS);
  if (category == null) {
    System.err.println("Create Property Group Action - Could not find category " + IPropertyGroupConstants.COBOL_SETTINGS);
    return;
  }
  ICategoryInstance instance = category.makeInstance();
  instance.setValue(IPropertyGroupConstants.JOBCARD, "//sample jobcard");
  System.out.println(IPropertyGroupConstants.JOBCARD + " set to //sample jobcard");
  instance.setValue(IPropertyGroupConstants.GENERATED_JCL_DATASET, "sample.test.jcl");
  System.out.println(IPropertyGroupConstants.GENERATED_JCL_DATASET + " set to sample.test.jcl");
  group.addCategoryInstance(instance);
} catch (UnregisteredPropertyException e) {
  System.err.println("Create Property Group Action - Unregistered property: " + e.getName());
  return;
} catch (DuplicateInstanceException e) {
  System.err.println("Create Property Group Action - Duplicate instance of category: " + e.getCategoryInstance().getCategory().getName());
}

(IPropertyGroupConstants) contains the names of the predefined categories and properties.

Getting the lists of categories, properties, and category instances

This sample demonstrates how to programmatically obtain the list of registered categories and properties. Currently, the names of the registered categories and properties can be obtained. For each category, the instances of that category can be obtained, and the property values in each instance can be obtained.

Sample scenario

To retrieve information about the categories, properties, and instances, select the API Samples > Properties Information action.

For each registered category, the name, the properties, and any instances of the category are displayed in the standard output that is associated with the workbench. This information can be found in the Console view of the workbench.

Note: The standard output for a workbench started with the standard Eclipse icon might not be visible depending on the runtime settings for the workbench.
Artwork for listPropertiesResults

Retrieving registered categories

The following code snippet from the ListPropertiesAction class demonstrates how to retrieve the registered categories:
  
ZOSPropertyGroupManager manager = ZOSPropertyGroupManager.getZOSPropertyGroupManager();
// Retrieve registered categories
List<ICategory> categories = manager.getCategories();

It starts by using the ZOSPropertyGroupManager class, obtained by using the getZOSPropertyGroupManager method of the ZOSPropertyGroupManager class, to retrieve the ICategory objects that represent the registered categories. The registered categories can also be obtained by using the ( LocalPropertyGroupManager) class, since both managers share the Eclipse extension point to register properties.

Retrieving properties by using category objects

The following code snippet from the ListPropertiesAction class demonstrates how to use ICategory objects:
 
for (ICategory category : categories) {
  System.out.println("---------------");
  System.out.println("Category: " + category.getName());
  // Retrieve registered properties for this category.
  List<IPropertyInfo> infos = category.getPropertyInformation();
  for (IPropertyInfo info : infos) {
    System.out.println("  Property: " + info.getName());
  }
  // Retrieve category instances for each registered category, and print
  // the properties and values for each instance.
  List<ICategoryInstance> instances = category.getInstances();
  for (ICategoryInstance instance : instances) {
    System.out.println("  Instance: ");
    List<IProperty> properties = instance.getProperties();
    for (IProperty property : properties) {
    System.out.println("    ----");
    System.out.println("    Property name: " + property.getName());
    System.out.println("    Property value: " + property.getValue());
  }
}

Each category has a name and retrieves IPropertyInfo objects, which have the names of the registered properties for that category.

Each category retrieves ICategoryInstance objects, which hold property values. Finally, the IProperty objects that are retrieved from a ICategoryInstance object have the name of the property and the property value.

The menu item is contributed to the workbench through an Eclipse action set:
  <extension
         id="com.ibm.ftt.api.samples.actionSets"
         name="%apiSample.actionSets"
         point="org.eclipse.ui.actionSets">
      <actionSet label="com.ibm.ftt.api.samples.actionSet1"
            description="Action set for the API samples"
            visible="true"
            id="com.ibm.ftt.api.samples.actionSet1">
         <menu label="%apiMenu.title"
               id="com.ibm.ftt.api.samples.apiMenu">
            <separator name="com.ibm.ftt.api.samples.apiMenu.resourcesAPI"/>
         </menu>
         <action label="%apiMenu.samples.listProperties"
               class="com.ibm.ftt.api.samples.resources.ListPropertiesAction"
               style="push" menubarPath=
    "com.ibm.ftt.api.samples.apiMenu/com.ibm.ftt.api.samples.apiMenu.resourcesAPI"
               id="com.ibm.ftt.api.samples.listPropertiesAction"/>
               ......
      </actionSet>
   </extension>

Specifying a different builder for an MVS subproject

This sample demonstrates how to replace the default builder of an MVS subproject with a new builder. The new builder can be used to change the way that the subproject is built when a build action is requested.

Sample scenario

Create a z/OS project and MVS subproject according to the instructions in the following topics: The act of creating a subproject causes an XML file to be created in which various characteristics and properties of the subproject are maintained. The following illustration shows a portion of this XML file after creation of the subproject:
Artwork for DefaultBuilderXML

To replace the default subproject builder with a different (user-defined) builder, right-click the subproject and select the API Sample Actions > Replace Default Builder action.

This action causes the information in the XML file to be updated. The file might not be updated immediately, but the builder information changes the next time that the file is written. The file looks similar to the following illustration:

Artwork for SampleBuilderXML

If you want to restore the original (default) subproject builder, select the API Sample Actions > Restore Default Builder action.

Subproject builder definition

Subproject builders in Developer for z/OS are similar in concept to an incremental project builder in base Eclipse. They provide a means to build the contents of a subproject, creating new or updated build results. For example, all of the COBOL source files in the subproject can be processed by a COBOL compiler. The resulting files (object, listing, executable file) can be added to the subproject.

To define a custom project builder for a remote subproject, you need to create a class that implements the ILogicalSubProjectBuilder interface. Thus the SampleBuilder class contains the following code:

public class SampleBuilder implements ILogicalSubProjectBuilder {
   ......
}

The ILogicalSubProjectBuilder interface is relatively simple. In SampleBuilder, add a private field subproject for the subproject instance. This sample issues the println statement "Sample Builder called for subProject:". It includes the subproject name.

The complete source code can be found in SampleBuilder.java.

Replacing the default subproject builder

The Replace Default Builder menu item is implemented by the AddSubProjectBuilderAction class. Its run(IAction) method changes the buildSpec of the subproject to replace the name of the default builder class with the name of the sample builder class SampleBuilder. It accomplishes this purpose by using the getBuildSpec() method, finding the old builder in the IBuildCommand list, and updating the BuildSpec by using the setBuildSpec(com.ibm.ftt.projects.core.logical.IBuildCommand newCommands) method.
		IBuildCommand[] buildCommands = subProject.getBuildSpec();

		// look through the existing build commands for the "standard" 
		// RDz builder for MVS subprojects, and if found substitute the
		// sample builder in its place
		for (int i = 0; i < buildCommands.length; ++i) {
			if (buildCommands[i].getBuilderName().equals(IProjectCoreConstants.ZOS_BUILDER_ID)) {
				// copy the commands preceding the RDz builder
				IBuildCommand[] newCommands = new IBuildCommand[buildCommands.length];
				if (i > 0) {
					System.arraycopy(buildCommands, 0, newCommands, 0, i - 1);
				}
				// create a new command for the sample builder
				IBuildCommand buildCommand = getSubProject().newCommand();
				buildCommand.setBuilderName("com.ibm.ftt.api.samples.samplebuilder");
				// add the new command
				newCommands[i] = buildCommand;
				// copy the rest of the build commands
				if (i < buildCommands.length) {
					System.arraycopy(
						buildCommands,
						i + 1,
						newCommands,
						i,
						buildCommands.length - i - 1);
				}
				
				// store the new set of build commands
				subProject.setBuildSpec(newCommands);
				
				System.out.println("Sample builder replaces RDz builder");
				break;
			}
		}

The complete source code can be found in AddSubProjectBuilderAction.java.

The action is contributed by using standard Eclipse contribution mechanisms.

       <objectContribution
         adaptable=“false”
         objectClass=“com.ibm.ftt.projects.core.logical.ILogicalSubProject”
         id=“com.ibm.ftt.api.samples.addbuilder”>
         <visibility>
            <not>
               <objectState
                     name=“projectBuilder”
                     value=“com.ibm.ftt.api.samples.samplebuilder”>
               </objectState>
            </not>
         </visibility>
         <action
            label=“
            class=“com.ibm.ftt.api.samples.builders.AddSubProjectBuilderAction”
            menubarPath=“com.ibm.ftt.api.samples.PopupMenu/group1”
            id=“com.ibm.ftt.api.samples.addbuilderaction”/>
      </objectContribution>

Restoring the default subproject builder

The Restore Default Builder menu item is implemented by the RemoveSubProjectBuilderAction class. Its run(IAction) method changes the buildSpec of the subproject to replace the name of the sample builder class SampleBuilder with the name of the default builder class. It accomplishes this purpose by using the getBuildSpec() method, finding the old builder in the IBuildCommand list, and updating the BuildSpec by using the setBuildSpec(com.ibm.ftt.projects.core.logical.IBuildCommand newCommands) method.
      
		IBuildCommand[] buildCommands = subProject.getBuildSpec();

		// look through the existing build commands for the sample 
		// builder, and if found substitute the "standard" RDz builder
		// in its place
		for (int i = 0; i < buildCommands.length; ++i) {
			if (buildCommands[i].getBuilderName().equals("com.ibm.ftt.api.samples.samplebuilder")) {
				// copy the commands preceding the sample builder
				IBuildCommand[] newCommands = new IBuildCommand[buildCommands.length];
				if (i > 0) {
					System.arraycopy(buildCommands, 0, newCommands, 0, i - 1);
				}
				// create a new command for the RDz builder
				IBuildCommand buildCommand = getSubProject().newCommand();
				buildCommand.setBuilderName(IProjectCoreConstants.ZOS_BUILDER_ID);
				// add the new command
				newCommands[i] = buildCommand;
				// copy the rest of the build commands
				if (i < buildCommands.length) {
					System.arraycopy(
						buildCommands,
						i + 1,
						newCommands,
						i,
						buildCommands.length - i - 1);
				}
				
				// store the new set of build commands
				subProject.setBuildSpec(newCommands);
				
				System.out.println("RDz builder replaces Sample builder");
				break;
			}
		}

The complete source code can be found in RemoveSubProjectBuilderAction.java.

The action is contributed by using standard Eclipse contribution mechanisms.

                  <objectContribution
         adaptable=“false”
         objectClass=“com.ibm.ftt.projects.core.logical.ILogicalSubProject”
         id=“com.ibm.ftt.api.samples.removebuilder”>
         <filter
            value=“com.ibm.ftt.api.samples.samplebuilder”
            name=“projectBuilder”/>
         <action
            label=“
            class=“com.ibm.ftt.api.samples.builders.RemoveSubProjectBuilderAction”
            menubarPath=“com.ibm.ftt.api.samples.PopupMenu/group1”
            id=“com.ibm.ftt.api.samples.removebuilderaction”/>
      </objectContribution>

Submitting JCL and waiting for output

This sample demonstrates how to submit JCL and wait for the output.

Sample scenario

To submit JCL and wait for the output, select a JCL file from the z/OS Projects view and then select the API Sample Actions > Submit and Wait action.