[snow-cvs] r5 - in trunk: . src/java/org src/java/org/armedbear src/java/org/armedbear/lisp src/java/snow/binding src/lisp/snow src/lisp/snow/swing test/lib test/src/snow
Alessio Stalla
astalla at common-lisp.net
Sun Oct 18 22:14:02 UTC 2009
Author: astalla
Date: Sun Oct 18 18:14:01 2009
New Revision: 5
Log:
Added property change listener supporting nested properties (dot notation)
and relative tests. Modified build.xml to launch JUnit tests.
Added Java-friendly callback class.
Small refactorings.
Added:
trunk/src/java/org/
trunk/src/java/org/armedbear/
trunk/src/java/org/armedbear/lisp/
trunk/src/java/org/armedbear/lisp/Callback.java
trunk/src/java/snow/binding/BeanPropertyPathBinding.java
trunk/src/lisp/snow/cells.lisp (contents, props changed)
- copied, changed from r2, /trunk/src/lisp/snow/swing/cells.lisp
trunk/src/lisp/snow/swing/data-binding.lisp (props changed)
- copied unchanged from r4, /trunk/src/lisp/snow/swing/binding-jgoodies.lisp
trunk/test/lib/
trunk/test/lib/junit.jar (contents, props changed)
Removed:
trunk/src/lisp/snow/swing/binding-jgoodies.lisp
trunk/src/lisp/snow/swing/cells.lisp
Modified:
trunk/build.xml
trunk/src/java/snow/binding/AccessorBinding.java
trunk/src/lisp/snow/packages.lisp
trunk/src/lisp/snow/snow.asd
trunk/src/lisp/snow/swing/snow-swing.asd
trunk/test/src/snow/BindingTest.java
Modified: trunk/build.xml
==============================================================================
--- trunk/build.xml (original)
+++ trunk/build.xml Sun Oct 18 18:14:01 2009
@@ -29,11 +29,13 @@
snow.source.zip snow.source.tar
-- create source distributions in ${dist.dir}.
snow.clean
- -- remove SNOW intermediate files</echo>
+ -- remove SNOW intermediate files
+ snow.test
+ -- run SNOW's JUnit tests</echo>
</target>
<patternset id="snow.source.java">
- <include name="snow/**/*.java"/>
+ <include name="**/*.java"/>
</patternset>
<patternset id="snow.source.lisp">
@@ -72,9 +74,6 @@
<format property="build.stamp" pattern="yyyymmdd-HHmm"/>
</tstamp>
- <property name="snow.test.log.file"
- value="snow-test-${build.stamp}.log"/>
-
<!--- antversion fails in ant 1.7.1 <antversion property="ant.version"
atleast="1.7"/> -->
<property name="java.path"
@@ -216,8 +215,50 @@
</zip>
</target>
- <import file="netbeans-build.xml" optional="true"/>
-<!-- <import file="j-build.xml" optional="true"/> -->
- <import file="not.org-build.xml" optional="true"/>
+ <!-- Testing -->
+
+ <property name="snow.test.dir" value="${basedir}/test"/>
+
+ <property name="snow.test.src.dir" value="${snow.test.dir}/src"/>
+ <property name="snow.test.classes.dir" value="${snow.test.dir}/bin"/>
+
+ <patternset id="snow.test.source.java">
+ <!-- For now, we list tests explicitly, because we have to
+ enumerate them later to the JUnit test runner. -->
+ <include name="snow/BindingTest.java"/>
+ </patternset>
+
+ <path id="snow.test.compile.classpath">
+ <pathelement location="${snow.test.dir}/lib/junit.jar"/>
+ <path refid="snow.classpath.build" />
+ </path>
+
+ <target name="snow.test.compile" depends="snow.compile">
+ <mkdir dir="${snow.test.classes.dir}"/>
+ <javac destdir="${snow.test.classes.dir}"
+ classpathref="snow.test.compile.classpath"
+ debug="true"
+ target="1.6">
+ <src path="${snow.test.src.dir}"/>
+ <patternset refid="snow.test.source.java"/>
+ </javac>
+ </target>
+
+ <path id="snow.test.run.classpath">
+ <path refid="snow.test.compile.classpath"/>
+ <pathelement location="${snow.test.classes.dir}"/>
+ <path refid="snow.classpath.build" />
+ </path>
+
+ <target name="snow.test" depends="snow.test.java"/>
+
+ <target name="snow.test.java" depends="snow.test.compile">
+ <java fork="true"
+ classpathref="snow.test.run.classpath"
+ classname="org.junit.runner.JUnitCore">
+ <arg value="snow.BindingTest"/>
+ </java>
+ </target>
+
</project>
Added: trunk/src/java/org/armedbear/lisp/Callback.java
==============================================================================
--- (empty file)
+++ trunk/src/java/org/armedbear/lisp/Callback.java Sun Oct 18 18:14:01 2009
@@ -0,0 +1,102 @@
+/*
+ * Callback.java
+ *
+ * Copyright (C) 2002-2005 Peter Graves
+ * $Id: Function.java 12079 2009-07-31 19:45:54Z ehuelsmann $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * As a special exception, the copyright holders of this library give you
+ * permission to link this library with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module. An independent module is a module which is not derived from
+ * or based on this library. If you modify this library, you may extend
+ * this exception to your version of the library, but you are not
+ * obligated to do so. If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+package org.armedbear.lisp;
+
+import java.util.concurrent.Callable;
+
+public abstract class Callback extends Function {
+
+ public Callback() {
+ super();
+ }
+
+ @Override
+ public LispObject execute() throws ConditionThrowable {
+ try {
+ return JavaObject.getInstance(call());
+ } catch(Throwable e) {
+ throw new ConditionThrowable(new JavaException(e));
+ }
+ }
+
+ protected Object call() throws Throwable {
+ return error(new WrongNumberOfArgumentsException(this));
+ }
+
+ @Override
+ public LispObject execute(LispObject arg0) throws ConditionThrowable {
+ try {
+ return JavaObject.getInstance(call(arg0.javaInstance()));
+ } catch(Exception e) {
+ throw new ConditionThrowable(new JavaException(e));
+ }
+ }
+
+ protected Object call(Object arg0) throws Exception, ConditionThrowable {
+ return error(new WrongNumberOfArgumentsException(this));
+ }
+
+ @Override
+ public LispObject execute(LispObject arg0, LispObject arg1) throws ConditionThrowable {
+ try {
+ return JavaObject.getInstance(call(arg0.javaInstance(), arg1.javaInstance()));
+ } catch(Exception e) {
+ throw new ConditionThrowable(new JavaException(e));
+ }
+ }
+
+ protected Object call(Object arg0, Object arg1) throws Exception, ConditionThrowable {
+ return error(new WrongNumberOfArgumentsException(this));
+ }
+
+ /** TODO **/
+
+ public static Callback fromRunnable(final Runnable r) {
+ return new Callback() {
+ protected Object call() {
+ r.run();
+ return null;
+ }
+ };
+ }
+
+ public static Callback fromCallable(final Callable<?> c) {
+ return new Callback() {
+ protected Object call() throws Exception {
+ return c.call();
+ }
+ };
+ }
+
+}
\ No newline at end of file
Modified: trunk/src/java/snow/binding/AccessorBinding.java
==============================================================================
--- trunk/src/java/snow/binding/AccessorBinding.java (original)
+++ trunk/src/java/snow/binding/AccessorBinding.java Sun Oct 18 18:14:01 2009
@@ -68,12 +68,14 @@
public void setValue(Object value) {
try {
writer.execute(JavaObject.getInstance(value, true), place);
- //valueChanged(value);
} catch (ConditionThrowable e) {
throw new RuntimeException(e);
}
}
+ /**
+ * Called from Lisp to notify a value change without invoking the writer.
+ */
public void valueChanged(Object value) {
fireValueChange(oldValue, value, false);
oldValue = value;
Added: trunk/src/java/snow/binding/BeanPropertyPathBinding.java
==============================================================================
--- (empty file)
+++ trunk/src/java/snow/binding/BeanPropertyPathBinding.java Sun Oct 18 18:14:01 2009
@@ -0,0 +1,159 @@
+/*
+ * BeanPropertyPathBinding.java
+ *
+ * Copyright (C) 2008-2009 Alessio Stalla
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * As a special exception, the copyright holders of this library give you
+ * permission to link this library with independent modules to produce an
+ * executable, regardless of the license terms of these independent
+ * modules, and to copy and distribute the resulting executable under
+ * terms of your choice, provided that you also meet, for each linked
+ * independent module, the terms and conditions of the license of that
+ * module. An independent module is a module which is not derived from
+ * or based on this library. If you modify this library, you may extend
+ * this exception to your version of the library, but you are not
+ * obligated to do so. If you do not wish to do so, delete this
+ * exception statement from your version.
+ */
+
+package snow.binding;
+
+import org.armedbear.lisp.ConditionThrowable;
+import org.armedbear.lisp.JavaObject;
+import org.armedbear.lisp.LispObject;
+import java.beans.*;
+import java.util.*;
+import java.lang.reflect.*;
+import com.jgoodies.binding.value.AbstractValueModel;
+import com.jgoodies.binding.value.ValueModel;
+
+public class BeanPropertyPathBinding
+ extends AbstractValueModel
+ implements PropertyChangeListener {
+
+ private String propertyName;
+ private Object object;
+ private Method removeMethod;
+ private BeanPropertyPathBinding nextListener;
+ private BeanPropertyPathBinding prevListener;
+ private String[] nextPropertyPath;
+ private Method reader;
+ private Method writer;
+
+ private static final Class[] addRemovePropertyChangeListenerSignature = new Class[] { PropertyChangeListener.class };
+
+ public BeanPropertyPathBinding(Object o, String propertyPath) {
+ this(o, propertyPath.split("\\."));
+ }
+
+ protected BeanPropertyPathBinding(Object o, String[] propertyPath,
+ BeanPropertyPathBinding prevListener) {
+ this.prevListener = prevListener;
+ Class<?> oClass = o.getClass();
+ object = o;
+ propertyName = propertyPath[0];
+ nextPropertyPath = new String[propertyPath.length - 1];
+ System.arraycopy(propertyPath, 1, nextPropertyPath, 0, nextPropertyPath.length);
+ try {
+ Method addPropertyChangeListener = oClass.getMethod("addPropertyChangeListener", addRemovePropertyChangeListenerSignature);
+ addPropertyChangeListener.invoke(o, this);
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ PropertyDescriptor pd = getPropertyDescriptor(oClass, propertyName);
+ reader = pd.getReadMethod();
+ writer = pd.getWriteMethod();
+ if(nextPropertyPath.length > 0) {
+ Object subObj = getValue();
+ if(subObj != null) {
+ nextListener = new BeanPropertyPathBinding(subObj, nextPropertyPath, this);
+ }
+ }
+ }
+
+ public BeanPropertyPathBinding(Object o, String[] propertyPath) {
+ this(o, propertyPath, null);
+ }
+
+ public void remove() {
+ try {
+ Method removePropertyChangeListener = object.getClass().getMethod("removePropertyChangeListener", addRemovePropertyChangeListenerSignature);
+ removePropertyChangeListener.invoke(object, this);
+ if(nextListener != null) {
+ nextListener.remove();
+ }
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static PropertyDescriptor getPropertyDescriptor(Class<?> c, String propertyName) {
+ try {
+ BeanInfo info = Introspector.getBeanInfo(c);
+ for(PropertyDescriptor pd : info.getPropertyDescriptors()) {
+ if(pd.getName().equals(propertyName)) {
+ return pd;
+ }
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public void propertyChange(PropertyChangeEvent evt) {
+ if(propertyName.equals(evt.getPropertyName())) {
+ if(nextListener != null) {
+ nextListener.remove();
+ }
+ if(nextPropertyPath.length > 0) {
+ Object subObj = evt.getNewValue();
+ if(subObj != null) {
+ nextListener = new BeanPropertyPathBinding(subObj, nextPropertyPath, this);
+ }
+ }
+ fireValueChange(evt);
+ }
+ }
+
+ protected void fireValueChange(PropertyChangeEvent evt) {
+ if(prevListener != null) {
+ prevListener.fireValueChange(evt);
+ } else {
+ fireValueChange(evt.getOldValue(), evt.getNewValue(), false);
+ }
+ }
+
+ @Override
+ public Object getValue() {
+ try {
+ return reader.invoke(object);
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void setValue(Object value) {
+ try {
+ writer.invoke(object, value);
+ } catch(Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
Copied: trunk/src/lisp/snow/cells.lisp (from r2, /trunk/src/lisp/snow/swing/cells.lisp)
==============================================================================
--- /trunk/src/lisp/snow/swing/cells.lisp (original)
+++ trunk/src/lisp/snow/cells.lisp Sun Oct 18 18:14:01 2009
@@ -31,36 +31,36 @@
(in-package :snow)
;;Cellular slot Binding
-(defmodel cells-binding (binding cells::model-object)
+(defmodel cells-data-binding (data-binding cells::model-object)
((expression :initarg :expression :reader binding-expression
:initform (error "expression is mandatory")
:cell t)
(writer :initarg writer :accessor binding-writer :initform nil :cell nil)
(model :accessor binding-model :initform nil :cell nil)))
-(defmethod initialize-instance :after ((obj cells-binding) &rest args)
+(defmethod initialize-instance :after ((obj cells-data-binding) &rest args)
(declare (ignore args))
(setf (binding-model obj)
(make-cells-value-model obj)))
-(defobserver expression ((binding cells-binding) new-value)
+(defobserver expression ((binding cells-data-binding) new-value)
(bwhen (it (binding-model binding))
(invoke "valueChanged" it new-value)))
-(defun make-cells-binding (expression &optional writer)
+(defun make-cells-data-binding (expression &optional writer)
(check-type writer (or null function))
(let ((instance
- (make-instance 'cells-binding :expression expression)))
+ (make-instance 'cells-data-binding :expression expression)))
(setf (binding-writer instance) writer)
instance))
-(defun make-slot-binding (object slot-accessor-name)
- (make-cells-binding
+(defun make-slot-data-binding (object slot-accessor-name)
+ (make-cells-data-binding
(eval `(c? (,slot-accessor-name ,object)))
(compile nil `(lambda (x)
(setf (,slot-accessor-name ,object) x)))))
-(defmethod make-model ((binding cells-binding))
+(defmethod make-model ((binding cells-data-binding))
(binding-model binding))
(defun make-cells-value-model (binding)
Modified: trunk/src/lisp/snow/packages.lisp
==============================================================================
--- trunk/src/lisp/snow/packages.lisp (original)
+++ trunk/src/lisp/snow/packages.lisp Sun Oct 18 18:14:01 2009
@@ -33,21 +33,24 @@
(:use :common-lisp :java #+snow-cells :cells)
(:shadow #+snow-cells #:dbg)
(:export
- ;Widgets
+ ;;Widgets
#:button
#:frame
#:label
#:panel
#:text-field
- ;Common operations on widgets
+ ;;Common operations on widgets
#:hide
#:pack
#:show
- ;Various
+ ;;Various
#:install-graphical-debugger
#:*parent*
#:self
- #:with-widget))
+ #:with-widget
+ ;;Java
+ #:invoke
+ #:new))
(defpackage :snow-user
(:use :common-lisp :snow :java :ext #+snow-cells :cells))
\ No newline at end of file
Modified: trunk/src/lisp/snow/snow.asd
==============================================================================
--- trunk/src/lisp/snow/snow.asd (original)
+++ trunk/src/lisp/snow/snow.asd Sun Oct 18 18:14:01 2009
@@ -39,6 +39,8 @@
(:file "snow")
(:file "repl")
(:file "data-binding")
+ #+snow-cells
+ (:file "cells")
(:file "backend")
(:file "debugger")
(:file "inspector")))
\ No newline at end of file
Modified: trunk/src/lisp/snow/swing/snow-swing.asd
==============================================================================
--- trunk/src/lisp/snow/swing/snow-swing.asd (original)
+++ trunk/src/lisp/snow/swing/snow-swing.asd Sun Oct 18 18:14:01 2009
@@ -34,6 +34,4 @@
:version "0.1"
:depends-on ()
:components ((:file "swing")
- (:file "binding-jgoodies")
- #+snow-cells
- (:file "cells")))
+ (:file "data-binding")))
Added: trunk/test/lib/junit.jar
==============================================================================
Binary file. No diff available.
Modified: trunk/test/src/snow/BindingTest.java
==============================================================================
--- trunk/test/src/snow/BindingTest.java (original)
+++ trunk/test/src/snow/BindingTest.java Sun Oct 18 18:14:01 2009
@@ -10,68 +10,103 @@
import net.miginfocom.swing.MigLayout;
-import org.junit.Test;
+import org.junit.*;
import com.jgoodies.binding.adapter.Bindings;
import com.jgoodies.binding.beans.Model;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.value.ValueModel;
+import java.beans.*;
+import snow.binding.*;
public class BindingTest {
- @Test
- public void testBinding() {
- final Bean bean = new Bean();
- ValueModel valueModel = new PropertyAdapter<Bean>(bean, Bean.PROPERTY, true);
- JFrame frame = new JFrame("test");
- frame.setLayout(new MigLayout());
- JTextField field1 = new JTextField(20);
- frame.add(field1, "wrap");
- JTextField field2 = new JTextField(20);
- field2.setColumns(20);
- frame.add(field2, "wrap");
- JLabel field3 = new JLabel();
- frame.add(field3, "wrap");
- Bindings.bind(field1, valueModel, true);
- Bindings.bind(field2, valueModel, false);
- Bindings.bind(field3, "text", new PropertyAdapter<Bean>(bean, Bean.PROPERTY, true));
- JButton resetButton = new JButton("reset");
- resetButton.addActionListener(new ActionListener() {
-
- @Override
- public void actionPerformed(ActionEvent e) {
- bean.setProperty("cippalippa");
- }
-
- });
- frame.add(resetButton);
- frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
- frame.pack();
- frame.setVisible(true);
+ @Test
+ public void testBinding() {
+ final Bean bean = new Bean();
+ ValueModel valueModel = new PropertyAdapter<Bean>(bean, Bean.PROPERTY, true);
+ JFrame frame = new JFrame("test");
+ frame.setLayout(new MigLayout());
+ JTextField field1 = new JTextField(20);
+ frame.add(field1, "wrap");
+ JTextField field2 = new JTextField(20);
+ field2.setColumns(20);
+ frame.add(field2, "wrap");
+ JLabel field3 = new JLabel();
+ frame.add(field3, "wrap");
+ Bindings.bind(field1, valueModel, true);
+ Bindings.bind(field2, valueModel, false);
+ Bindings.bind(field3, "text", new PropertyAdapter<Bean>(bean, Bean.PROPERTY, true));
+ JButton resetButton = new JButton("reset");
+ resetButton.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ bean.setProperty("cippalippa");
+ }
+
+ });
+ frame.add(resetButton);
+ frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ @Test
+ public void testPropertyPath() {
+ Bean bean = new Bean();
+ bean.setBean(new Bean());
+ bean.getBean().setProperty("ciao");
+ ValueModel model = new BeanPropertyPathBinding(bean, "bean.property");
+ final boolean[] flag = new boolean[] { true };
+ model.addValueChangeListener(new PropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent evt) {
+ System.out.println("change: " + evt);
+ flag[0] = false;
+ }
+ });
+ bean.getBean().setProperty("value2");
+ if(flag[0]) {
+ Assert.fail("value was set but listener not fired");
}
+ flag[0] = true;
+ bean.getBean().setProperty("value2");
+ if(!flag[0]) {
+ Assert.fail("value was set to same value and listener fired");
+ }
+ }
+
+ public static void main(String[] args) {
+ new BindingTest().testBinding();
+ }
+
+ public static class Bean extends Model {
+
+ public static final String PROPERTY = "property";
- public static void main(String[] args) {
- new BindingTest().testBinding();
+ private String property = "cippalippa";
+ private Bean bean;
+
+ public String getProperty() {
+ System.out.println("get " + property);
+ return property;
}
- public static class Bean extends Model {
-
- public static final String PROPERTY = "property";
-
- private String property = "cippalippa";
-
- public String getProperty() {
- System.out.println("get " + property);
- return property;
- }
+ public void setProperty(String property) {
+ String oldProperty = this.property;
+ this.property = property;
+ System.out.println("set " + property);
+ firePropertyChange(PROPERTY, oldProperty, property);
+ }
- public void setProperty(String property) {
- String oldProperty = this.property;
- this.property = property;
- System.out.println("set " + property);
- firePropertyChange(PROPERTY, oldProperty, property);
- }
-
+ public Bean getBean() {
+ return bean;
+ }
+
+ public void setBean(Bean bean) {
+ this.bean = bean;
}
+ }
+
}
More information about the snow-cvs
mailing list