Thanks, John. It would be helpful if you could create an issue in jira to track this.
On Jan 8, 2014, at 4:18 PM, John Hendrikx <hj...@xs4all.nl> wrote: > 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 List? > 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) { > super(initialValue); > > this.bean = bean; > this.name = name; > this.cssName = cssName; > this.initialValue = initialValue; > } > > @Override > 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) { > @Override > public boolean isSettable(Styleable styleable) { > return !SimpleStyleableDoubleProperty.this.isBound(); > } > > @Override > public StyleableDoubleProperty getStyleableProperty(Styleable > styleable) { > return SimpleStyleableDoubleProperty.this; > } > }; > } > > return cssMetaData; > } > > @Override > public Object getBean() { > return bean; > } > > @Override > public String getName() { > return name; > } > } > > --John > > >> >> 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", true); >>> >>> 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 code: >>> >>> private static List<CssMetaData<? extends Styleable, ?>> cssMetaData; >>> >>> @Override >>> public List<CssMetaData<? extends Styleable, ?>> getCssMetaData() { // >>> Unsynchronized. WC: list gets initialized multiple times. >>> if(cssMetaData == null) { >>> List<CssMetaData<? extends Styleable, ?>> metaData = new >>> ArrayList<>(super.getCssMetaData()); >>> Collections.addAll(metaData, >>> cellAlignment.getCssMetaData(), >>> density.getCssMetaData(), >>> reflectionEnabled.getCssMetaData(), >>> clipReflections.getCssMetaData() >>> ); >>> 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! >>> >>> --John >