Skip Headers
Oracle® OLAP Developer's Guide to the OLAP API
10g Release 2 (10.2)

Part Number B14347-01
Go to Documentation Home
Home
Go to Book List
Book List
Go to Table of Contents
Contents
Go to Index
Index
Go to Master Index
Master Index
Go to Feedback page
Feedback

Go to previous page
Previous
Go to next page
Next
View PDF

6 Understanding Source Objects

This chapter introduces Source objects, which you use to specify a query. With a Source, you specify the data that you want to retrieve from the data store and the analytical or other operations that you want to perform on the data. Chapter 7, "Making Queries Using Source Methods", provides examples of using Source objects. Using Template objects to make modifiable queries is discussed in Chapter 11, "Creating Dynamic Queries".

This chapter includes the following topics:

For the complete code for most of the examples in this chapter, see the example programs available from the Overview of the Oracle OLAP Java API Reference.

Overview of Source Objects

After you have used the classes in the oracle.olapi.metadata.mdm package to get MdmSource objects that represent measures and dimensions in the OLAP Catalog, you can get Source objects from them. You can also create other Source objects with methods of a DataProvider. You can then use the Source objects to create a query that specifies the data that you want to retrieve from the database. To retrieve the data, you create a Cursor for the Source.

With the methods of a Source, you can specify selections of dimension or measure values and specify operations on the elements of the Source, such as mathematical calculations, comparisons, and ordering, adding or removing elements of a query. The Source class has a few basic methods and many shortcut methods that use one or more of the basic methods. The most complex basic methods are the join(Source joined, Source comparison, int comparisonRule, boolean visible) method and the recursiveJoin(Source joined, Source comparison, Source parent, int comparisonRule, boolean parentsFirst, boolean parentsRestrictedToBase, int maxIterations, boolean visible) method. The many other signatures of the join and recursiveJoin methods are shortcuts for certain operations of the basic methods.

In this chapter, the information about the join method applies equally to the recursiveJoin method, except where otherwise noted. With the join method, you can select elements of a Source and, most importantly, you can relate the elements of one Source to those of another Source. For example, to specify the dimension members that retrieving the data of a measure requires, you use a join method to relate the dimension to the measure.

A Source has certain characteristics, such as a type and a data type, and it sometimes has one or more inputs or outputs. This chapter describes these concepts. It also describes the different kinds of Source objects and how you get them, the join method and other Source methods, and how you use those methods to specify a query.

Kinds of Source Objects

The kinds of Source objects that you use to specify data and to perform analysis, and the ways that you get them, are the following:

The Source class has the following subclasses:

These subclasses have different data types and implement Source methods that require those data types. Each subclass also implements methods unique to it, such as the implies method of a BooleanSource or the indexOf method of a StringSource.

Characteristics of Source Objects

A Source has a data type and a type, a Source identification (ID), and a SourceDefinition. This topic describes these concepts. Some Source objects have one or more inputs or outputs. Those complex concepts are discussed in the "Inputs and Outputs of a Source" topic. Some Source objects have an associated Model object, which is discussed in the "Model Objects and Source Objects" topic.

Data Type of a Source

As described in Chapter 2, "Understanding OLAP API Metadata", the OLAP API has a class, FundamentalMetadataObject, that represents the data type of the elements of an MdmSource. The data type of a Source is represented by a fundamental Source. For example, a BooleanSource has elements that have Java boolean values. The data type of a BooleanSource is the fundamental Source that represents OLAP API Boolean values.

To get the fundamental Source that represents the data type of a Source, call the getDataType method of the Source. You can also get a fundamental Source by calling the getSource method of a FundamentalMetadataObject.

Example 6-1 demonstrates getting the fundamental Source for the OLAP API String data type, the Source for the data type of an MdmPrimaryDimension, and the Source for the data type of the Source for the MdmPrimaryDimension, and comparing them to verify that they are all the same object. In the example, dp is the DataProvider and mdmProdDim is the MdmPrimaryDimension for the Product dimension.

Example 6-1 Getting the Data Type of a Source

FundamentalMetadataProvider fmp = dp.getFundamentalMetadataProvider();
FundamentalMetadataObject fmoStringDataType = fmp.getStringDataType();
Source stringDataTypeSource = fmoStringDataType.getSource();
FundamentalMetadataObject fmoMdmProdDimDataType =
                                               mdmProdDim.getDataType();
Source mdmProdDimDataTypeSource = fmoMdmProdDimDataType.getSource();
Source prodDim = mdmProdDim.getSource();
Source prodDimDataTypeSource = prodDim.getDataType();
if(stringDataTypeSource == prodDimDataTypeSource &&
   mdmProdDimDataTypeSource == prodDimDataTypeSource) 
  System.out.println("The Source objects for the data types are all the same.");
else
  System.out.println("The Source objects for the data types are not " +
                     "all the same.");

The example displays the following:

The Source objects for the data types are all the same.

Type of a Source

Along with a data type, a Source has a type, which is the Source from which the elements of the Source are drawn. The type of a Source determines whether the join method can match the Source to an input of another Source. The only Source that does not have a type is the fundamental Source for the OLAP API Value data type, which represents the set of all values, and from which all other Source objects ultimately descend.

The type of a fundamental Source is its data type. The type of a list or range Source is the data type of the values of the elements of the list or range Source.

The type of a primary Source is one of the following:

  • The fundamental Source that represents the data type of the values of the elements of the primary Source. For example, the Source returned by getSource method of a typical MdmMeasure is the fundamental Source that represents the set of all OLAP API number values.

  • The Source for the MdmSource of which the MdmSource of the primary Source is a component. For example, the type of the Source returned by the getSource method of an MdmLevelHierarchy is the Source for the MdmPrimaryDimension of which the hierarchy is a component.

The type of a derived Source is one of the following:

  • Its base Source, which is the Source whose method returned the derived Source. A Source returned by the alias, extract, join, recursiveJoin, or value methods, or one of their shortcuts, has its base Source as its type. An exception is the derived Source returned by the distinct method, whose type is the type of its base Source rather than the base Source itself.

  • A fundamental Source. Methods such as position and count return a Source that has the fundamental Source for the OLAP API Integer data type as its type. Methods that make comparisons, such as eq, le, and so on, return a Source that has the fundamental Source for the Boolean data type as its type. Methods that perform aggregate functions, such as the NumberSource methods total and average, return as the type of the Source a fundamental Source that represents the function.

You can find the type of a Source by calling its getType method.

A Source derived from another Source is a subtype of the Source from which it is derived. You can use the isSubtypeOf method to determine if a Source is a subtype of another Source.

