Applying CSS to Java objects
Describes how the CSS selector mechanism is used to match a hierarchy of Java objects accessible from a CSS model interface.
Explains how CSS is applied to Java objects.
Describes the functions of the CSS engine at load time and run time.
Describes the information required for the CSS engine and explains the relationship between CSS and Java objects.
Describes how to style sheet recursion works using Java.
Explains how to use CSS constructs.
Explains how to use expressions in CSS declarations.
Explains changes to the CSS2 mechanism for specific behavior.
Overview
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 CSS 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.
In applying CSS to Java objects, the term
model object is used as the equivalent of the term
element in the W3C recommendation.
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.
JViews Gantt uses CSS declarations to create and customize graphic objects and renderers for objects in the Gantt data model and to customize components of the chart itself.
Possible customizations are:
In the Gantt and Schedule charts, activities in the Gantt data model are matched by the CSS selector mechanism to create and customize the activity and reservation graphic renderers. Constraints in the Gantt data model are matched to create and customize the constraint graphics.
In the Resource Data chart, resource data series are matched by the CSS selector mechanism to customize their rendering.
The CSS engine
The CSS engine has different responsibilities at load time and at run time:
At load time: creating and customizing graphic objects and renderers and customizing the chart itself.
At run time: customizing the graphic objects and renderers according to changes in the Gantt data model.
Usually the left side of a declaration represents a bean property of the chart, the graphic object, or the renderer. 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 CSS 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 used by selector transitions.
This structure consists of the chart itself and parts of the Gantt data model, such as the tree of activities for a Gantt chart.
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 or renderer associated with the model object. In the case of the chart itself, the CSS model object and the target graphic object are the same, that is, the chart. For CSS model objects that are part of the Gantt data model, such as activities, the graphic object is the associated activity renderer. 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.
In the Gantt and Schedule charts, the target object to which the CSS declarations are applied is usually:
In the Resource Data chart, the target object is usually an instance of
IlvResourceDataSet when styling the data series for a resource.
Object Types and Attribute Matching
The following code sample shows a rule that matches the object of type activity with the attribute completion greater than or equal to 1 and sets the property background of the graphic renderer associated with this object (defined elsewhere) to green.
Setting a property value for a class
activity[completion>='1'] {background : green;}
Attribute matching can be used to add dynamic behavior: a property_change event occurring on the model can activate the CSS engine to set new property values on the graphic objects.
The following code example shows a rule that changes the color of objects that are of CSS type activity and CSS class sales whenever the model attribute critical is set to true.
Color change behavior dependent on an attribute value
activity.sales[critical = true] {color : "gray"}
For more information see also
Attribute matching.
Object identifiers and CSS classes
The CSS ID of a model object can be checked against the # selector of a rule. For activities and resources, the CSS ID is obtained from the
id property of the object by calling the
getID method. The CSS ID of
IlvGeneralConstraint is obtained from the user-defined property with the reserved name
id. Other implementations of constraints have an undefined CSS ID.
CSS classes of a model object are matched against the CSS classes in the rule selector. The classes of an
IlvGeneralActivity or an
IlvGeneralConstraint object are given by the user-defined property with the reserved name
tags. Multiple classes are specified by setting the
tags property as a space-delimited string of CSS class names.
Other activity and constraint implementations do not support CSS class membership.
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 CSS classes or none. A check on a CSS class is for its presence or absence. Therefore a CSS class can be seen as an attribute without a value or as a Boolean attribute flagged by its presence or absence.
For example,
Matching CSS classes shows how to change the color of activities that are both critical and related to the Beta test.
Matching CSS classes
activity.critical.betatest {
background : red;
}
This rule matches all activities with a space-delimited tags property that contains the strings critical and betatest.
Class name
The class property is a reserved keyword indicating the class name of the generated graphic object or renderer. The class property must be specified somewhere in the rule hierarchy for every activity and constraint leaf rule. However, the class declaration is applied only when there is a creation request. If the model state is changed, the graphic objects and renderers are customized by applying only new declarations from new matching rules of the style sheet. Therefore, the class declaration is ignored if it is not declared in the subset of rules matched by the change in the model.
For activities, the right side of a class declaration is a class name that will be loaded by the system class loader. It may be:
activity {
class : 'ilog.views.gantt.graphic.renderer.IlvBasicActivityBar';
thickness : 3;
background : yellow;
}
For information and examples, see
Styling activities.
For constraints, the right side of a class declaration may be:
For information and examples, see
Styling constraints.
Pseudo-classes and pseudo-elements
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, activity:selected matches a node only if the activity is selected. The CSS engine can resolve this pseudo-class at run time according to the state of each model object.
Activities support the selected, leaf, milestone, and parent pseudo-classes. Constraints support the selected pseudo-class.
A pseudo-class has the same specificity as a CSS class.
Pseudo-elements are metaclasses, like pseudo-classes, but match document structure instead of the user agent state.
Model indirection
The right side of a declaration resolves to a literal that is determined at run time by a
Property Editor. 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
activity {
class : 'ilog.views.gantt.graphic.renderer.IlvBasicActivityBar';
background : powderblue;
label : '@id';
toolTipText : @'|"<html><center><b>"+@id+"<br>"+@name+"</b>
</center></html>"';
}
The label property labels the activity bar with the ID of the activity. The tooltip is rendered with HTML formatting and displays the activity name and ID on separate lines bold and centered.
Such indirection is also used in the opposite direction, that is, to retrieve the name of the model attribute that controls a graphic property. This allows user interactions to modify the data model correctly. Two special names,
@id and
@tags, represent values of the user-defined properties with the reserved names
id and
tags, returned by calls to the method
getProperty.
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 recur in the style sheet, that is, to define a new Java object that has the same style sheet, but is unrelated to the current data model.
Constructs
@# 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
activity {
class : ilog.views.gantt.graphic.renderer.IlvActivityCompositeRenderer;
renderer[0] : @Subobject#barRenderer;
}
#barRenderer {
class : 'ilog.views.gantt.graphic.renderer.IlvBasicActivityBar';
thickness : 1;
}
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. In particular, this means that the Java class of the subobject is determined by the 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
Creating a bean in a declaration, an
IlvBasicActivityBar object is created, with the
thickness property set to
1. This new object is assigned to the
renderer[0] property of the
activity object, which is an instance of
IlvActivityCompositeRenderer.
@= 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 access 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 useless 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, the same 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 JViews Gantt 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:
The @ construct for preventing a property from being modified
activity {
class : "ilog.views.gantt.graphic.renderer.IlvActivityBar";
bottomMargin : "0.3";
}
activity:parent {
bottomMargin : @;
}
These two rules say that the bottomMargin property value should be set to 0.3, unless the activity has the CSS pseudo-class parent. Without the "@" capability, the default value of bottomMargin 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 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
The pseudo-class construct is fully implemented and used to represent renderer-specific states or GUI items.
The list of predefined pseudo-classes is as follows:
selected
parent
milestone
leaf
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 that 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 ( : ), commercial at sign ( @ ), hash sign ( # ), space ( ), and so on.
Quotes can be used almost everywhere, in particular to delimit a declaration value, an element type, or a CSS class with reserved characters.
The closing semicolon ( ; ) 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:
node.not-handled {
class : '';
}
When a null class name is specified, no object is created at all and no error is reported, as it would be for a malformed class name.
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. The following code example shows that you can create an empty string.
Creating an empty string
activity {
toolTipText : @#emptyString;
}
#emptyString {
class : 'java.lang.String';
}
Better still, you can use the sharing mechanism to avoid the creation of several strings. The following code example shows that the @= construct will create the empty string the first time only and will then reuse the same instance for all other occurrences of @#emptyString.
Sharing an empty string
activity {
toolTipText : @=emptyString;
}
#emptyString {
class : 'java.lang.String';
}
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.