Applying CSS to Java objects
Explains how CSS is applied to Java objects.
Presents the CSS engine.
Describes the data model and how it interacts with the CSS engine.
Explains how to recurse in the style sheet.
Explains how to use expressions in CSS declarations.
Describes the differences with the CSS mechanism.
The CSS Engine
The CSS selector mechanism was designed to match elements in HTML or XML documents. It can also be used to match a hierarchy of Java™ objects accessible from a model interface. In this context, the CSS level 2 recommendation is transposed for the Java language and used to set bean properties according to the Java object hierarchy and state.
The CSS declarations for each model object are sorted and used according to the application that controls the CSS engine. The declarations represent property settings on a target object. The target object concerned depends on the way the CSS engine is used.
The CSS engine has different responsibilities at load time and at run time:
At load time: creating and customizing series, points and elements of the chart itself.
At run time: customizing the series and points according to model changes.
Usually the left side of a declaration represents a bean property of the graphic object. The right side is a literal and, if it needs type conversion, the method setAsText is invoked on the Property Editor associated with the bean property.
The Data Model
The input data model represents the seed of the “CSS for Java” engine. It provides three important kinds of information to the CSS engine, required to resolve the selectors:
The tree structure of objects, which will be used by selector transitions.
Object type, ID, and tag (or user-defined type), which match element type, ID, and CSS classes. IDs and types are strings; CSS classes are words separated by a space character. ID is not required to be unique, although it is wise to assume so.
Attribute, which matches an attribute of the same name in an attribute condition within the selector.
The target object is the graphic object associated with the model object. The declarations change property values of the graphic object that corresponds to the matching model object, thereby customizing the graphic appearance given by the rendering.
Object Types and Attribute Matching
Setting a Property Value for a Class shows a rule that matches the object of class (type)
test_Vehicle, with the attribute
model equal to
sport, and sets the property
icon of the graphic object associated with this object (defined elsewhere) to
sport-car.gif.
Setting a Property Value for a Class
test_Vehicle[model=sport] {icon : "sport-car.gif";}
Attribute matching can be used to add dynamic behavior: a PropertyChange event occurring on the model can activate the CSS engine to set new property values on the graphic objects.
Color Change Behavior Dependent on an Attribute Value shows a rule that changes the color of any object of CSS class
computer whenever the model attribute
state is set to
down.
Color Change Behavior Dependent on an Attribute Value
.computer[state = down] {color : "gray"}
For more information on attribute matching see also
Attribute matching.
Object Identifiers and CSS Classes
All model objects have an ID. This ID can be checked against the # selector of a rule.
A "user-defined type” can be set for an object in a property called CSSclass. CSS classes are not necessarily related to data model semantics; they are devices to add to the pattern-matching capabilities in the style sheet. An object belongs to only one type but can belong to several (or no) CSS classes. A check on a CSS class is a check for its presence or absence. Therefore a CSS class can be seen as an attribute without a value.
Class Name
The class property is a reserved keyword indicating the class name of the generated graphic object. Obviously the class declaration is applied only when there is a creation request. If the model state changes, the graphic objects are customized by applying only new declarations coming from new matching rules of the style sheet. The class declaration is then simply ignored.
The right side of a class declaration is the fully-resolved name of the Java class, loaded with the system class loader. For example:
#annotation {
class : ilog.views.chart.graphic.IlvDataLabelAnnotation;
text : "Hello World !";
}
By extension, the class declaration also indicates the constructor to use to initialize the object. In the example above, an IlvDataLabelAnnotation instance is created by invoking the default constructor, and the text bean property is set to "Hello World !".
When no default constructor exists for a class, you can specify a particular constructor, provided the following limitation is respected: only constructors that export the parameters as bean properties are supported. In other words, the class should have the following interface:
public class foobar {
public foobar(FooType prop1, BarType prop2) {...}
public FooType getFoo() {...}
public BarType getBar() {...}
...
}
which gives the following class declaration:
#afoobar {
class : my.package.foobar(foo, bar);
foo: ...;
bar: ...;
}
When the CSS engine resolves the declaration, it first looks for the types of foo and bar bean properties, then tries to find a constructor with parameters of these types. If such a constructor exists, it is invoked. Find below a more concrete example with a LineBorder:
#border {
class: javax.swing.border.LineBorder(lineColor, thickness);
lineColor: 'red';
thickness: 2;
}
will use the LineBorder(Color color, int thickness) constructor.
Pseudo-classes
Pseudo-classes are the minimal building blocks of a selector that match model objects according to an external context. The syntax is like a CSS class but with a colon instead of a dot. For example, series:highlighted matches a series only if the series is highlighted. The user agent can resolve this pseudo-class at run time according to the state of each series.
A pseudo-class has the same specificity as a CSS class.
Model Indirection
The right side of a declaration resolves to a literal that is determined at run time by a
Property Editor. However, if the literal is prefixed by @, the remainder of the string is interpreted as a model attribute name. The declaration takes the value from the model object, as shown in
Setting a Property to an Attribute Value.
Setting a Property to an Attribute Value
series { lineWidth : "@width";}
The lineWidth property will be set to the value of the attribute called width in the model
Resolving URLs
Sometimes declaration values are URLs relative to the style sheet location. A special construct, standard in CSS level2, allows you to create a URL from the base URL of the current style sheet. For example:
imageURL : url(images/icon.gif);
This declaration extends the path of the current style sheet URL with images/icon.gif. This construct is very useful for creating a style sheet with images located relative to it, because the URL remains valid even if the style sheet is cascaded or imported elsewhere.
CSS Recursion
You are likely to want to specify a Java object as the value of a declaration. A simple convention allows you to recurse in the style sheet, that is, to define a new Java object which has the same style sheet but is unrelated to the current data model.
@# Construct
Prefix the value with ‘@#’ to create new beans when required as shown in
Creating a Bean in a Declaration.
Creating a Bean in a Declaration
form {
date : "@#dateBean";
title : "CSS rules";
}
Subobject#dateBean {
class : ’java.util.Date’;
time : ’23849291’;
}
The ‘@#’ operator extends the current data model by adding a dummy model object as the child of the current object. The object ID of the dummy object is the remainder of the string, beyond the ‘@#’ operator. The type of the dummy object is 'Subobject'. The dummy object inherits CSS classes and attributes from its parent.
The CSS engine creates and customizes a new subobject according to the declarations it finds for the dummy object. This means, in particular, that the Java class of the subobject is determined by value of the 'class' property. The newly created subobject becomes the value of the @# expression. In the declarations for the subobject, attribute references through the @ operator refer to the attributes of the parent object.
Once the subobject is completed, the previous model is restored so that normal processing is resumed.
In the above example, a java.util.Date object is created, with the time property set to 23849291. This new object is assigned to the date property of the form object.
@= and @+ Constructs
There are two refinements of the '@#ID' operator:
'@=ID'
Using '@=ID' instead of '@#ID' shares the instance. The first time the declaration is resolved, the object is created as with the '@#' operator. But for all subsequent accesses to the same value, '@=ID' will return the same instance, the one created the first time, without applying the rules. Note that all instances created with '@=' are cleared when a new style sheet is applied. '
'@+ID'
Using ‘@+ID’ instead of ‘@#ID’ avoids unnecessary objects creation. Basically ‘@+ID’ customizes only the object currently assigned to the property, unless it does not exist or its class is not the same as the one defined in the #ID rule. In this case, the object is first created, then customized, and then assigned to the property, as with an ‘@#’ construct.
The need for these refinements arises from a performance issue. The '@#' operator creates a new object each time a declaration is resolved. Usually a declaration is applied whenever a property changes. Under certain circumstances, the creation of objects may lead to expensive processing, so Rogue Wave® JViews Charts provides an optional mechanism to minimize the creation of objects during property changes.
@| Construct
A CSS declaration value starting with "@|" is interpreted as an expression (see
Expressions).
@ Construct
A CSS declaration value that is exactly "@" means cancel the property setting made in a previous rule. This construct is useful to prevent a property from being modified, especially when the default value is unknown. For example:
series {
lineWidth : 23;
}
series[name="Sales"] {
lineWidth : @;
}
These two rules say that the lineWidth property value should be set to 23, unless the series has the name "Sales". Without the "@" ability, the default value of lineWidth would have to be written down in the CSS.
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
There are some default functions provided by JViews Gantt: formatDate, formatDuration, and activityProperty.
The formatDate function formats a java.util.Date object, passed as the second argument, into a String, with a SimpleDataFormat string as the first argument.
The
formatDuration function formats an
IlvDuration object, passed as the second argument, into a
String, with one of the following supported constants as the first argument:
TIME_UNIT_MEDIUM,
TIME_UNIT_SHORT, or
LARGEST_UNIT_MEDIUM.
The
activityProperty function retrieves the value of a user-defined property from an activity. This function is useful for styling constraint graphics based on the value of the
From activity or
To activity associated with the constraint.
The following sample shows how the activityProperty function provides an additional level of indirection, which allows you to retrieve the id property of activities that are themselves the fromActivity and toActivity properties of the constraint.
Styling Constraint Graphics Based on From or To Activity
constraint {
class : 'ilog.views.gantt.graphic.IlvConstraintGraphic';
toolTipText : activityProperty(@fromActivity,"id")+"to"+
activityProperty(@toActivity,"id");
If the CSS engine encounters an error while it is resolving an expression, it silently ignores the declaration.
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). |
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. |
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. |
Custom functions
Users of CSS for Java™ can register their own functions, which can be part of an expression. A custom function must implement
IlvCSSFunction. This is an abstract class, but technically you should consider it like an interface.
The following code example shows the signature of the main method.
public Object call(Object[] args, Class type, IlvCSSModel model,
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 other parameters are the
model,
node,
target, and
closure at invocation time;
model is the current CSS object model,
node is the current CSS model object being customized, and
target is the graphic object or renderer being customized. Not all functions need these parameters. (See, for example,
Calling the Custom Function Average.)
If an error occurs during the call, the exception will be reported and the current property setting will be canceled.
The following sample shows an example of a function that computes the average value of its parameters.
Custom function example: average of parameters
import ilog.views.util.styling.IlvCSSFunction;
class Average extends IlvCSSFunction {
//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, IlvCSSModel model,
Object node, Object target, Object closure) {
// 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 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 CSS model.
Calling the Custom Function Average
constraint {
lineWidth : @|avrg(@param1,@param2);
}
Registering custom functions
You must register custom functions before using them in a style sheet.
To register a function, call registerFunction in ilog.views.gantt.IlvHierarchyChart or ilog.views.schedule.IlvScheduleDataChart.
Divergences from CSS2
Java objects are not HTML documents. The CSS2 syntax remains, so that a CSS editor can still be used to create the style sheet. However, the differences lead to adaptations of the CSS mechanism so that its power can be fully exploited and directed to some specific behavior.
Cascading
Cascading is explicit: the API offers a means of cascading style sheets. However, the !important and inherit tags are not supported for the sake of simplicity.
Pseudo-classes and Pseudo-elements
In CSS2 there are predefined pseudo-classes and the notion of pseudo-elements. In JViews, when applied to Java objects, there is no predefined pseudo-class for a data set, but you can define your own and use them in style rules.
The CSS2 predefined pseudo-elements and pseudo-classes (:link, :hover, and so forth) are not implemented because they have no meaning in Java.
Attribute matching
The attribute pattern in CSS2 makes the following checks for strings: presence [ att ], equality [ att= val], and inclusion [ att~= val]. The |= operator is disabled.
For Java objects, there are the following numeric comparators >, >=, <>, <=, <, with the usual semantics.
There are also equal and not-equal comparators which make the distinction between string comparison and numerical comparison:
Equal: "A==B" is true if and only if A and B are numerically equal (for example, 10 == 10.0); use "=" to test the equality of two Strings.
Not-equal: "A~B" is true if and only if A and B are two different Strings (for example, "10" ~ "10.0"); use "<>" to test the inequality of two numbers.
Operators Available in the Attribute Selectors
Operator | Meaning | Applicable To |
A | present | strings |
A=val | equals | strings |
A~val | not equals | strings |
A~=val | contains the word | strings |
A==val | equals | numbers |
A<>val | not equals | numbers |
A<val | less than | numbers |
A<=val | less than or equals | numbers |
A>val | greater than | numbers |
A>=val | greater than or equals | numbers |
Syntax Enhancement
CSS for Java requires the use of quotation marks when a token contains special characters, such as dot (.), colon (:), at sign (@), pound sign (#), space ( ), and so on.
Quotes can be used almost everywhere, in particular to delimit a declaration value, a minimal building block denoting a type, or a CSS class with reserved characters.
The closing “;” is optional.
Null Value
Sometimes it makes sense to specify a null value in a declaration. By convention, null is a zero-length string '' or "". For example:
public Object call(Object[] args,Object closure, Class type, IlvCSSModel
model, Object target, Object closure);
series[name="Foo"] {
color1 : '';
}
This will reset the color of the data series to its default value. The notation '' is also used to denote a null array for properties expecting an array of values.
Empty String
The null syntax does not allow you to specify an empty string in the style sheet. Instead, you can create an empty string, as shown in
Creating an Empty String.
Creating an Empty String
chart {
headreText : @#emptyString;
}
Subobject#emptyString {
class : 'java.lang.String';
}
Better still, you can use the sharing mechanism to avoid the creation of several strings. The
@= construct will create the empty string the first time only and will then reuse the same instance for all other occurrences of
@#emptyString, see
Sharing an Empty String.
Sharing an Empty String
chart {
headerText : @=emptyString;
}
Subobject#emptyString {
class : 'java.lang.String';
}
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.