***  This class is subject to change.  ***

com.sas.iquery.metadata.expr
Class ResourceAwareStringExpression

com.sas.iquery.metadata.expr.ResourceAwareStringExpression
All Implemented Interfaces:
Base, BusinessModelResourceReferencer, ModelItem, UsabilityInterface, DataItemContainerExpression, ExpressionInterface, PropertyChangeListenee, java.beans.PropertyChangeListener, java.lang.Cloneable, java.util.EventListener

public class ResourceAwareStringExpression
implements DataItemContainerExpression

Specifies a "string form" of an expression that provides support for identifying certain resources within the string syntax. By that we mean that this class can identify/resolve any BusinessModelResource(s) contained in the expression.

Resource Identification Syntax and Usage

Introduction - The Problem

Products like SAS Information Map Studio or SAS Web Report Studio must provide users with the ability to use data items, columns, OLAP items or other business resources in expressions commonly known as calculated items which may be used in a query. References to these resources may either be typed in or selected using a UI.

Resources can be a data item, prompt, column or an Olap item (for example, measure, hierarchy)

We need a way to identify resources so that they can be handled when they appear in an expression.

Solution

Since resources can appear anywhere in an expression we need a special syntax to identify them. Within an expression, Resources are identified by their IDs and can be specified in one of two ways

<<id1.id2.id3 >> (Each identifier is separated by a "." No spaces are allowed between the identifier name and ".")

<<[id1].[id2].[id3]>> (Each identifier is enclosed within "[]" and separated by a "." No spaces are allowed between "[]" and "." )

Identifiers within a Resource can only contain alphanumeric characters, spaces and '_' as part of it's ID. The character "." is not allowed as part of identifier name. Leading and trailing spaces in an Identifier name will be trimmed and names cannot be empty. In other words, <<>> is invalid.

In order to uniquely qualify a resource, it may to be optionally qualified by its enclosing business model such as an information map, or its data selection. Examples are shown below.

Within our expression, our syntax supports single or double quoted strings which are maintained as typed and have no limitations on what they can contain. All single or double quoted strings should be closed properly with a matching quote like in 'Fred' or "Sales"

Our syntax also supports the notion of escaped syntax enclosed within <!-- and --> . One possible use of this is for executing queries on DBMS which support << and or >> in a manner which conflicts with our resource syntax. Within both quoted strings and escaped text Resource Identification is disabled.

Examples:

Use the StringExpressionUtil class (which provides factory methods) to create ResourceAwareStringExpressions. The examples below ilustrate this approach. See the javadoc for StringExpressionUtil, ResourceScope, ExpressionTypes, BusinessModel, ExpressionInterface for more information on the SAS Query Service APIs.

Example 1

String exprString = "<<DataItemID>> + 1";

BusinessModel someBusinessModel; // A valid InformationMap for example

int exprType = ExpressionTypes.EXP_TYPE_NUMERIC; // possible user selection

// scope if business, physical, or both --- this will be used as the search context

// we choose Business here

ResourceScope scope = ResourceScope.BUSINESS_SCOPE;

// Get an Expresion from a string

// this will be an instance of a ResourceAwareStringExpression

ExpressionInterface expr = StringExpressionUtil.getInstance().newExpression(somebusinessModel, exprString, exprType, scope );

// The fully qualified (human readable) expression text for display to an end user.

String exprText = StringExpressionUtil.getInstance().getText(expr, ResourceScope.BUSINESS_SCOPE, _businessModel);

 

Example 2

// Given some BusinessResource (anything which implements ExpressionInterface) get its string text which can then be used in an expression

Measure olapMeasure;

ResourceScope scope;

BusinessModel businessModel;

String text = StringExpressionUtil.getInstance().getText(olapMeasure,scope, businessModel );

Example 3

// Use of escapes and Quotes and Escapes

String exprString1 = "<<Table.Column>> = 'Sales' <!-- something my DBMS supports and I do not want parsed -->";

// Usage is similar to example 1. Text inside of the quotes and escape is maintained as passed in