For example, in Example 6-2 the myList object is a list Source. The example uses myList to select values from prodRollup, a Source for the default MdmLevelHierarchy of the MdmPrimaryDimension for the Product dimension. In the example, dp is the DataProvider.

Example 6-2 Using the isSubtypeOf Method

Source myList = dp.createListSource(new String[] {
                                     "PRODUCT_ROLLUP::FAMILY::4",
                                     "PRODUCT_ROLLUP::FAMILY::5",
                                     "PRODUCT_ROLLUP::FAMILY::7",
                                     "PRODUCT_ROLLUP::FAMILY::8"});
Source prodSel = prodRollup.selectValues(myList);
if (prodSel.isSubtypeOf(prodRollup))
  System.out.println("prodSel is a subtype of prodRollup.");
else
  System.out.println("prodSel is not a subtype of prodRollup.");

Because prodSel is a subtype of prodRollup, the condition in the if statement is true and the example displays the following:

prodSel is a subtype of prodRollup.

The type of both myList and prodRollup is the fundamental String Source. The type of prodSel is prodRollup because the elements of prodSel are derived from the elements of prodRollup.

The supertype of a Source is the type of the type of a Source, and so on, up through the types to the Source for the fundamental Value data type. For example, the fundamental Value Source is the type of the fundamental String Source, which is the type of prodRollup, which is the type of prodSel. The fundamental Value Source and the fundamental String Source are both supertypes of prodSel. The prodSel Source is a subtype of prodRollup, and of the fundamental String Source, and of the fundamental Value Source.

Source Identification and SourceDefinition of a Source

A Source has an identification, an ID, which is a String that uniquely identifies it during the current connection to the database. You can get the identification of a Source by calling its getID method. For example, the following code gets the identification of the Source for the MdmPrimaryDimension for the Product dimension and displays the value.

System.out.println("The Source ID of prodDim is " + 
                     prodDim.getID());

The preceding code displays the following:

The Source ID of prodDim is Hidden..D_GLOBAL.PRODUCT

The text displayed by Example 6-9 has several examples of Source identifications.

Each Source has a SourceDefinition object, which records information about the Source. The different kinds of Source objects have different kinds of SourceDefinition objects. For example, the fundamental Source for an MdmPrimaryDimension has an MdmSourceDefinition, which is a subclass of HiddenDefinition, which is a subclass of SourceDefinition.

The SourceDefinition of a Source that is produced by a call to the join method is an instance of the JoinDefinition class. From a JoinDefinition you can get information about the parameters of the join operation that produced its Source, such as the base Source, the joined Source, the comparison Source, the comparison rule, and the value of the visible parameter.

Inputs and Outputs of a Source

The inputs and the outputs of a Source are complex and powerful aspects of the class. This section describes the concepts of inputs and outputs and provides examples of how they are related.

Inputs of a Source

A Source that has inputs is a dimensioned Source. An input of a Source is also a Source. An input indicates that the values of the dimensioned Source depend upon an unspecified set of values of the input. A Source that matches to the input provides the values that the input requires. You match an input to a dimensioned Source by using the join method. For information on how to match a Source to an input, see "Matching a Source To an Input".

Certain Source objects always have one or more inputs. They are the Source objects for the MdmDimensionedObject subclasses MdmMeasure and MdmAttribute. They have inputs because the values of a measure or attribute are specified by the values of their dimensions. The inputs of the Source for the measure or attribute are the Source objects for the dimensions of the measure or the attribute. Before you can retrieve the data for a measure or an attribute, you must match each input to a Source that provides the required values.

Some Source methods produce a Source that has an input. You can produce a Source that has an input by using the extract, position, or value methods. These methods provide a means of producing a Source whose elements are a subset of the elements of another Source. A Source produced by one of these methods has its base Source as an input.

For example, in the following code, the base Source is prodRollup. Its value method produces prodRollupValues, which has prodRollup as an input.

Source prodRollupValues = prodRollup.value();

The input provides the means to select values from prodRollup, as demonstrated by Example 6-2. The selectValues method in Example 6-2 is a shortcut for the following join method.

Source prodSel = prodRollup.join(prodRollup.value(),
                                 myList,
                                 Source.COMPARISON_RULE_SELECT,
                                 false);

The parameters of the join method specify the elements of the base Source that appear in the resulting Source. In the example, the joined parameter is the Source produced by the prodRollup.value() method. The resulting unnamed Source has prodRollup as an input. The input is matched by the base of the join method, which is also prodRollup. The result of the join operation, prodSel, has the values of prodRollup that match the values of prodRollup that are in the comparison Source, myList.

If the joined Source were prodRollup and not the Source produced by prodRollup.value(), then the comparison would be between the Source object itself and the values of the comparison Source and not between the values of the Source and the values of the comparison Source. Because the joined Source object does not match any of the values of the comparison Source, the result of the join method would have all of the elements of prodRollup instead of having only the values of prodRollup that are specified by the values of the joined Source that match the values of the comparison Source as specified by the comparison rule.

The input of a Source produced by the position or value method, and an input intrinsic to an MdmDimensionedObject, are regular inputs. A regular input causes the join method, when it matches a Source to the input, to compare the values of the comparison Source to the values of the Source that has the input rather than to the input Source itself.

The input of a Source produced by the extract method is an extraction input. An extraction input differs from a regular input in that, when a value of the Source that has the extraction input is a Source, the join method extracts the values of the Source that is a value of the Source that has the input. The join method then compares the values of the comparison Source to the extracted values rather than to the Source itself.

A Source can have from zero to many inputs. You can get all of the inputs of a Source by calling its getInputs method, the regular inputs by calling its getRegularInputs method, and its extraction inputs by calling its getExtractionInputs method. Each of those methods returns a Set of Source objects.

Outputs of a Source

The join method returns a Source that has the elements of its base Source that are specified by the parameters of the method. If the value of the visible parameter is true, then the joined Source becomes an output of the returned Source. An output of a Source returned by the join method has the elements of the joined Source that specify the elements of the returned Source. An output is a means of identifying the elements of the joined Source that specify the elements of the Source that has the output.

A Source can have from zero to many outputs. You can get the outputs of a Source by calling its getOutputs method, which returns a List of Source objects.

A Source with more than one output has one or more elements for each set of the elements of the outputs. For example, a Source that represents a measure that has had all of its inputs matched, and has had the Source objects that match the inputs turned into outputs, has a single type element for each set of the elements of its outputs because each data value of the measure is identified by a unique set of the values of its dimensions. A Source that represents dimension values that are selected by some operation performed on the data of a measure, however, might have more than one element for each set of the elements of its outputs. An example is a Source that represents product values that have unit costs greater than a certain amount. Such a Source might have several products for each time period that have a unit cost greater than the specified amount.

