Applying CSS to Java objects
Shows how to apply the CSS to Java™ objects.
Provides an overview of how to map the CSS mechanism to the hierarchy of Java™ objects
Describes the CSS engine and what it does at load time and at run time.
Describes the input data model and the information it supplies to the CSS engine.
Explains recursion in CSS documents.
Explains how to use expressions in CSS declarations.
Describes the differences between the CSS2 syntax and the style sheets used in Rogue Wave® JViews Diagrammer.
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 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 objects is used as the equivalent of the term
elements 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.
Rogue Wave® JViews Diagrammer uses CSS declarations to create and customize one graphic object for each object in the data model, and to create and customize renderers according to user settings.
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
At run time: customizing the graphic objects 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 Java method s
etAsText 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.
In Rogue Wave® JViews Diagrammer the target object is an
IlvGraphic instance (see
Graphic objects in
The Essential JViews Framework,for information about
IlvGraphic objects) or a composite graphic.
Object types and attribute matching
The following code example 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.
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.
The following code example shows a rule that changes the color of any object of CSS class computer whenever the model attribute state is set to down.
.computer[state = down] {color : "gray"}
For more information on attribute matching see also
Attribute matching.
Object identifiers and CSS classes
The Java model provides a
getID method which represents the ID of a model object. This ID can be checked against the # selector of a rule.
If there is a single CSS class in the selector, it is resolved with the
getTag method in the model.
Additional CSS classes can be set for an object in a property called CSSclass. The engine automatically merges the result of getTag with the value of the CSSclass property.
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 class (its type) but can belong to several (or no) CSS classes. 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.
By default, an XML element name is defined as a CSS class. If, for example, a simple XML file contains the element names root and leaf, then the following code example shows how to change the color of leaf nodes to an RGB color specification.
node.leaf {
fillColor1 : 255,198,202;
foreground : 153,40,100;
}
The RGB color specification shown for the foreground (border) color is magenta.
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 is changed, 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.
To change the class, you must remove the model object and add it again.
The right side of a class declaration may be:
The class name, loaded with the system class loader. For example:
link {
class : ilog.views.sdm.graphic.IlvGeneralLink;
foreground : red;
}
A factory (interface
IlvRectangularObjectFactory). The factory requires an
IlvRect object which should be present in the declarations. This rectangle will be passed as a parameter of the factory. For example:
node {
class : ilog.views.interactor.IlvMakeFilledRectangleInteractor;
IlvRect : 0,0,100,200;
}
There is a factory for most of the available graphic components. See
IlvGraphicFactories for more details.
A path name to a file. The class name is forwarded to the beans library (method
java.beans.Beans.instantiate ) so a serialized bean is suitable. For example:
link {
class : data.beans.gauge;
foreground : red;
}
When beanName is used as a serialized object name, the given beanName is converted to a resource path name and a trailing .ser suffix is added. An attempt is made to load a serialized object from that resource.
In this example, Beans.instantiate would try to read a serialized object from the resource data/beans/gauge.ser.
Pseudo-classes and pseudo-elements
Pseudo-classes are the minimal building blocks of a selector which 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,
node:selected matches a
node only if the node is selected. The user agent can resolve this
pseudo-class at run time according to the state of each node.
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. 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 the following code example.
node { label : "@caption"; title : "CSS rocks";}
The label property will be set to the value of the attribute called caption in the model. If the specified attribute does not exist for the object, it is searched for recursively in the model ascendancy. The title property will be set to the literal CSS rocks.
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
@_TAG, represent values returned by the model method calls
getID and
getTag getTag respectively.
In a palette symbol CSS, the indirection cannot directly access the properties of the SDM object that is rendered by the palette symbol. In this case, the indirection can only access the symbol parameters. If you want a property of a graphic element of the symbol to be mapped to a property of the corresponding SDM object, you have to define a parameter and use it as an intermediate property. The ID of this parameter can then be accessed in the symbol CSS as an object property. In the outer CSS, you can use this parameter ID as a bean property.
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 the following code example.
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 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 code example
Creating a bean in a declaration, 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 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 Rogue Wave® JViews Diagrammer 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:
Canceling a property setting
node {
width : 23;
}
node.fixed {
width : @;
}
These two rules say that the width property value should be set to 23, unless the node has the CSS class fixed. Without the @ ability, the default value of width 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
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.
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 and where they are used is as follows:
init (is automatically enabled at creation time only.
:init rules are not used if the bean is customized only)
selected (any renderer)
collapsed or
expanded (
expandCollapseRenderer )
<renderer_name>, for example,
legendRenderer (to specify that the rule applies only to rendering properties and not graphic object properties)
renderer (to specify a property belonging to a renderer, as opposed to one with the same name belonging to the graphic object)
tree (Workflow Modeler)
table ( Workflow Modeler)
You can add custom pseudo-classes with the method IlvSDMEngine:setPseudoClasses.
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 (@), number sign (also known as 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 “;” 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. Instead, you can create an empty string, as shown in the following code example.
node {
label : @#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
node {
label : @=emptyString;
}
Subobject#emptyString {
class : 'java.lang.String';
}
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.