[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