/******************************************************************************* * Copyright (c) 2012 Michael Vorburger (http://www.vorburger.ch). * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package ch.vorburger.xbindings; import org.eclipse.xtext.xbase.lib.Procedures.Procedure0; /** * XBindings. * * @author Michael Vorburger */ public class XBinding implements PropertyChangeListener { // TODO rename to XPropertyBinding or XValueBinding? @Override public void propertyChange() { this.apply(); } private static final ThreadLocal<PropertyChangeListener> currentThreadPropertyChangeListener = new ThreadLocal<>(); public static final PropertyAccessListener PROPERTY = new PropertyAccessListener() { @Override public void accessed(PropertyChangeNotifier cn) { final PropertyChangeListener propertyChangeListener = currentThreadPropertyChangeListener.get(); if (propertyChangeListener != null) cn.addChangeListener(propertyChangeListener); }; }; // TODO Later instead of using xtext.xbase.Procedure0 could use our own interface, just so that we don't depend on xtext just for this protected final Procedure0 binding; // TODO I'm not entirely sure yet if it's a better design to take Procedure0 // bindingStatement as constructor argument as it's now, or to instead make // XBinding extend Procedure0 and have an abstract apply() method // (like I had it initially). // public XBinding(final Procedure0 bindingStatement) { binding = bindingStatement; apply(); // initial assignment } // TODO doc! yada yada yada... applies the bindingStatement closure passed to constructor... records any accessors... protected void apply() { // This check is NOT NEEDED (causes a problem in fact; try it) // Not doing this and missing this use case would only be an issue in real world // if a Binding contained two statements, with the first an assignment/setter and the second an accessor (would be missed) // TODO Planned Refactoring to have Binding more clearly express Property to set and expression used to obtain its (one and only) value, thus avoiding Bindings with two Statements conceptually // if (currentThreadPropertyChangeListener.get() != null) { // throw new IllegalStateException("how come threadLocal is already set?!"); // } currentThreadPropertyChangeListener.set(this); try { binding.apply(); // should we catch(Throwable) & log in case of trouble? but as it nicely bubbles up, there is currently no need to do that.. // if Exception is caught, could remove/dispose binding here - but we would have to keep track of all PropertyChangeNotifiers seen for that } finally { currentThreadPropertyChangeListener.remove(); } } }