Example 6-3 produces a selection of the elements of shipRollup, which is a Source for a hierarchy of a dimension of customer values. The customers are grouped by a shipment origination and destination hierarchy.

Example 6-3 Using the join Method To Produce a Source Without an Output

Source custValuesToSelect = dp.createListSource(new String[]
                                    {"SHIPMENTS_ROLLUP::REGION::9",
                                     "SHIPMENTS_ROLLUP::REGION::10"});
Source shipRollupValues = shipRollup.value();
Source custSel = shipRollup.join(shipRollupValues, 
                                 custValuesToSelect,
                                 Source.COMPARISON_RULE_SELECT, 
                                 false);

The shipRollupValues Source has an input of shipRollup. In the join method in the example, the base Source, shipRollup, matches the input of the joined Source, shipRollupValues because the base and the input are the same object. The join method selects the elements of the base shipRollup whose values match the values of the joined shipRollup that are specified by the comparison Source, custValuesToSelect. The method produces a Source, custSel, that has only the selected elements of shipRollup. Because the visible parameter is false, the joined Source is not an output of custSel. The custSel Source therefore has only two elements, the values of which are SHIPMENTS_ROLLUP::REGION::9 and SHIPMENTS_ROLLUP::REGION::10.

You produce a Source that has an output by specifying true as the visible parameter to the join method. Example 6-4 joins the Source objects for the dimension selections from Example 6-2 and Example 6-3 to produce a Source, custSelByProdSel, that has one output. The custSelByProdSel Source has the elements from custSel that are specified by the elements of prodSel.

The comparison Source is an empty Source, which has no elements and which is the result of the getEmptySource method of the DataProvider, dp. The comparison rule value, COMPARISON_RULE_REMOVE, selects only the elements of prodSel that are not in the comparison Source. Because the comparison Source has no elements, all of the elements of the joined Source are selected. Each of the elements of the joined Source specify all of the elements of the base Source. The resulting Source, custSelByProdSel, therefore has all of the elements of custSel.

Because the visible parameter is true in Example 6-4, prodSel is an output of custSelByProdSel. Therefore, for each element of the output, custSelByProdSel has the elements of custSel that are specified by that element of the output. Because the custSel and prodSel are both simple lists of dimension values, the result is the cross product of the elements of both Source objects.

Example 6-4 Using the join Method To Produce a Source With an Output

Source custSelByProdSel = custSel.join(prodSel,
                                        dp.getEmptySource(),
                                        Source.COMPARISON_RULE_REMOVE,
                                        true);

To actually retrieve the data specified by custSelByProdSel, you must create a Cursor for it. Such a Cursor contains the values shown in the following table, which has headings added that indicate that the values from the output, prodSel, are in the left column and the values from the elements of the custSelByProdSel Source, which are derived from its type, custSel, are in the right column.

Output Values                Type Values
-------------------------  ----------------------------
PRODUCT_ROLLUP::FAMILY::4  SHIPMENTS_ROLLUP::REGION::9
PRODUCT_ROLLUP::FAMILY::4  SHIPMENTS_ROLLUP::REGION::10
PRODUCT_ROLLUP::FAMILY::5  SHIPMENTS_ROLLUP::REGION::9
PRODUCT_ROLLUP::FAMILY::5  SHIPMENTS_ROLLUP::REGION::10
PRODUCT_ROLLUP::FAMILY::7  SHIPMENTS_ROLLUP::REGION::9
PRODUCT_ROLLUP::FAMILY::7  SHIPMENTS_ROLLUP::REGION::10
PRODUCT_ROLLUP::FAMILY::8  SHIPMENTS_ROLLUP::REGION::9
PRODUCT_ROLLUP::FAMILY::8  SHIPMENTS_ROLLUP::REGION::10

The custSelByProdSel Source has two type elements, and its output has four elements. The number of elements of custSelByProdSel is eight because for this Source, each output element specifies the same set of two type elements.

Each join operation that specifies a visible parameter of true adds an output to the list of outputs of the resulting Source. For example, if a Source has two outputs and you call one of its join methods that produces an output, then the Source that results from the join operation has three outputs. You can get the outputs of a Source by calling its getOutputs method, which returns a List of Source objects.

Example 6-5 demonstrates joining a measure to selections from the dimensions of the measure, thus matching to the inputs of the measure Source objects that provide the required elements. Because the last two join methods match the dimension selections to the inputs of the measure, the resulting Source does not have any inputs. Because the visible parameter in those joins is true, the last join method produces a Source that has two outputs.

Example 6-5 gets the Source for the measure of unit costs. That Source, unitCost, has two inputs, which are the primary Source objects for the Time and Product dimensions, which are the dimensions of unit cost. The example gets the Source objects for level hierarchies of the dimensions, which are subtypes of the Source objects for the dimensions. It produces selections of the level hierarchies and then joins those selections to the measure. The result, unitCostSel, specifies the unit costs of the selected products at the selected times.

Example 6-5 Using the join Method To Match Source Objects To Inputs

Source unitCost = mdmUnitCost.getSource();
Source calendar = mdmCalendar.getSource();
Source prodRollup = mdmProdRollup.getSource();
Source timeSel = calendar.join(calendar.value(),
                               dp.createListSource(new String[]
                                                    {"CALENDAR::MONTH::47",
                                                     "CALENDAR::MONTH::59"}),
                               Source.COMPARISON_RULE_SELECT,
                               false);
Source prodSel = prodRollup.join(prodRollup.value(),
                               dp.createListSource(new String[]
                                        {"PRODUCT_ROLLUP::ITEM::13",
                                         "PRODUCT_ROLLUP::ITEM::14",
                                         "PRODUCT_ROLLUP::ITEM::15"}),
                               Source.COMPARISON_RULE_SELECT,
                               false);
Source unitCostSel = unitCost.join(timeSel,
                                   dp.getEmptySource(),
                                   Source.COMPARISON_RULE_REMOVE,
                                   true);
                             .join(prodSel, 
                                   dp.getEmptySource(),
                                   Source.COMPARISON_RULE_REMOVE,
                                   true);

The unnamed Source that results from joining timeSel to unitCost has one output, which is timeSel. Joining prodSel to that unnamed Source produces unitCostSel, which has two outputs, timeSel and prodSel. The unitCostSel Source has the elements from its type, unitCost, that are specified by its outputs.

