This chapter explains how to work with nodes of the runtime Model, such as Components and Features.
This chapter covers the following topics:
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.
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.
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.
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.
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).
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 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.
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:
Option Features are represented by OptionFeature objects. An OptionFeature has a logic value. If the Option Feature is satisfied, the value is TRUE. The values of an OptionFeature object are Options.
You can use the methods getMinSelected() and getMaxSelected(), of IOptionFeature, to determine the minimum and maximum number of a Feature’s child Options that can be selected. If you do, first use hasMinSelected() or hasMaxSelected() to determine whether there is a minimum or maximum number of Options. You can use areOptionsCounted() to determine whether the Feature has Counted Options.
Keep in mind that an end user of the runtime Oracle Configurator can select an Option of an Option Feature, but not the Option Feature itself. However, in a Configurator Extension, it is possible to use select() to select an OptionFeature object itself. You should avoid selecting OptionFeature objects. If you do so, and save the configuration, then, when you later restore the configuration, this selection is not applied, and will produce a RestoreValidationFailure.
See Access to Options for information about methods for working directly with Options.
CountFeature objects have an associated integer-valued numeric count, and are a special case of IntegerFeature that has a count greater than or equal to zero. CountFeature objects behave like counted options in an OptionFeature.
Note: In Oracle Configurator Developer, if you set the minimum count of an Integer Feature greater than or equal to zero, then at runtime the CIO treats this Feature as a CountFeature object. If you set the minimum count to less than zero, then the CIO treats this Feature as an IntegerFeature object. When working with runtime nodes, you must consider this distinction to ensure that you are working with the expected set of objects. For example, if you use IRuntimeNode.getChildrenByType() to collect Integer Feature objects, then you must make two calls, one with an IRuntimeNode.COUNT_FEATURE argument, and another with an IRuntimeNode.INTEGER_FEATURE argument.
To interact with objects that have a logic state, you use methods of the IState interface. This interface contains:
A set of constants that represent input states, used to specify a new state for an object, listed in the table Input Logic States:
A set of constants that represent output states, returned when querying an object for its state listed in the table Output Logic States:
A set of methods for getting and setting the object's state listed in the table Methods for Getting and Setting State:
Observe the following practices when you use methods of the IState interface:
The code fragment in Getting the State of a Node uses getState() with UTRUE to test whether the state of an Option node is user true, meaning that the Option has been selected by the end user.
// Get the necessary components from the configuration. baseComponent = (Component)comp_node.getChildByName("Component-1"); of = (OptionFeature)baseComponent.getChildByName("Feature-1"); op = (Option)of.getChildByName("Option-1"); intFeat = (IntegerFeature)baseComponent.getChildByName("IF-1"); // Check if the option is set to UTRUE. // If so, set the Integer value to 5. if( op.getState() == IState.UTRUE ) intFeat.setIntValue(5);
When using getState(), Always check for deleted or discontinued nodes. See Checking for Deleted or Discontinued Nodes.
Using isUnknown(), which returns TRUE if the Feature is in an unknown state, is important when a node is cast to an integer or decimal class such as IntegerNode or ReadOnlyDecimalNode. When the numeric value of the node is zero, a zero value can mean either UNKNOWN (if no value has been set by the user) or KNOWN (if the value has been set to zero by the user).
The code fragment in Setting the State of a Node, which uses setState() with TOGGLE, toggles the state of the selected item in the Model tree.
private void toggleSelectedItem() { IState node = (IState)getSelectedNode(); node.setState(IState.TOGGLE); }
You should not use the TOGGLE state unless you are working with a user interface. If you do not need to render the result in the interface (for instance, if you are using batch validation) then it is much more efficient to set the state directly:
node.setState(IState.TRUE); ... node.setState(IState.FALSE);
If you do need to use TOGGLE, do not turn off defaulting, because the CIO must turn defaulting on in order to determine the correct state to toggle to. This operation impairs performance.
If you try to set the state of a RuntimeNode to UNKNOWN and this causes a contradiction, then the CIO throws a nonoverridable LogicalException. For example, assume the following Model structure:
M |_A (Boolean, UNKNOWN) |_B (Boolean, UNKNOWN)
And a logic rule:
A Requires B
When you select A, it makes B LTRUE. If you try setting B to UNKNOWN, you get a nonoverridable logical contradiction:
A.setState(IState.UTRUE); ... try { B.setState(IState.UNKNOWN); } catch (LogicalException le) { //le is not overridable
When you are not interested in the difference between UTRUE and LTRUE, the proper way to determine whether the state of a node is true is to call IState.isTrue().
By contrast, if you test the state of the node this way:
(state == IState.TRUE)
then the test only returns TRUE if the logic state is UTRUE, but not if it is LTRUE.
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.
// 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.
Open a transaction.
Get the minimum or maximum for the Feature, with getMin() or getMax().
Set the new value appropriately.
Close the transaction.
Get the collection of validation failures for the configuration, to find out about the status of other nodes.
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.
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.
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.
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.
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.
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.
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()+"]" ); } }
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.
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.
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.
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 flag is true but the node does not appear in the runtime UI because:
The node has an ancestor whose flag is false
The node is hidden by a display condition
The flag is false but the node does appear in the runtime UI because:
The "Show All Nodes" flag was set when the UI was generated
The node was manually added to the UI
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".
... 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();