Working with Model Entities

This chapter explains how to work with nodes of the runtime Model, such as Components and Features.

This chapter covers the following topics:

Accessing Runtime Nodes

The root component, and every other node in the underlying runtime Model tree, implements the IRuntimeNode interface. This interface exposes several attributes of the configuration model, such as the type of the node (based on a set of node type constants), its name, the node ID, a runtime ID that is unique to this node across all nodes created by this particular Configuration, the parent node (which is null for the root component), a (possibly empty) collection of children, and information about whether this part of the runtime tree has been satisfied. See Introspection through IRuntimeNode.

Opportunities for Modifying the Configuration

During a configuration session, there are certain optimal points for modifying the configuration.

Note: This use of the CIO is intended for Configurator Extensions.

To get the runtime configuration to which a node belongs, use IRuntimeNode.getConfiguration() .

The code fragment in Getting the Configuration from a Runtime Node shows how to get the Configuration object associated with the a node in the runtime Oracle Configurator. You choose the node by binding the node parameter in a Configurator Extension rule.

Getting the Configuration from a Runtime Node

public Configuration getConfig (IRuntimeNode node) {
    // Get the the current configuration from the bound node
    Configuration config = node.getConfiguration();
    return config;
}

You can modify a configuration by using a Configurator Extension bound to one of the configuration events described in Events for Processing Configurations, Types of Configuration Events, and the chapter on Configurator Extensions in the Oracle Configurator Developer User’s Guide.

For instance, if you want to modify the configuration immediately after a new configuration session has been initialized, then bind your Configurator Extension to the postConfigNew event.

Modifying the configuration through a Configurator Extension is sometimes referred to as side-effecting it.

Caution: Be careful of recursion when using the events postValueChange and onConfigValidate, which are triggered when a change to the configuration is detected by Oracle Configurator. It is possible to enter an infinite loop in which changes that you make in your Configurator Extension trigger an event that makes the Configurator Extension run again. See Avoiding Circularity and Recursion for more details.

Be careful when binding a Configurator Extension to the postCXInit event, since that event always occurs when a configuration session begins.

Accessing Components

The CIO represents instantiable components with two structures that are used together: Component and ComponentSet. An individual instance of a component is represented by the interface Component. A set of these instances of a given component is represented by an instance of the class ComponentSet. Both structures inherit from the interface IRuntimeNode.

In Oracle Configurator Developer, there is no element that corresponds to a ComponentSet, but you can control the Instantiability settings for a node. The Instantiability settings for initial minimum and initial maximum determine the minimum and maximum number of instances that can be added at runtime. Components that have a minimum number of instances of 1 and a maximum number of instances of 1 are called required components. Components that have a minimum number of instances of 0 and a maximum number of instances of 1 or more are called instantiable components. See the Oracle Configurator Developer User’s Guide for details about required and instantiable components.

Adding and Deleting Instantiable Components

Note: This use of the CIO is intended for both custom applications and Configurator Extensions.

It is most likely that you would add or delete instantiable components in a Configurator Extension.

Use ComponentSet.add() to add an instantiable component. The result is a new object that uses the Component interface.

The add() method can throw a LogicalException if adding the component causes a logical contradiction.

Use ComponentSet.delete() to delete an instantiable component.

In the user interface for the runtime Oracle Configurator, a configurable component is normally represented by a single screen. The screen that represents the parent node of this component contains a button that adds instances of the component, producing a new component screen and a new Component object. This is equivalent to adding instances through ComponentSet.add(). The screen representing the configurable component itself contains a button that deletes that instance of the component. This is equivalent to deleting the instance through ComponentSet.delete().

In a user interface generated by Oracle Configurator Developer, when the end user adds an instance of an instantiable component that is a BOM Model (which is represented by a BomInstance object), that instance is automatically selected. If the addition causes any contradictions, the appropriate messages are displayed. However, if you use a Configurator Extension to add an instance of a BOM Model, that instance is not automatically selected. If you want your Configurator Extension to select the instance, you must do it explicitly, as shown in Adding and Selecting an Instance of a BOM Model. Instantiable components that do not represent BOM Models cannot be selected.

Adding and Selecting an Instance of a BOM Model

...
ComponentSet compSet = (ComponentSet)comp1.getChildByName("My Model");
Component comp = compSet.add();
if (comp instanceof BomModel) {
   (BomInstance(comp)).select();
}
...