A Cursor for unitCostSel contains the following, displayed as a table with headings added that indicate the structure of the Cursor. A Cursor has the same structure as its Source. The unit cost values are formatted as dollar values.

Output 1                Output 2         Type
         Values                  Values         Values
------------------------  -------------------  --------
PRODUCT_ROLLUP::ITEM::13  CALENDAR::MONTH::47  2897.40
PRODUCT_ROLLUP::ITEM::13  CALENDAR::MONTH::59  2376.73
PRODUCT_ROLLUP::ITEM::14  CALENDAR::MONTH::47  3238.36
PRODUCT_ROLLUP::ITEM::14  CALENDAR::MONTH::59  3015.90
PRODUCT_ROLLUP::ITEM::15  CALENDAR::MONTH::47  2847.47
PRODUCT_ROLLUP::ITEM::15  CALENDAR::MONTH::59  2819.85

Output 1 has the values from prodSel, output 2 has the values from timeSel, and the type values are the values from unitCost that are specified by the output values.

Because these join operations are performed by most OLAP API applications, the API provides shortcuts for these and many other join operations. Example 6-6 uses shortcuts for the join operations in Example 6-5 to produce the same result.

Example 6-6 Using Shortcuts

Source unitCost = mdmUnitCost.getSource();
StringSource calendar = (StringSource) mdmCalendar.getSource();
StringSource prodRollup =(StringSource) mdmProdRollup.getSource();
Source timeSel = calendar.selectValues(new String[]
                                       {"CALENDAR::MONTH::47",
                                        "CALENDAR::MONTH::59"}),
Source prodSel = prodRollup.selectValues(new String[]
                                        {"PRODUCT_ROLLUP::ITEM::13",
                                         "PRODUCT_ROLLUP::ITEM::14",
                                         "PRODUCT_ROLLUP::ITEM::15"}),
Source unitCostSel = unitCost.join(timeSel).join(prodSel);

Matching a Source To an Input

In a join operation, a Source-to-input match occurs only between the base Source and the joined Source. A Source matches an input if one of the following conditions is true.

  1. The Source is the same object as the input or it is a subtype of the input.

  2. The Source has an output that is the same object as the input or the output is a subtype of the input.

  3. The output has an output that is the same object as the input or is a subtype of the input.

The join operation looks for the conditions in the order in the preceding list. It searches the list of outputs of the Source recursively, looking for a match to the input. The search ends with the first matching Source. An input can match with only one Source, and two inputs cannot match with the same Source.

When a Source matches an input, the result of the join method has the elements of the base that match the elements specified by the parameters of the method. You can determine if a Source matches another Source, or an output of the other Source, by passing the Source to the findMatchFor method of the other Source.

When a Source matches an input, the resulting Source does not have that input. Matching a Source to an input does not affect the outputs of the base Source or the joined Source. If a base Source has an output that matches the input of the joined Source, the resulting Source does not have the input but it does have the output.

If the base Source or the joined Source in a join operation has an input that is not matched in the operation, then the unmatched input is an input of the resulting Source.

The comparison Source of a join method does not participate in the input matching. If the comparison Source has an input, then that input is not matched and the Source returned by the join method has that same input.

Example 6-7 demonstrates a base Source matching the input of the joined Source in a join operation. The example uses the position method to produce a Source that has an input, and then uses the join method to match the base of the join operation to the input of the joined Source.

Example 6-7 Matching the Base Source to an Input of the Joined Source

Source myList = dp.createListSource(new String[]
                                     "PRODUCT_ROLLUP::FAMILY::4",
                                     "PRODUCT_ROLLUP::FAMILY::5",
                                     "PRODUCT_ROLLUP::FAMILY::7",
                                     "PRODUCT_ROLLUP::FAMILY::8"});
Source pos = dp.createListSource(new int[] {2, 4});
Source myListPos = myList.position();
Source myListSel = myList.join(myListPos, pos,
                               Source.COMPARISON_RULE_SELECT, false);

In Example 6-7, the position method returns myListPos, which has the elements of myList and which has myList as an input. The join method matches the base myList to the input of the joined Source, myListPos.

The comparison Source, pos, specifies the positions of the elements of myListPos to match to the positions of the elements of myList. The elements of the resulting Source, myListSel, are the elements of myList whose positions match those specified by the parameters of the join method.

A Cursor for myListSel has the following values.

PRODUCT_ROLLUP::FAMILY::5
PRODUCT_ROLLUP::FAMILY::8

If the visible parameter in Example 6-7 were true instead of false, then the result would have elements from myList and an output of myListPos. A Cursor for myListSel in that case would have the following values, displayed as a table with headings added that indicate the output and type values.

Output  Type
Values  Values
------  -------------------------
  2     PRODUCT_ROLLUP::FAMILY::5
  4     PRODUCT_ROLLUP::FAMILY::8

Example 6-8 demonstrates matching outputs of the joined Source to two inputs of the base Source. In the example, units is a Source for an MdmMeasure. It has as inputs the primary Source objects for the Time, Product, Customer, and Channel dimensions.

The DataProvider is dp, and prodRollup, shipRollup, calendar, and chanRollup are the Source objects for the default hierarchies of the Product, Customer, Time, and Channel dimensions, respectively. Those Source objects are subtypes of the Source objects for the dimensions that are the inputs of units.

The join method of prodRollup in the first line of Example 6-8 results in prodSel, which specifies selected product values. In that method, the joined Source is the result of the value method of prodRollup. The joined Source has the same elements as prodRollup, and it has prodRollup as an input. The comparison Source is the list Source that is the result of the createListSource method of the DataProvider.

The base Source of the join method, prodRollup, matches the input of the joined Source. Because prodRollup is the input of the joined Source, the Source returned by the join method has only the elements of the base, prodRollup, that match the elements of the joined Source that appear in the comparison Source. Because the visible parameter value is false, the resulting Source does not have the joined Source as an output. The next three similar join operations in Example 6-8 result in selections for the other three dimensions.

The join method of timeSel has custSel as the joined Source. Its comparison Source is the result of the getEmptySource method, so it has no elements. The comparison rule specifies that the elements of the joined Source that are present in the comparison Source do not appear in the resulting Source. Because the comparison Source has no elements, all of the elements of the joined Source are selected. The true value for the visible parameter causes the joined Source to be an output of the Source returned by the join method. The returned Source, custSelByTime, has the selected elements of the Customer dimension and has timeSel as an output.

The join method of prodSel has custSelByTime as the joined Source. It produces prodByCustByTime, which has the selected elements from the Product dimension and has custSelByTime as an output. Example 6-8 then joins the dimension selections to the units Source.

