Expressions

The value in a CSS declaration is usually a literal. However, it is possible to write an expression in place of a literal.
If the value begins with @| , then the remainder of the value is processed as an expression.
The syntax of the expressions, after the @| prefix, is close to the Java syntax. The expression type can be arithmetic (type int , long , float , or double ), Boolean , or String . Examples:
@|3+2*5                -> 13
@|true&&(true||!true)  -> true
@|start+end            -> "startend"
You can use the following regular arithmetic operators, listed here in decreasing precedence:
  • Unary operators + , , !
  • Exponentiation operator ^
  • Multiplicative operators * , / , %
  • Additive operators + ,
  • Relational operators == , != , < , > , <= , >=
  • Conditional operators && , ||
  • Conditional operator ?:
    Note
    The conditional operator is left-associative, unlike in Java where it is right-associative. In CSS, the expression a ? b : c ? d : e is equivalent to (a ? b : c) ? d : e , whereas in Java it is equivalent to a ? b : (c ? d : e) .
    In conditional expressions such as A ? B : C , put spaces around the colon because a colon can also be part of a subexpression, for example, when B is @abc .
An expression can refer to model attributes. The syntax is the usual one:
@|@speed/100+@drift -> 1/100 of the value of speed plus the value of drift . speed and drift are attributes of the current object.
'@|"name is: " + @name' -> "name is: Bob", if the value of the current object attribute name is Bob . Note the use of quotation marks to keep the space characters. You could use the backslash (\) character instead, directly preceding the space characters to retain them. The backslash works to quote any character that directly follows it. The use of the backslash character makes sure that the character thus quoted is not interpreted by the expression parser.
For example, when you edit a style sheet directly and you use double quotation marks for the entire declaration value specification, you must use escaped double quotation marks for inner strings of CSS expressions.
node {
   name: "@|concat(\"Name is \", @name)" ;
}
Alternatively, you can use quotation marks:
node {
   name: '@|concat("Name is ", @name)' ;
}
The standard functions abs() , acos() , asin() , atan() , ceil() , cos() , exp() , floor() , log() , pi , rint() , round() , sin() , sqrt() , and tan() are accepted, as in, for example:
@|3+sin(pi/2) -> 4
The following functions are also available:
Function Description
concat(arg1,...,argn) Returns the concatenation of the arguments arg1,...,argn as strings.
emptyString() Returns an empty string.
messageFormat(format,arg0,arg1,... Returns a formatted string. The formatting is done through the object MessageFormat corresponding to the first argument. See class java.text.MessageFormat for details.
decimalFormat(format, arg) Returns a formatted string. The formatting is done through the object DecimalFormat corresponding to the first argument. See class java.text.DecimalFormat for details.
simpleDateFormat(format, arg[, locale]) Returns a formatted string. The formatting is done through the object SimpleDateFormat corresponding to the first argument. See class java.text.SimpleDateFormat for details.
customFormat(formatBean,arg0,arg1,...) Returns a formatted string. formatBean must be a Java object of type MessageFormat, NumberFormat, or DateFormat, usually defined using the @#id syntax.
locale() Returns the locale of the object being styled or of the current context.
localized(resourceBundleName, resourceName[, locale]) Returns the localized value of the designated resource, fetched from a ResourceBundle, as a string.
id() Returns the ID of the object being styled.
type() Returns the CSS type of the object being styled.
cast(expr,type) Casts a value to a type. The second argument can be a fully qualified class name (entered as a string) or a class object.
double(expr) Evaluates the argument as an expression. The result is a number of type Double.
float(expr) Evaluates the argument as an expression. The result is a number of type Float.
int(expr) Evaluates the argument as an expression. The result is a number of type Integer.
long(expr) Evaluates the argument as an expression. The result is a number of type Long.
min(arg0,arg1, ...) Returns the smallest number among the arguments. All arguments must be numbers. Unless otherwise specified, the arguments are interpreted as numbers of type Double.
max(arg0,arg1, ...) Returns the largest number among the arguments. All arguments must be numbers. Unless otherwise specified, the arguments are interpreted as numbers of type Double.
invoke(obj,methodname,signature,arg...) Invokes a method on the object by introspection. The signature string specifies the argument types of the method as a comma separated list of (fully qualified) type names. The object and the arguments must be cast accordingly. For example, the specification @|invoke(cast(@val,java.lang.String),substring,"int,int",int(3),int(6)) corresponds to the call ((String)@val).substring(3,6).
invokeStatic(classname,methodname,signature,arg...) Invokes a static method on the specified class by introspection. The signature string specifies the argument types of the method as a comma separated list of (fully qualified) type names. The arguments must be cast accordingly. For example, the specification @|invokeStatic("java.lang.Math","atan2","double,double",double(0.6),double(0.4)) corresponds to the call java.lang.Math.atan2(0.6,0.4).
new(classname,signature,arg...) Invokes a constructor on the specified class by introspection. The signature string specifies the argument types of the method as a comma separated list of (fully qualified) type names. The arguments must be cast accordingly. For example, the specification @|new("java.util.Date","long",long(1293840000000)) corresponds to the call java.util.Date(1293840000000L).
random() Returns a random number between 0 (inclusive) and 1 (exclusive).
random(n) Returns a random number between 0 (inclusive) and n (exclusive).
point(x, y) Returns a point of type IlvPoint.
rect(x, y, w, h) Returns a rectangle of type IlvRect.
blinkingColor(onColor, offColor) Returns a blinking color with the timing specified in the IlvBlinkingRenderer.
blinkingColor(onColor, offColor, onTime, offTime) Returns a blinking color with the timing onTime and offTime specified in milliseconds.
brighterColor(color) Returns a brighter color. Corresponds to Color.brighter.
darkerColor(color) Returns a darker color. Corresponds to Color.darker.
styleSheetURL() Returns the URL of the current style sheet, if any.
styleSheetURL(path) Returns a URL specified by a path relative to the location of the style sheet.
getGraphicFromId(id) Returns the graphic object that corresponds to the SDM model object with the specified ID.
childrenCount() Returns the number of children in the SDM model for the current node, for instance if the current node is a subgraph.
childrenCount(tag) Returns the number of children with a given tag in the SDM model for the current node, for instance if the current node is a subgraph.
depends(expr, prop1, ...,propn) Returns the value of the expression argument. The expression is declared to depend on the mode properties prop1 to propn. When these properties change, the expression is re-evaluated.
If the CSS engine encounters an error while it is resolving an expression, it silently ignores the declaration.

Custom functions

Users of CSS for Java can register their own functions, which can be part of an expression. A custom function must implement IlvSDMCSSFunction. This is an abstract class, but technically you should consider it just like an interface.
The signature of the main method is as follows:
public Object call(Object[] args, Class type, IlvSDMEngine engine, 
                   Object node, Object target, Object closure);
  • When a function is evaluated, the parameters are first resolved as subexpressions. Then the final values of parameters are passed to the args array.
  • The parameter type is the expected type of the function, when known. A null value is possible. Implementation should take care to return an object of this type; otherwise the conversion will only be performed if it can be (that is, if it is a simple conversion between primitive types or to String).
  • The parameters engine , node , and target are determined at invocation time as follows: engine is the current SDM engine, node is the current model object being customized, and target is the graphic object being customized. Not all functions need these parameters (see, for example, code example Calling the custom function average).
  • The parameter closure allows the caller to retrieve the context on exit from the method.
If an error occurs during the call , the exception will be reported and the current property setting will be canceled.
The following code example gives an example of a function that computes the average value of its parameters.
Custom function example: average of parameters
import ilog.views.sdm.renderer.IlvSDMCSSFunction;
import.ilog.views.sdm.IlvSDMEngine;

public class Average extends IlvSDMCSSFunction {
   //default constructor
   public Average() { }

   // Returns 'avrg'
   public String getName() {
      return "avrg";
   }

   // Returns ','
   public String getDelimiters() {
      return ",";
   }

   // Returns the average of arguments.
   public Object call(Object[] args, Class type, IlvSDMEngine engine,
                      Object node, Object target) {
      // Assume only double, for the sake of simplicity.
      double result = 0d;
      for (int i=0; i<args.length; i++) {
         if (args[i] != null) {
            result += Double.parseDouble(args[i].toString());
         }
      }
      result /= args.length;
      return new Double(result);
   }
}
The following code example shows an example of how to call a custom function, where the custom function is the Average class, which has the return value avrg . Note that this function does not require information from the engine.
Calling the custom function average
node {
   width : @|avrg(@param1,@param2);
}
The following code example gives an example of a CSS function for SDM, which returns the graphic object ( IlvGraphic ) associated with the object whose ID is specified as argument.
Custom function example: Get Graphic Object From ID
class SDMGetGraphic extends IlvSDMCSSFunction {

   public SDMGetGraphic() {
   }

   public String getName() {
     return "getGraphicFromId";
   }

   public Object call(Object[] args, Class type, IlvSDMEngine engine,
                      Object node, Object target) {

     if (args.length < 1)
       throw new IllegalArgumentException("getGraphicFrom Id needs an id");

     String id = (String)args[0];
     IlvSDMModel model = engine.getModel();
     Object ref = model.getObject(id);
     if (ref == null)
       return null;
     IlvGraphic graphic = engine.getGraphic(ref,true);
     return graphic;

   }

   public Feedback[] invert(Object[] args, Object value,
                            IlvSDMEngine engine,
                            Object node) {

     return null;
   }
}
The following code example shows an example of calling the getGraphicFromId function to return the graphic object associated with the current object. Note that this function does require information from the engine to retrieve the current object and its associated graphic object.
Calling the custom function GetGraphicFromId
   graphic : @|getGraphicFromId(@__ID) ;

Registering custom functions

You must register custom functions before using them in a style sheet.
To register a function, call registerFunction in ilog.views.sdm.renderer.IlvStyleSheetRenderer (given an IlvSDMEngine , use IlvRendererUtil.getRenderer to find the active instance of IlvStyleSheetRenderer ).
It is also possible to register a function in the style sheet, provided that the function is a JavaBean. You can set the property functionList to the list of custom functions, as class names separated by commas. This property is available in the IlvStyleSheetRenderer class. The following code example shows an example.
Registering a list of custom functions
StyleSheet {
   functionList : "myPackage.RevertFunction,tests.RandomFunction";
}

Expert feature: inverting expressions

In very special situations, expressions must be inverted. For example, if there is a rule:
node {
   x : @|log(@X) ;
}
and the representation of the node is moved horizontally, then the attribute named X in the model should be updated according to the expression and the new x value (here: X = 10^ x ).
If you can modify a representation object in the interface, the model attributes described in the style sheet may change. When the style sheet maps a property to an attribute directly, as in:
label : @name ; 
the update is automatic. But if the mapping is realized through a function, as in:
label : '@|concat("name is: ",@name)' ; 
then the function must be able to invert its operation (here, if the string evaluates to: “ name is: Bob ”, then the inverse function should set the name attribute to "Bob" ).
Note
In this release, expressions cannot be inverted. However custom functions can be inverted, using the invert method.
Custom functions can be inverted using the invert method, which is defined as follows:
public Feedback[] invert(Object[] args, Object value, IlvSDMEngine engine,
   Object target)
The parameters args , engine , target have the same meaning as in call . The parameter value is the final value, and the method returns an array of couples ( name , value ). The name is the attribute name, and value is the value to set for this attribute. The result is an array when there are several attributes to update.
If a function cannot be inverted, then a null value should be returned.
The following code example shows how the Average function implements the invert method.
The Invert function for updating the data model from the style sheet
public Feedback[] invert(Object[] args, Object value, IlvSDMEngine engine,
                         Object target) {
   ArrayList result = new ArrayList();
   //The simplest to do is to set all attributes to the same final value.
   for (int i=0; i<args.length; i++) {
      // assume there is only @ construct.
      String att = args[i].toString();
      // Skip ’@’, which is the first character.
      att = att.substring(1);
      //Create and fill Feedback structure.
      Feedback f = new IlvSDMCSSFunction.Feedback();
      f.property = att;
      f.value = value;
      // Record feedback.
      result.add(f);
   }
   // Convert to Feedback[].
   return result.toArray(new IlvSDMCSSFunction.Feedback[result.size()]);
}
As you see in this example, there are two assumptions in the implementation of invert for Average :
  • All attributes are set to the same value, which is the average value.
  • Parameters of the Average function must be attributes only.
Most functions are difficult (sometimes even impossible) to invert. They usually need a compromise to support a minimum operation. But remember that invert needs to be implemented only when the interface allows you to modify the rendered object in such a way that the style sheet change needs to be reflected in the data model. Usually it is better to modify the model directly.
The default functions try at best to deliver a sensible result for invert . They accept model indirection (that is, @name ) but no recursive calls to other functions.