Developing with the SDK > Using CSS Syntax in the Style Sheet > Applying CSS To Java Objects > 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"

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 , where speed and drift are attributes of the current object.

'@|"name is: " + @name'-> "name is: Bob", if the value of current object attribute name is "Bob." Note the use of quotes to keep the space characters. You could use the backslash (\) character instead, directly preceding the space characters to retain them. The backslah works to quote any character that directly follows it. Use of the backslash character makes sure that the character thus quoted is not interpreted by the expression parser.

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

There are some default functions in the ilog.views.sdm.renderer package: concat, int, long, float, double. The first one concatenates its parameters as String; the others evaluate basic numerical expressions (only the four operators +, -, *, / are allowed).

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 ilog.views.sdm.renderer.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);

If an error occurs during the call, the exception will be reported and the current property setting will be canceled.

Code Sample 3.7 gives an example of a function that computes the average value of its parameters.

import ilog.views.sdm.renderer.IlvSDMCSSFunction;
import.ilog.views.sdm.IlvSDMEngine;

public class Average extends IlvSDMCSSFunction {
   //default contructor
   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);
   }
}

Code Sample 3.7 Custom Function Example: Average of Parameters

Code Sample 3.8 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.

node {
   width : @|avrg(@param1,@param2);
}

Code Sample 3.8 Calling the Custom Function Average

Code Sample 3.9 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.

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;
   }
}

Code Sample 3.9 Custom Function Example: Get Graphic Object From ID

Code Sample 3.10 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.

   graphic : @|getGraphicFromId(@__ID) ;

Code Sample 3.10 Calling the Custom Function GetGraphicFromId

Registering Custom Functions

You must register custom functions before using them in a style sheet.

To register a function, you can simply 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. Code Sample 3.11 shows an example.

StyleSheet {
   functionList : "myPackage.RevertFunction,tests.RandomFunction";
}

Code Sample 3.11 Registering a List of Custom Functions

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.

Code Sample 3.12 shows how the Average function implements the invert method.

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()]);
}

Code Sample 3.12 The Invert Function For Updating the Data Model From the Style Sheet

As you see in this example, there are two assumptions in the implementation of invert for Average:

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.