The dimension selections are subtypes of the Source objects that are the inputs of units, and therefore the selections match the inputs of units. The input for the Product dimension is matched by prodByCustByTime because prodByCustByTime is a subtype of prodSel, which is a subtype of prodRollup. The input for the Customer dimension is matched by the custSelByTime, which is the output of prodByCustByTime.

The custSelByTime Source is a subtype of custSel, which is a subtype of shipRollup. The input for the times dimension is matched by timeSel, which is the output of custSelByTime. The timeSel Source is a subtype of calendar.

Example 6-8 Matching an Input of the Base Source to an Output of the Joined Source

Source prodSel = prodRollup.join(prodRollup.value(),
                                 dp.createListSource(new String[]
                                     {"PRODUCT_ROLLUP::FAMILY::4",
                                      "PRODUCT_ROLLUP::FAMILY::5"}),
                                 Source.COMPARISON_RULE_SELECT, 
                                 false);
Source custSel = shipRollup.join(shipRollup.value(),
                                 dp.createListSource(new String[]
                                    {"SHIPMENTS_ROLLUP::REGION::9",
                                     "SHIPMENTS_ROLLUP::REGION::10"}),
                                 Source.COMPARISON_RULE_SELECT,
                                 false);
Source timeSel =  calendar.join( calendar.value(),
                                dp.createConstantSource(
                                               "CALENDAR::YEAR::4"),
                                Source.COMPARISON_RULE_SELECT, 
                                false);
Source chanSel = chanRollup.join(chanRollup.value(),
                                 dp.createConstantSource(
                                      "CHANNEL_ROLLUP::CHANNEL::4"),
                                 Source.COMPARISON_RULE_SELECT, 
                                 false);

Source custSelByTime = custSel.join(timeSel, 
                                    dp.getEmptySource(),
                                    Source.COMPARISON_RULE_REMOVE,
                                    true);
Source prodByCustByTime = prodSel.join(custSelByTime,
                                       dp.getEmptySource(),
                                       Source.COMPARISON_RULE_REMOVE,
                                       true);

Source selectedUnits = units.join(prodByCustByTime, 
                                  dp.getEmptySource(),
                                  Source.COMPARISON_RULE_REMOVE,
                                  true)
                            .join(promoSel, 
                                  dp.getEmptySource(),
                                  Source.COMPARISON_RULE_REMOVE,
                                  true ),
                            .join(chanSel, 
                                  dp.getEmptySource(),
                                  Source.COMPARISON_RULE_REMOVE,
                                  true);

A Cursor for selectedUnits contains the following values, displayed in a crosstab format with column headings and formatting added. The table has only the local values of the dimension elements. The first two lines are the page edge values of the crosstab, which are the values of the chanSel output of selectedUnits, and the value of timeSel, which is an output of the prodByCustByTime output of selectedUnits. The row edge values of the crosstab are the customer values in the left column, and the column edge values are the products values that head the middle and right columns.

The crosstab has only the local value portion of the unique values of the dimension elements. The measure values are the units sold values specified by the selected dimension values.

4
4
            Products
           ----------
Customers   4     5
---------  ---  ----
9          215   439
10         846  1748

The following table has the same results except that the dimension element values are replaced by the short descriptions of those values.

Internet
2001
                           Products
               -------------------------
Customers      Portable PCs  Desktop PCs
-------------  ------------  -----------
Europe         215            439
North America  846           1748

To demonstrate turning inputs into outputs, Example 6-9 uses units, which is the Source for the Units measure, and defaultHiers, which is an ArrayList of the Source objects for the default hierarchies of the dimensions of the measure. The example gets the inputs and outputs of the Source for the measure. It displays the Source identifications of the Source for the measure and for its inputs. The inputs of the Source for the measure are the Source objects for the MdmPrimaryDimension objects that are the dimensions of the measure.

Example 6-9 next displays the number of inputs and outputs of the Source for the measure. Using the join(Source joined) method, which produces a Source that has the elements of the base of the join operation as its elements and the joined parameter Source as an output, it joins one of the hierarchy Source objects to the Source for the measure, and displays the number of inputs and outputs of the resulting Source. It then joins each remaining hierarchy Source to the result of the previous join operation and displays the number of inputs and outputs of the resulting Source.

Finally the example gets the outputs of the Source produced by the last join operation, and displays the Source identifications of the outputs. The outputs of the last Source are the Source objects for the default hierarchies, which the example joined to the Source for the measure. Because the Source objects for the hierarchies are subtypes of the Source objects for the MdmPrimaryDimension objects that are the inputs of the measure, they match those inputs.

Example 6-9 Matching the Inputs of a Measure and Producing Outputs

Set inputs = units.getInputs();
Iterator inputsItr = inputs.iterator();
List outputs = units.getOutputs();
Source input = null;

int i = 1;
System.out.println("The inputs of " + units.getID() + " are:");
while(inputsItr.hasNext())
{
  input = (Source) inputsItr.next();
  System.out.println(i + ": " + input.getID());
  i++;
}

System.out.println(" ");
int setSize = inputs.size();
for(i = 0; i < (setSize + 1); i++) 
{
  System.out.println(units.getID() + " has " + inputs.size() + 
                   " inputs and " + outputs.size() + " outputs.");
  if (i < setSize) 
  {
    input = defaultHiers.get(i);
    System.out.println("Joining " + input.getID() + " to " 
                                  + units.getID());
    units = units.join(input);   
    inputs = units.getInputs();
    outputs = units.getOutputs();
  }
}

System.out.println(" ");
System.out.println("The outputs of " + units.getID() + " are:");
Iterator outputsItr = outputs.iterator();
i = 1;
while(outputsItr.hasNext())
{
  Source output = (Source) outputsItr.next();
  System.out.println(i + ": " + output.getID());
  i++;
}

The text displayed by the example is the following:

The inputs of Hidden..M_GLOBAL.UNITS_CUBE.UNITS are:
1: Hidden..D_GLOBAL.TIME
2: Hidden..D_GLOBAL.PRODUCT
3: Hidden..D_GLOBAL.CUSTOMER
4: Hidden..D_GLOBAL.CHANNEL

Hidden..M_GLOBAL.UNITS_CUBE.UNITS has 4 inputs and 0 outputs.
Joining Hidden..D_GLOBAL.PRODUCT.PRODUCT_ROLLUP to
        Hidden..M_GLOBAL.UNITS_CUBE.UNITS