See Restoring Configurations for information on the effects of changes to Instantiability settings in Oracle Configurator Developer when restoring configurations in which instances have been added, deleted, or modified.

Note: There are some performance problems that can arise when adding and deleting several instantiable components. See the Oracle Configurator Modeling Guide for details.

Renaming Instances of Components

During a configuration session, when the end user of the runtime Oracle Configurator creates a new instance of a configurable component, the user interface displays a distinctive name for the instance.

For more information on controlling the display of instance names in the runtime Oracle Configurator, see the Oracle Configurator Implementation Guide.

You can access the default name that is displayed in the runtime user interface, by using the methods setInstanceName(), getInstanceName(), and hasInstanceName() in the interface Component.

You can use setInstanceName() to set the name of an instance of an instantiable component. The component to be renamed cannot be a required component. The name that you set persists when you restore the configuration that contains the instance.

You can use hasInstanceName(), and getInstanceName() to test whether the name of an instance has been set, and to return the name.

For a fragmentary example of how to change the name of an instance, see Renaming an Instance of a Component.

Renaming an Instance of a Component

...
String inputText = "My Instance Name";
ComponentSet compSet = (ComponentSet)comp1.getChildByName("My Model");
Component comp = compSet.add();
comp.setInstanceName(inputText);
...

For a full example of how to change the name of an instance, see Sample Java Code for Configurator Extension (InstanceNameChange.java).

Accessing Features

There are several specialized types of Features. Each Feature type implements the IRuntimeNode interface, enabling you to use its general methods for working with runtime nodes (see Introspection through IRuntimeNode). Each type also implements its own interface with appropriately specialized methods.

The table below, Interfaces for Features, lists the types of Features that you can work with in the CIO, the types of their values, and the CIO interface for working with them.

Interfaces for Features
CIO Interface Feature Type Description
IState Boolean boolean state (true/false/unknown)
IDecimal Decimal floating point numeric
IInteger Integer integer numeric
The value can be positive, negative, or zero.
IText Text string
ICount, IState Count boolean, with an associated integer-valued numeric count
IOptionFeature Option Feature An OptionFeature itself can have a logic state, a count (if Option Quantities are enabled), or a Satisfaction state
The children of an Option Feature are Options, accessed with the interface IOption.

Some of these types require special comment:

Getting and Setting Logic States

To interact with objects that have a logic state, you use methods of the IState interface. This interface contains:

Observe the following practices when you use methods of the IState interface:

Getting and Setting Numeric Values

You can use the following methods to get and set the values of objects that have numeric values. Consult the CIO reference (see Reference Documentation for the CIO) for the hierarchy of the classes you wish to use.

For decimal values, use:

For integer values, use:

The code fragment in Setting a Numeric Value uses setIntValue() to change the value of an Integer Feature. Note that you can use the generalized IRuntimeNode interface for flexibility in getting a child node, and then cast the node object to a particular interface to perform the desired operation on it.

Setting a Numeric Value

// select a node by name
IRuntimeNode limit = baseComp.getChildByName("Current Limit");

// use an interface cast to set the node’s value by the desired type
((IInteger)limit).setIntValue(5);

To determine whether a numeric value has violated its Minimum or Maximum range, you may need to iterate through the collection of validation failures returned by Configuration.getValidationFailures() after setting a value, for instance with IInteger.setIntValue(). See Validating Configurations for more background.

There is a subtlety that you should take note of. IDecimal.setDecimalValue() does not throw a LogicalException when setting the value of a decimal feature that exceeds the feature's minimum/maximum limits. The collection of validation failures returned by Configuration.getValidationFailures() does not include any failures that result from setting a numeric value until the logic transaction has been closed. Thus, there is no way to roll back a transaction once it is committed. You can only undo the setting of the value. Here is a suggested method for dealing with this situation:

Caution: The classes Total and Resource both inherit the methodsetDecimalValue() from DecimalNode. This method provides the ability to set the value of Totals and Resources programmatically (rather than in the runtime application as the result of user actions). However, the use of this method, while permitted, is deprecated, and may be removed in a future release. When working programmatically with Totals and Resources, use only the methods inherited from ReadOnlyDecimalNode.

  1. Open a transaction.

  2. Get the minimum or maximum for the Feature, with getMin() or getMax().

  3. Set the new value appropriately.

  4. Close the transaction.

  5. Get the collection of validation failures for the configuration, to find out about the status of other nodes.

  6. If the last transaction caused a minimum/maximum violation, then call Configuration.undo(), which retracts the last action in the transaction.