// and not used for Resource Identification

// Note: when invoking getText() for expressions which may contain physical and business objects

//specify a scope of ResourceScope.BUSINESS_AND_PHYSICAL_SCOPE

Precedence Rules

Since Resources can be both business and physical items we use a scoping mechanism to resolve conflicts. By default the scope is business items only.

If ResourceScope is business or unspecified then only business items like DataItems, Prompts will be searched during resource identification. In other words the default scope is business items only.

If ResourceScope is physical then only physical items like columns and OLAP items will be searched during resource identification.

If ResourceScope is both physical and business items then business items will be first searched. If a match is not found then physical items will be searched. SAS Information Map Studio will use this mixed usage of business and physical.

For Physical Resources a match with Relational items will be attempted initially. If a match is not found then an OLAP match will be attempted.

Within Relational when an expression can be both a Column and Table Column has higher precedence

Within OLAP when an expression can be both a Hierarchy and a Measure, Measure has a higher precedence during the search.

To illustrate the above precedence Rules consider the following expression with ResourceScope both Physical and Business

<<Identifier1.Identifier2>>

1. Resolve the expression as a business item

2. If (1) fails resolve the expression as a relational column (table.column)

3. If (2) fails resolve the expression as a relational table (mapID.table)

4. if (3) fails resolve the expression as a OLAP measure

5. if (4) fails resolve the expression as a OLAP hierarchy

6. if (5) fails then the expression was not successfully resolved.

In the above example

If ResourceScope was only Business then only step (1) and (6) apply.

If ResourceScope was only Physical then only steps (2) thru (6) apply.

Note: Currently our business models do not allow mixing of Relational and Olap Items. So there will never be a case where there is a conflict between these types. The implementation of ResourceAwareStringExpression already supports this mixing if required and supported by business models  in the future.

Conflict Resolution between Business and Physical Items

A ResourceAwareStringExpression can potentially contain business items and physical items with the same IDs. The precedence rules in the previous section provide one way of resolving name collisions between the Resources in an expression. But sometimes it may be necessary to override the default precedence rules. For example since business items are always searched ahead of physical items (if scope has business) we can use the keyword physical:: for the Resource in question to specify a Relational Table.Column if it has the same name as a Map.DataItem. (see examples below).

Scope and Conflict Resolution Examples

Given the following business model, dataItem, table column IDs

BusinessModelID = DST_1, DataItemID = Sales_ID, TableID = DST_1, Column1 = Sales_ID, Column2 = Job_Code

 

Invoking StringExpressionUtil.getInstance().getText(ExpressionInterface) with no scope and no businessmodel specified

Physical items are always qualified since we cannot determine if there is a conflict with business item names

DataItem Text: <<DST_1.Sales_ID>>

Column(with Same ID as DataItem) Text: physical::<<DST_1.Sales_ID>>

Column(ID different than DataItem ID) Text: physical::<<DST_1.Job_Code>>

 

Invoking StringExpressionUtil.getInstance().getText(ExpressionInterface,ResourceScope.BUSINESS_SCOPE, BusinessModel )

DataItem Text: <<DST_1.Sales_ID>>

For Column and OlapItems this will result in an exception

com.sas.iquery.metadata.MetadataException: A Physical Object expression is invalid in BusinessScope

 

Invoking StringExpressionUtil.getInstance().getText(ExpressionInterface,ResourceScope.PHYSICAL_SCOPE, BusinessModel)

For BusinessItems (DataItems, Prompt etc) this will result in an exception

com.sas.iquery.metadata.MetadataException: A Business Object expression is invalid in PhysicalScope

Column(with Same ID as DataItem) Text: <<DST_1.Sales_ID>>

Column(ID different than DataItem ID) Text: <<DST_1.Job_Code>>

 

Invoking StringExpressionUtil.getInstance().getText(ExpressionInterface,ResourceScope.BUSINESS_AND_PHYSICAL_SCOPE, BusinessModel)

DataItem Text: <<DST_1.Sales_ID>>