Join.0 has 3 inputs and 1 outputs.
Joining Hidden..D_GLOBAL.CUSTOMER.SHIPMENTS_ROLLUP to Join.0
Join.1 has 2 inputs and 2 outputs.
Joining Hidden..D_GLOBAL.TIME.CALENDAR to Join.1
Join.2 has 1 inputs and 3 outputs.
Joining Hidden..D_GLOBAL.CHANNEL.CHANNEL_ROLLUP to Join.2
Join.3 has 0 inputs and 5 outputs.

The outputs of Join.3 are:
1: Hidden..D_GLOBAL.CHANNEL.CHANNEL_ROLLUP
2: Hidden..D_GLOBAL.TIME.CALENDAR
3: Hidden..D_GLOBAL.CUSTOMER.SHIPMENTS_ROLLUP
4: Hidden..D_GLOBAL.PRODUCT.PRODUCT_ROLLUP

Note that as each successive Source for a hierarchy is joined to the result of the previous join operation, it becomes the first output in the List of outputs of the resulting Source. Therefore, the first output of Join.3 is Hidden..D_GLOBAL.CHANNEL.CHANNEL_ROLLUP, and its last output is Hidden..D_GLOBAL.PRODUCT.PRODUCT_ROLLUP.

Describing Parameterized Source Objects

Parameterized Source objects provide a way of specifying a query and retrieving different result sets for the query by changing the set of elements specified by the parameterized Source. You create a parameterized Source with a createParameterizedSource method of the DataProvider that you are using. In creating the parameterized Source, you supply a Parameter object. The Parameter supplies the value that the parameterized Source specifies.

Parameter objects are similar to CursorInput objects in that you use them to specify an initial value for a Source that is part of a query. A typical use of both Parameter and CursorInput objects is to specify the page edges of a cube. Example 7-9 demonstrates using Parameter objects to specify page edges.

An advantage of Parameter objects over CursorInput objects is that with Parameter objects you can easily fetch from the server only the set of elements that you currently need. Example 7-16 demonstrates using Parameter objects to fetch different sets of elements.

When you create a Parameter object, you supply an initial value for the Parameter. You then create the parameterized Source using the Parameter. You include the parameterized Source in specifying a query. You create a Cursor for the query. You can change the value of the Parameter with its setValue method, which changes the set of elements that the query specifies. Using the same Cursor, you can then display the new set of values.

Example 6-10 demonstrates the use of a Parameter and a parameterized Source to specify an element in a measure dimension. It creates a list Source that has as its element values the Source objects for Unit Cost and Unit Price measures. The example creates a StringParameter object that has as its initial value the unique identifying String for the Source for the Unit Cost measure. That StringParameter is then used to create a parameterized Source.

The example extracts the values from the measures, and then selects the data values that are specified by joining the dimension selections to the measure specified by the parameterized Source. It creates a Cursor for the resulting query and displays the results. After resetting the Cursor position and changing the value of the measParam StringParameter, the example displays the values of the Cursor again.

The dp object is the DataProvider. The context object has a method that displays the values of the Cursor with only the local value of the dimension elements.

Example 6-10 Using a Parameterized Source With a Measure Dimension

Source measDim = dp.createListSource(new Source[] {unitCost, 
                                                   unitPrice});
 
// Get the unique identifiers of the Source objects for the measures.
String unitCostID = unitCost.getID();
String unitPriceID = unitPrice.getID();
 
// Create a StringParameter using one of the IDs as the initial value.
StringParameter measParam = new StringParameter(dp, unitCostID);
 
// Create a parameterized Source.
StringSource measParamSrc = dp.createParameterizedSource(measParam);

// Extract the values from the measure dimension elements, and join 
// them to the specified measure and the dimension selections.
Source result = measDim.extract().join(measDim, measParamSrc)
                                 .join(prodSelShortDescr)
                                 .join(timeSelShortDescr);
// Get the TransactionProvider and prepare and commit the 
// current transaction. These operations are not shown.
 
// Create a Cursor.
CursorManagerSpecification cMngrSpec = 
                        dp.createCursorManagerSpecification(results);
SpecifiedCursorManager  spCMngr = dp.createCursorManager(cMngrSpec);
Cursor resultsCursor = spCMngr.createCursor();
 
// Display the results.
context.displayCursor(resultsCursor, true);
 
//Reset the Cursor position to 1.
resultsCursor.setPosition(1);

// Change the value of the parameterized Source.
measParam.setValue(unitPriceID);

// Display the results again.
context.displayCursor(resultsCursor, true);

The following table displays the first set of values of resultsCursor, with column headings and formatting added. The left column of the table has the local value of the Time dimension hierarchy. The second column from the left has the short value description of the time value. The third column has the local value of the Product dimension hierarchy. The fourth column has the short value description of the product value. The fifth column has the Unit Cost measure value for the time and product.

Time Description Product Description      Unit Cost
---- ----------- ------- ---------------   ---------
 58    Apr-01      13    Envoy Standard    2360.78
 58    Apr-01      14    Envoy Executive   2952.85
 59    May-01      13    Envoy Standard    2376.73
 59    May-01      14    Envoy Executive   3015.90
 

The following table displays the second set of values of resultsCursor in the same format. This time the fifth column has values from the Unit Price measure.

Time Description Product Description      Unit Price
---- ----------- ------- ---------------  ----------
 58    Apr-01      13    Envoy Standard    2412.42
 58    Apr-01      14    Envoy Executive   3107.65
 59    May-01      13    Envoy Standard    2395.63
 59    May-01      14    Envoy Executive   3147.85

Model Objects and Source Objects

This topic describes the Model interface and its implementations, and the relationship of Model and Source objects. It also presents examples of creating custom Model objects and performing other tasks that involve Source and Model objects.

Describing the Model for a Source

Introduced to the OLAP API in Oracle Database 10.2, a Model is analogous to the Oracle SQL Model clause and an Oracle DML Model object. With a Model you can assign a value to the Source for a dimensioned object for one or more sets of members of the dimensions of the object. The value that the Model assigns can be anything from a simple constant to the result of a complex calculation involving several other Source objects with nested Model objects.

The value that a Model assigns for a set of dimension members is represented by an Assignment object. A Model can have one or more Assignment objects. Each dimension member in the set is represented by a Qualification object. An Assignment has one or more Qualification objects.

The value that the Assignment assigns is specified by a Source. An Assignment also has an integer that specifies a precedence that affects the order in which Oracle OLAP calculates a value and assigns it. If you create more that one Assignment for a Model without specifying a precedence, then the order in which Oracle OLAP calculates and assigns the values is not guaranteed.

A Model assigns values for existing dimension members. You can use a Model to assign a different value for a dimension member, or to assign a value for a set of members of more than one dimension, or to assign a different value for a specific measure for the set of dimension members, or to assign a value for the dimension member for an attribute.