This situation illustrates why it is a good practice to perform the setting of a single value inside a logic transaction. You can always undo the transaction if the result is unsatisfactory. Remember: inside a transaction, you can roll back an action; outside a transaction, you undo an action.

Working with Decimal Quantities

Quantities for imported BOM Standard Items can be either integers or decimals.

The table Methods for Integer and Decimal Nodes lists certain methods of CIO classes and interfaces that are relevant to decimal quantities. The table indicates the corresponding methods to be used for BOM nodes having Integer (indivisible) values or Decimal (divisible) values. Using the wrong type of method raises an IncompatibleValueException. For details on these methods, see Reference Documentation for the CIO.

In the classes IRuntimeNode and RuntimeNode, the methods hasIntegerValue() and hasDecimalValue() should be used to find out if a runtime node belongs to a Decimal or an Integer BOM.

StateCountNode.getDecimalCount() is a general method for getting the count and works for both Integer and Decimal BOMs.

Methods for Integer and Decimal Nodes
Class/Interface Integer Method Decimal Method
BomNode getDefaultQuantity() getDecimalDefaultQuantity()
BomNode getMaxQuantity() getDecimalMaxQuantity()
BomNode getMinQuantity() getDecimalMinQuantity()
IBomItem getMaxQuantity() getDecimalMaxQuantity()
IBomItem getMinQuantity() getDecimalMinQuantity()
ICount getCount() getDecimalCount()
ICount setCount() setDecimalCount()
StateCountNode getCount() getDecimalCount()
StateCountNode setCount() setDecimalCount()

When using one of the methods listed in Methods for Integer and Decimal Nodes, always check for deleted or discontinued nodes. See Checking for Deleted or Discontinued Nodes.

Accessing Properties

You can determine which Properties belong to a runtime node, then use methods of the class Property to obtain information about the Properties.

Use IRuntimeNode.getProperties() to get a collection of the properties associated with a node.

Use IRuntimeNode.getPropertyByName() to get a particular property of a node, based on its name.

When you have the Property, use methods of the class Property, such as getStringValue(), to obtain specific information.

User String Properties

If you need to dynamically associate text strings with runtime nodes, and save them with the configuration, then you can use the set of accessor methods in the IRuntimeNode interface that are listed in Methods for User Strings.

These methods set and get the values of the System Properties UserStr01, UserStr02, UserStr03, and UserStr04, which are available on runtime nodes.

Methods for User Strings
System Property Setter Method Getter Method
UserStr01 setUserStr01() getUserStr01()
UserStr02 setUserStr02() getUserStr02()
UserStr03 setUserStr03() getUserStr03()
UserStr04 setUserStr04() getUserStr04()

You can only set the values of these properties by using these methods, in a Configurator Extension or custom user interface, by using the setter methods listed here. The values must be set at runtime, and are not saved with the configuration.

To display the values of one or more of these properties in a generated User Interface, you can add a UI element such as Styled Text, and derive its value from one of the System Properties listed here. For details about modifying generated User Interfaces, see the Oracle Configurator Developer User's Guide.

For an example of setting these System Properties in a Configurator Extension, see Setting User Strings.

Setting User Strings

package oracle.apps.cz.cx;
import oracle.apps.cz.cio.*;

public class UserString {
  public UserString()  {
  }

/**
 * Sets the user string value on the Node.
 * CX event: postConfigNew and postConfigRestore
 * BaseNode: Node on which you want to set the user string value.
 * Event Scope: Global
 */
  public void onSessionLoad(IRuntimeNode node1) {
    node1.setUserStr01("setUserStr01 for " +node1.getName() + "["+node1.getRuntimeID()+"]" );
    node1.setUserStr02("setUserStr02 for " +node1.getName() + "["+node1.getRuntimeID()+"]" );
    node1.setUserStr03("setUserStr03 for " +node1.getName() + "["+node1.getRuntimeID()+"]" );
    node1.setUserStr04("setUserStr04 for " +node1.getName() + "["+node1.getRuntimeID()+"]" );
  }
}

Access to Options

An Option is a child of an Option Feature which supports a boolean state (true, false, or unknown) and a count. Options implement the IRuntimeNode interface.

OptionFeature objects have special methods for selecting options and querying for selected options. See Accessing Features for information about methods for working directly with Features.