Column(with Same ID as DataItem) Text: physical::<<DST_1.Sales_ID>>

Column(ID different than DataItem ID) Text: <<DST_1.Job_Code>>

 

Note: OLAP Items behavior is the same as Relational Column

Expression Identifier Examples

Note: These examples use the syntax in the form <<id1.id2>>. The syntax of the form <<[id1].[id2]>> is also valid and fully supported.  For physical items the physical:: keyword may be optionally used.

Business Items in Expressions

1. Data item expression

<<DataItem1ID>>

2. Data item qualified by its enclosing information map (Note: Providing the information map is optional)

<<Info_MapID.DataItemID>>

3. Calculated data items with functions/operators applied

<<DataItemID>> + 1

AVG( <<DataItemID>> )

4. With Escaped text

ABS( <<DataItemID>> ) <!-- <something my DBMS allows> -->

Note: The escaped text within <!-- --> is not scanned for resources and is passed directly to the DBMS with the escapes removed

5. Prompts

Resource identification is done for prompts but prompts use a special syntax to identify them

Prompt::<<promptID>>

OLAP Expressions

1. Dimension Expression

<<Dimension>>

2. Measure Expression

<<Dimension.Measure>>

3. Hierarchy Expression

<<Dimension.Hierarchy>>

4. Level Expression

<<Dimension.Hierarchy.Level>>

5. Member Expression

Since members are not resources currently, no resource identification will be done on members. Members can be specified as some text. An example would be:

[D1].[H1].[L1].[M1]

There are no limitations on what a Member name.

Relational Expressions

1. DataSource Table and Column

<<TableID.ColumnID>>

2. DataSource Table and Column qualified with its information map (a business model)

<<Info_MapID.TableID.ColumnID>>

3. DataSource Table

<<TableID>>

4. DataSource Table qualified with its information map (a business model)

<<InfoMapID.TableID>>

Known Problems and Limitations

Currently the following types of Resources are supported in an Expression

com.sas.iquery.metadata.business.DataItem

com.sas.iquery.metadata.business.Prompt

com.sas.iquery.metadata.business.DataSourceTable

com.sas.iquery.metadata.physical.Column

com.sas.iquery.metadata.physical.Dimension

com.sas.iquery.metadata.physical.Level

com.sas.iquery.metadata.physical.Measure

com.sas.iquery.metadata.physical.Hierarchy

Currently there is no way to override ResourceIdentification in certain cases. For example if a Resource was identified as a Measure and a Hierarchy was preferred there is no override mechanism.

In its present form ResourceAwareStringExpressions only handle the portion of an expression enclosed in the specified syntax. The rest of the expression is pretty much "passthorugh" except for simple checks like quote endings. It is possible that application "security" can be circumvented by typing in an expression for example containing a Column as a string in a select clause which the user does not have access to. This represents a potential security hole which these expressions cannot catch currently. To do so would require sophisticated parsing techniques which may be addressed in a later release. 

 

 


Field Summary
 
Fields inherited from interface com.sas.iquery.metadata.business.ModelItem
EVENT_OBJECT_PROPERTY_CHANGED
 
Fields inherited from interface com.sas.iquery.metadata.business.Base
EVENT_OBJECT_DEFINITION_CHANGED
 
Method Summary
 boolean containsAggregateFunction(ServerProperties serverProperties)
          Checks to see if the text contains one of the server's aggregate functions
 boolean containsUserTypedAggregation()
          Return true if this expression or any of its dataitems have expressions that have user typed aggregations
 java.util.List getLeafDataItems()
          Return a list of leaf data items (items whose aggType is not "defined in expression")
 ResourceScope getResourceScope()
           
 java.lang.String getText()
          Get the Expression String This is the human readable String This is the same as calling getText(ResourceScope.BUSINESS_AND_PHYSICAL_SCOPE, null)
 java.lang.String getText(java.util.Map resourceMap)
          Get the Expression String
 java.lang.String getText(ResourceScope scope, BusinessModel businessModel)
          Get the Expression String This is the human readable String
 boolean isCalculatedItem()
          Checks whether a this expression is a calculated item
 void setText(java.lang.String text)
          This is not supported currently and will result in a UnsupportedOperationException if invoked
 