When you create a custom dimension member, you specify an assignment value for it. Oracle OLAP automatically adds an Assignment object that specifies the value for the custom member to the appropriate Model for the dimension. Oracle OLAP assigns that value as the measure value for any measure dimensioned by the dimension.

Figure 6-1 illustrates the class hierarchy of the Model interface and the classes that implement it. The oracle.olapi.metadata.mdm.MdmModel class implements the Model interface for MdmObject objects. Another implementation of the Model interface is the CustomModel class in the oracle.olapi.data.source package.

Figure 6-1 Model Interface and its Implementations

Description of Figure 6-1 follows
Description of "Figure 6-1 Model Interface and its Implementations"

A Model has one or more inputs, which are the Source objects for which the model assigns values. The inputs are equivalent to the list of dimensions of an OLAP DML or SQL Model. For example, the MdmDimensionCalculationModel returned by the getNumberCalcModel method of an MdmStandardDimension has as its input the Source for that same MdmStandardDimension. The MdmDimensionedObjectModel returned by the getModel method of an MdmAttribute has as its input the Source for the MdmPrimaryDimension that dimensions the attribute. The MdmDimensionedObjectModel returned by getModel method of an MdmMeasure has as its inputs the Source objects for the MdmPrimaryDimension objects that dimension the measure.

A Model can have one or more parents, which are other Model objects from which the Model inherits Assignment objects. An MdmMeasureModel has as its parents the MdmDimensionCalculationModel objects of its dimensions. MdmAttributeModel and MdmDimensionCalculationModel objects do not have parent Model objects.

A CustomModel can have inputs and it can have parent Model objects. When you create a CustomModel object, you can specify inputs and parent Model objects for it. A CustomModel can have also have outputs, which MdmModel objects do not have.

You can create a series of CustomModel objects and have them inherit Assignment objects from each other. The following restrictions apply to the inheritance of an Assignment by one CustomModel from another:

  • The inheritance cannot be circular. For example, if customModelB inherits from customModelA, then customModelA cannot inherit from customModelB.

  • The type and the outputs of the CustomModel objects must be the same.

  • If a parent CustomModel has an input, then the child CustomModel must also specify that input. The child CustomModel can have additional inputs, but it must specify the inputs of the parent CustomModel objects.

After creating a CustomModel and adding any assignments to it, you can create a Source for it by calling the createSolvedSource method of the CustomModel. With the defaultValues parameter of the createSolvedSource method, you can specify a Source that supplies default values for the Source returned by the method. If you do not specify a Source for the default values, then the default values of the resulting Source are null.

Creating a CustomModel - Example

The Source.extract method is now implemented as a CustomModel. An advantage of using your own CustomModel over the extract method is that you can assign the measure value to a String other than a Source ID. Example 6-11 demonstrates using the extract method and then using a CustomModel to achieve the same result. It also demonstrates using another CustomModel to achieve a result that assigns the measure values to a different set of String values.

In the example, unitPrice and unitCost are NumberSource objects for the Unit Price and Unit Cost measures, and dp is the DataProvider. The prodSel object is a Source that represents the selection of three members of the Product dimension.

Example 6-11 Implementing the extract method as a CustomModel

// Create a Source that represents a calculation involving two measures.
Source calculation = unitPrice.minus(unitCost);

// Create a list Source that has Source objects as its element values.
Source sourceListSrc = dp.createListSource(new Source[] 
                                           {unitPrice, unitCost, calculation});
// Use the extract method to get the values of the Source components of the
// list and join Source objects that match the inputs.
Source resultUsingExtract = sourceListSrc.extract()
                                         .join(sourceListSrc)
                                         .join(prodSel)
                                         .join(calendar, "CALENDAR::YEAR::3");

// Produce the same result using a CustomModel directly.
CustomModel  customModel = dp.createModel(sourceListSrc);
customModel.assign(unitPrice.getID(), unitPrice);
customModel.assign(unitCost.getID(), unitCost);
customModel.assign(calculation.getID(), calculation);
Source measValForSrc = customModel.createSolvedSource();
Source resultUsingCustomModel = 
                          measValForSrc.join(sourceListSrc)
                                       .join(prodSel)
                                       .join(calendar, "CALENDAR::MONTH::47");

// Create a list Source that has String objects as its element values.
Source stringListSrc = dp.createListSource(new String[]
                                           {"price", "cost", "markup"});
// Create a CustomModel for the list Source.
CustomModel  customModel2 = dp.createModel(stringListSrc);
customModel2.assign("price", unitPrice);
customModel2.assign("cost", unitCost);
customModel2.assign("markup", calculation);
Source measValForSrc2 = customModel2.createSolvedSource();
 
Source resultUsingCustomModel2 = 
                        measValForSrc2.join(stringListSrc)
                                      .join(prodSel)
                                      .join(calendar, "CALENDAR::MONTH::47");

Cursor objects for resultUsingExtract and resultUsingCustomModel have the same values, which are the following, shown with formatting added:

PRODUCT_ROLLUP::ITEM::13  Hidden..M_GLOBAL.PRICE_CUBE.UNIT_PRICE  3118.61
PRODUCT_ROLLUP::ITEM::13  Hidden..M_GLOBAL.PRICE_CUBE.UNIT_COST   2897.40
PRODUCT_ROLLUP::ITEM::13  Join.2                                   221.21
PRODUCT_ROLLUP::ITEM::14  Hidden..M_GLOBAL.PRICE_CUBE.UNIT_PRICE  3442.86
PRODUCT_ROLLUP::ITEM::14  Hidden..M_GLOBAL.PRICE_CUBE.UNIT_COST   3238.36
PRODUCT_ROLLUP::ITEM::14  Join.2                                   204.50
PRODUCT_ROLLUP::ITEM::15  Hidden..M_GLOBAL.PRICE_CUBE.UNIT_PRICE  2962.14
PRODUCT_ROLLUP::ITEM::15  Hidden..M_GLOBAL.PRICE_CUBE.UNIT_COST   2847.47
PRODUCT_ROLLUP::ITEM::15  Join.2                                   114.67

A Cursor for resultUsingCustomModel2 has the following values, shown with formatting added:

PRODUCT_ROLLUP::ITEM::13  price  3118.61
PRODUCT_ROLLUP::ITEM::13  cost   2897.40
PRODUCT_ROLLUP::ITEM::13  markup  221.21
PRODUCT_ROLLUP::ITEM::14  price  3442.86
PRODUCT_ROLLUP::ITEM::14  cost   3238.36
PRODUCT_ROLLUP::ITEM::14  markup  204.50
PRODUCT_ROLLUP::ITEM::15  price  2962.14
PRODUCT_ROLLUP::ITEM::15  cost   2847.47
PRODUCT_ROLLUP::ITEM::15  markup  114.67