In a custom application, you can use IOPtionFeature.select() to select a specified Option. If a maximum number of selections has been defined for an OptionFeature, and that maximum has been reached, then this method implements mutual exclusion behavior by first deselecting the most recently selected Option that does not cause a contradiction when deselected, then selecting the newly specified option. The minimum number of selections defined for the OptionFeature does not affect this behavior.

You can find out which Option has been deselected, after a selection is committed, by using IOPtionFeature.getSelectedOptions() and examining the list of selected nodes.

The getSelectedOption() method throws the SelectionNotMutexedException if this feature does not support (mutexed) selections.

You can use the interface IOption to select, deselect, and determine the selection state of Options. The table Methods of the Interface IOption lists these methods.

Methods of the Interface IOption
Method Action
deselect() Deselect this Option.
isSelected() Returns true if this Option is selected, and false otherwise. When using isSelected(), always check for deleted or discontinued nodes. See Checking for Deleted or Discontinued Nodes.
select() Select this Option.

The code fragment in Testing Whether an Option Is Selected displays a "check" icon if an Option of a runtime node is selected:

Testing Whether an Option Is Selected

IRuntimeNode rtNode = (IRuntimeNode)value;
if (value instanceof IOption) {
  IOption optionNode = (IOption)value;
  if !(optionNode.isDeleted() || optionNode.isDiscontinued()) {
    if (optionNode.isSelected()) {
      setIcon(checkIcon);
    }
  }
} 

In this example, assume that checkIcon points to an icon file, and that setIcon() is a custom method that displays it.

Introspection through IRuntimeNode

You can get information about a node in a Model at runtime by using methods of the interface IRuntimeNode. This helps you to write "generic" Configurator Extensions, which can interact with a Model tree dynamically, without having prior knowledge of its structure. Important Methods of the Interface IRuntimeNode lists some of the more important of these methods.

The table Important Methods of the Interface IRuntimeNode lists some of the methods defined in the interface IRuntimeNode that you are most likely to use in working with the CIO. For more detail about these and the other CIO interfaces, see Reference Documentation for the CIO.

Important Methods of the Interface IRuntimeNode
Method Action
getCaption() Get the Caption of this node to be displayed in messages.
getChildByID() Gets a particular child identified by its ID.
ComponentSet.getChildByID() could have duplicate children with same ID, so it returns only the first child. Instead, call getChildByInstanceNumber() or change the instance name.
getChildByName() Gets a particular child identified by its name.
getChildren() Gets the children of this runtime configuration node.
getDescription() Returns the design-time description of the runtime node.
getName() Gets the name of the node.
getParent() Gets the parent of the node.
getProperties() Returns a collection of the properties associated with this node. The collection contains items of the type Property.
getRuntimeID() Gets the runtime ID of the node.
getType() Gets the type of this node.
isEffective() Returns true if this particular node is effective given the effectivity criteria of the model.
  Returns true if the "Include in Generated UI" flag is selected for this node in Oracle Configurator Developer. Note that the value of this flag may not reflect the true visibility of this node in the UI. See the note elsewhere in this section.
isUnsatisfied() Returns true if this particular node, or any one of its children, has not been completely configured.

Regarding the method getIncludeInGeneratedUIFlag(), which is described in the table Important Methods of the Interface IRuntimeNode, be aware that the "Include in Generated UI" flag can be misleading, as shown in the following examples:

The code fragment in Getting a Child Node by Name creates a Configuration object config, sets rootComp to the root component of the configuration, and sets userType to the child node with the user-visible name "User Type".

Getting a Child Node by Name

...
Configuration config = m_cio.startConfiguration(params, context);
IRuntimeNode rootComp = (IRuntimeNode) config.getRootComponent();

IRuntimeNode userType = rootComp.getChildByName("User Type");
...

The code fragment in Collecting All Child Nodes by Type uses a test for the value of the TEXT_FEATURE field of an IRuntimeNode object named comp to gather a list of all the children of that node that are TextFeature objects. It is assumed that traverseTree() is a custom method.

Collecting All Child Nodes by Type

//get all the text features
List textFeatList = IRuntimeNode comp.getChildrenByType(IRuntimeNode.TEXT_FEATURE);
traverseTree(comp.getChildComponentNodes(),
             IRuntimeNode.TEXT_FEATURE,
             textFeatList);
Iterator iter = textFeatList.iterator();