On 8/01/2014 14:28, David Grieve wrote:
The reason things are as they are is because most properties in the core 
classes are lazily created and I didn't want cause the property to be created 
just to see if it was bound. But that is a particular concern for the core 
classes and not so much for controls.
I see, this mechanism certainly doesn't try to do that. It only works when the properties are created in advance.
I'm not sure if you are aware of the javafx.css.SimpleStyleableXXXProperty 
classes. I'd be interested to see how your custom classes dovetail into these 
as I'd be more than willing to consider taking  your additions and calling them 
my own… uh, um, I mean, rolling them into the source code.
I wasn't aware of those, but I just took a look at them. They do make the process a little bit easier, although still require creating a CssMetaData object yourself. If you want to use the code or just the idea, please feel free -- it would be very nice for creating custom controls if it was part of JavaFX.
Have you looked at the initialization-on-demand holder pattern for creating the 
That's the "Super-lazy instantiation pattern from Bill Pugh" ? I've seen it several times in the JavaFX sources, and I think it is a neat way to avoid certain statics from being created immediately after the class loads.

However, I'm not sure if it applies here, the first getCssMetaData call basically is the trigger that causes all CssMetaData objects and the List to get created... I'm not sure how I could make it any more lazy than that. The Map I use in the properties themselves is static, so shouldn't cause too much footprint.

The code I use for the one of the SimpleStyleableXXXProperty is below. It relies on a Map that will cache CssMetaData objects (for different instances of the same property in a Control) and are only created when they're needed. CssMetaData objects only get created one time on access, after that further calls just do a quick map lookup.

It makes assumptions about the kind of converter needed, which could be parameterized further with another constructor option.

public class SimpleStyleableDoubleProperty extends StyleableDoubleProperty {
private static final Map<Class<?>, Map<String, CssMetaData<? extends Styleable, Number>>> CSS_META_DATA_BY_NAME_BY_CLASS = new HashMap<>();

  private final Object bean;
  private final String name;
  private final String cssName;
  private final double initialValue;

public SimpleStyleableDoubleProperty(Object bean, String name, String cssName, double initialValue) {

    this.bean = bean;
    this.name = name;
    this.cssName = cssName;
    this.initialValue = initialValue;

  public CssMetaData<? extends Styleable, Number> getCssMetaData() {
// TODO consider synchronizing CSS_META_DATA_BY_NAME_BY_CLASS -- depends on whether or not this ever gets called by more than one thread. Map<String, CssMetaData<?, Number>> cssMetaDataByName = CSS_META_DATA_BY_NAME_BY_CLASS.get(bean.getClass());

    if(cssMetaDataByName == null) {
      cssMetaDataByName = new HashMap<>();
CSS_META_DATA_BY_NAME_BY_CLASS.put(bean.getClass(), cssMetaDataByName);

CssMetaData<? extends Styleable, Number> cssMetaData = cssMetaDataByName.get(cssName);

    if(cssMetaData == null) {
cssMetaData = new CssMetaData<Styleable, Number>(cssName, StyleConverter.getSizeConverter(), initialValue) {
        public boolean isSettable(Styleable styleable) {
          return !SimpleStyleableDoubleProperty.this.isBound();

public StyleableDoubleProperty getStyleableProperty(Styleable styleable) {
          return SimpleStyleableDoubleProperty.this;

    return cssMetaData;

  public Object getBean() {
    return bean;

  public String getName() {
    return name;


On Jan 7, 2014, at 9:34 PM, John Hendrikx<hj...@xs4all.nl>  wrote:

Hi List,

I'm in the process of adding CSS metadata to a new control, and I noticed there 
is a lot of boilerplate.

In order to reduce this, I've created some custom classes StyleableProperty 
classes (SimpleStyleableXXXProperty), which reduces the boilerplate 
significantly without sacrificing much (if any) performance.  The only thing 
that I cannot easily provide in this fashion is the static getClassCssMetaData 
method.  From the documentation I understand it is there just for convience for 
subclass creators and is not used by the CSS engine at all -- atleast, 
everything seems to work.

The shortened version for CSS aware properties basically looks like:

  private final SimpleStyleableDoubleProperty cellAlignment = new 
SimpleStyleableDoubleProperty(this, "cellAlignment", "-fx-cell-alignment", 0.8);
  private final SimpleStyleableDoubleProperty density= new SimpleStyleableDoubleProperty(this, 
"density", "-fx-density", 0.02);
  private final SimpleStyleableBooleanProperty reflectionEnabled= new 
SimpleStyleableBooleanProperty(this, "reflectionEnabled", 
"-fx-reflection-enabled", true);
  private final SimpleStyleableBooleanProperty clipReflections= new 
SimpleStyleableBooleanProperty(this, "clipReflections", "-fx-clip-reflections", 

With one small bit of supporting code in the relevant class (Skin or Control), 
which is basically a non-static implementation of the standard CSS List example 

  private static List<CssMetaData<? extends Styleable, ?>>  cssMetaData;

  public List<CssMetaData<? extends Styleable, ?>>  getCssMetaData() {  // 
Unsynchronized. WC: list gets initialized multiple times.
    if(cssMetaData == null) {
      List<CssMetaData<? extends Styleable, ?>>  metaData = new 
      cssMetaData = Collections.unmodifiableList(metaData);

    return cssMetaData;

Note that the List is static and lazy-final.  The same goes for the 
getCssMetaData method in the SimpleStyleableXXXProperty classes.  There is a 
slight performance decrease in those classes as getCssMetaData is looked up 
from a static Map (indexed by Class + css property name) and lazily created as 
needed -- a Map lookup however should be quite fast enough.

I'm sure the design had good reason to do things as they are, and I'm wondering 
if reducing the boilerplate has left me missing something important.

I welcome any insights!


Reply via email to