Dependent Assignment Values - Example

The value that is specified by the assigned Source of an Assignment object can be the result of a calculation that involves another Assignment object. Each custom member adds an Assignment to the appropriate MdmDimensionCalculationModel object of the dimension.

Example 6-12 creates the same custom member of the Product dimension that Example 2-1 creates. It then creates a second custom member for the dimension. In Example 6-12, the value assigned by the second custom member depends on the value assigned by the first custom member.

As in Example 2-1, Example 6-12 uses the DataProvider object, dp, to get the placeholder Source, ph, for the Number data type from the DataProvider. the example uses the placeholder in defining the objects, calc and dependentCalc, that define the values that Oracle OLAP assigns for the custom members.

The calc object defines the value assigned for the first custom member as the value specified by product item 14 plus the value specified by item 15. The dependentCalc object defines the value assigned for the second custom member as the value specified by the first custom member, product item 60, plus the value specified by item 13.

The prodSel object specifies the dimension members for items 13, 14, and 15 and the custom members, items 60 and 61. The unitCost and unitPrice objects are Source objects for the Unit Cost and Unit Price measures, and the calendar object is the Source for the Calendar hierarchy of the Time dimension.

The result object is the query produced by joining the Source objects for the Unit Cost and Unit Price measures to the Source objects for the selected members of the dimensions of the measures. The join method used to join the Time dimension value, CALENDAR::MONTH::47, to the result of the previous join operations causes the Time value to not appear in the result object.

Example 6-12 Creating an Assignment That Depends on Another Assignment

Source ph = dp.getFundamentalMetadataProvider()
              .getNumberPlaceholder()
              .getSource();

Source calc = ((NumberSource)
                  (ph.join(prodRollup, "PRODUCT_ROLLUP::ITEM::14")))
                  .plus(
                  (NumberSource)
                  (ph.join(prodRollup, "PRODUCT_ROLLUP::ITEM::15")));

MdmStandardMember mdmItem60 = mdmProdStdDim.createCustomMember("60",
                                                               mdmItemLevel,
                                                               "4",
                                                               calc,
                                                               10);

Source dependentCalc = ((NumberSource)
                       (ph.join(prodRollup, "PRODUCT_ROLLUP::ITEM::60")))
                       .plus(
                       (NumberSource)
                       (ph.join(prodRollup, "PRODUCT_ROLLUP::ITEM::13")));

MdmStandardMember mdmItem61 = mdmProdStdDim.createCustomMember("61",
                                                               mdmItemLevel,
                                                               "4",
                                                               dependentCalc,
                                                               10);

StringSource prodSel = prodRollup.selectValues(
                                new String[]{"PRODUCT_ROLLUP::ITEM::13",
                                             "PRODUCT_ROLLUP::ITEM::14",
                                             "PRODUCT_ROLLUP::ITEM::15",
                                             "PRODUCT_ROLLUP::ITEM::60"
                                             "PRODUCT_ROLLUP::ITEM::61"});

Source result = unitPrice.join(unitCost)
                         .join(prodSel)
                         .join(calendar, "CALENDAR::MONTH::47");

A Cursor for result has the following values, with column headings and formatting added:

Product Item         Cost     Price
------------------------  -------  -------
PRODUCT_ROLLUP::ITEM::13  2897.40  3118.61
PRODUCT_ROLLUP::ITEM::14  3238.36  3442.86
PRODUCT_ROLLUP::ITEM::15  2847.47  2962.14
PRODUCT_ROLLUP::ITEM::60  6085.83  6405.00
PRODUCT_ROLLUP::ITEM::61  8983.23  9523.61

A Custom Member That Specifies an Aggregated Value - Example

Example 6-13 creates a custom member of the Product dimension that has an assigned value that is the result of an aggregation operation. The example uses the Source for a custom MdmAttribute that relates colors to dimension members in the Item level of dimension hierarchy.

Like Example 6-12, this example uses a placeholder Source, ph, in creating the calc object, which defines the value that Oracle OLAP assigns for the custom member. The calc object represents the total of the values specified by a set of dimension members.

The example creates the custom member and then specifies a short value description for it. Next, the example appends the custom member to the selection of green products. Finally, it produces the result query by joining the Source for the Units measure to the Source objects for the short value description of the Product dimension and the selected members of the dimensions of the measure. The particular join method that is used to join the Customer dimension value, SHIPMENTS_ROLLUP::SHIP_TO::106, the Channel dimension value, CHANNEL_ROLLUP::ALL_CHANNELS::1, and the Time dimension value, CALENDAR::YEAR::3, to the result of the previous join operations causes the Customer, Channel, and Time values to not appear in the result object.

Example 6-13 Creating a Custom Member That Assigns an Aggregated Value

// Create a Source that has the green products.
Source greenProducts = itemLevel.join(prodColorAttr, "Green");

Source calc = ((NumberSource)
               (ph.join(prodRollup,
                        new String[] {"PRODUCT_ROLLUP::ITEM::25",
                                      "PRODUCT_ROLLUP::ITEM::26",
                                      "PRODUCT_ROLLUP::ITEM::30",
                                      "PRODUCT_ROLLUP::ITEM::31"}))).total();

MdmStandardMember mdmGreenProdTotal = 
          mdmProdStdDim.createCustomMember("65",  // member local value
                                           mdmItemLevel, // member level
                                           "4",   // parent local value
                                           calc, // calculation Source
                                           10);   // precedence value

mdmGreenProdTotal.setShortDescription("Green Products Total");

Source greenProdWithTotal =
  greenProducts.appendValue(prodRollup.selectValue("PRODUCT_ROLLUP::ITEM::65"));

Source result = units.join(prodShortDescr.join(greenProdWithTotal))
                     .join(shipRollup, "SHIPMENTS_ROLLUP::SHIP_TO::106")
                     .join(chanRollup, "CHANNEL_ROLLUP::ALL_CHANNELS::1")
                     .join(calendar, "CALENDAR::YEAR::3");

The following is a crosstab display of the values of a Cursor for result. The display includes only the local value of the Product dimension members and has column headings and formatting added.

Product          Description           Units Sold
-------  ----------------------------  ----------
   25    SIMM- 8MB PCMCIAII card           64
   26    SIMM- 16MB PCMCIAII card          21
   30    Mouse Pad                        277
   31    1.44MB External 3.5 Diskette      52
   65    Green Products Total             414