Methods inherited from class com.sas.iquery.metadata.expr.StringExpression
getExpressionType, setExpressionType
 
Methods inherited from interface com.sas.iquery.metadata.expr.ExpressionInterface
getExpressionType
 
Methods inherited from interface com.sas.iquery.metadata.business.BusinessModelResourceReferencer
getResources
 
Methods inherited from interface com.sas.iquery.metadata.PropertyChangeListenee
addListener, getListeners, removeListener
 
Methods inherited from interface com.sas.iquery.metadata.business.UsabilityInterface
getReasonsUnusable, isUsableInQuery
 

Method Detail

***  This method is subject to change.  ***

getText

public java.lang.String getText()
                         throws MetadataException
Get the Expression String This is the human readable String This is the same as calling getText(ResourceScope.BUSINESS_AND_PHYSICAL_SCOPE, null)

Overrides:
getText in class StringExpression
Returns:
a String representation for this Expression
Throws:
MetadataException
See Also:
getText(ResourceScope, BusinessModel)

***  This method is subject to change.  ***

getText

public java.lang.String getText(ResourceScope scope,
                                BusinessModel businessModel)
                         throws MetadataException
Get the Expression String This is the human readable String

Parameters:
scope - the ResourceScope if the scope is both business and physical physical resources will be qualified fully if there are potential name conflicts with business items for example, physical::<>
businessModel - the BusinessModel (may be null) if BusinessModel is null and scope is both physical and business physical items will always be qualified with physical::
Returns:
a String representation for this Expression
Throws:
MetadataException
See Also:
getResourceScope()

***  This method is subject to change.  ***

getText

public java.lang.String getText(java.util.Map resourceMap)
                         throws MetadataException
Get the Expression String

Parameters:
resourceMap - Map containing resource mappings to be applied on this expression key is any BusinessModelResource object, value is its String value
Returns:
a String representation for this Expression
Throws:
MetadataException

***  This method is subject to change.  ***

setText

public void setText(java.lang.String text)
This is not supported currently and will result in a UnsupportedOperationException if invoked

Overrides:
setText in class StringExpression
Parameters:
text - the text string to set on this expression
Throws:
java.lang.UnsupportedOperationException

***  This method is subject to change.  ***

getResourceScope

public ResourceScope getResourceScope()
Returns:
ResourceScope this expression was created with

***  This method is subject to change.  ***

containsAggregateFunction

public boolean containsAggregateFunction(ServerProperties serverProperties)
                                  throws MetadataException
Checks to see if the text contains one of the server's aggregate functions

Overrides:
containsAggregateFunction in class StringExpression
Parameters:
serverProperties - - ServerProperties containing a server's function lists
Returns:
boolean - true if one of the server's aggregate functions was found in the text
Throws:
MetadataException - if there was a problem formatting the text to be searched

***  This method is subject to change.  ***

isCalculatedItem

public boolean isCalculatedItem()
Checks whether a this expression is a calculated item

Overrides:
isCalculatedItem in class StringExpression
Returns:
true if it is a calculated item, false otherwise

***  This method is subject to change.  ***

containsUserTypedAggregation

public boolean containsUserTypedAggregation()
                                     throws MetadataException
Return true if this expression or any of its dataitems have expressions that have user typed aggregations

Specified by:
containsUserTypedAggregation in interface DataItemContainerExpression
Returns:
true, if there is at least one user typed aggregation, false otherwise
Throws:
MetadataException - on any failures while performing this operation

***  This method is subject to change.  ***

getLeafDataItems

public java.util.List getLeafDataItems()
Return a list of leaf data items (items whose aggType is not "defined in expression")

Specified by:
getLeafDataItems in interface DataItemContainerExpression
Returns:
a List of DataItem objects

***  This class is subject to change.  ***




Copyright © 2009 SAS Institute Inc. All Rights Reserved.