You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by gb...@apache.org on 2010/07/08 16:36:08 UTC

svn commit: r961785 - in /pivot/trunk: core/src/org/apache/pivot/beans/ core/src/org/apache/pivot/json/ demos/src/org/apache/pivot/demos/binding/

Author: gbrown
Date: Thu Jul  8 14:36:08 2010
New Revision: 961785

URL: http://svn.apache.org/viewvc?rev=961785&view=rev
Log:
Add initial support for BXML namespace binding.

Added:
    pivot/trunk/demos/src/org/apache/pivot/demos/binding/
    pivot/trunk/demos/src/org/apache/pivot/demos/binding/NamespaceBindingDemo.java
    pivot/trunk/demos/src/org/apache/pivot/demos/binding/namespace_binding_demo.bxml
Modified:
    pivot/trunk/core/src/org/apache/pivot/beans/NamespaceBinding.java
    pivot/trunk/core/src/org/apache/pivot/json/JSON.java

Modified: pivot/trunk/core/src/org/apache/pivot/beans/NamespaceBinding.java
URL: http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/beans/NamespaceBinding.java?rev=961785&r1=961784&r2=961785&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/beans/NamespaceBinding.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/beans/NamespaceBinding.java Thu Jul  8 14:36:08 2010
@@ -16,53 +16,203 @@
  */
 package org.apache.pivot.beans;
 
+import org.apache.pivot.collections.Dictionary;
+import org.apache.pivot.collections.Map;
+import org.apache.pivot.collections.MapListener;
+import org.apache.pivot.collections.Sequence;
+import org.apache.pivot.json.JSON;
+
 /**
- * Represents a binding relationship between an object property and a namespace
- * path.
+ * Represents a binding relationship between a source property and a target
+ * property within a namespace.
  */
 public class NamespaceBinding {
-    private Object object;
-    private String key;
-    private String path;
+    private Map<String, Object> namespace;
+
+    private String sourcePath;
+    private Object source;
+    private Map<String, Object> sourceMap;
+    private BeanMonitor sourceMonitor;
+    private String sourceKey;
+
+    private String targetPath;
+    private Object target;
+    private Dictionary<String, Object> targetDictionary;
+    private String targetKey;
+
+    private MapListener<String, Object> sourceMapListener = new MapListener.Adapter<String, Object>() {
+        @Override
+        public void valueUpdated(Map<String, Object> map, String key, Object previousValue) {
+            if (key.equals(sourceKey)) {
+                targetDictionary.put(targetKey, sourceMap.get(sourceKey));
+            }
+        }
+    };
+
+    private PropertyChangeListener sourcePropertyChangeListener = new PropertyChangeListener() {
+        @Override
+        public void propertyChanged(Object bean, String propertyName) {
+            if (propertyName.equals(sourceKey)) {
+                targetDictionary.put(targetKey, sourceMap.get(sourceKey));
+            }
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+    public NamespaceBinding(Map<String, Object> namespace, String sourcePath, String targetPath) {
+        if (namespace == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if (sourcePath == null) {
+            throw new IllegalArgumentException();
+        }
+
+        if (targetPath == null) {
+            throw new IllegalArgumentException();
+        }
+
+        // Set the namespace
+        this.namespace = namespace;
+
+        // Set the source properties
+        this.sourcePath = sourcePath;
+
+        Sequence<String> sourceKeys = JSON.parse(sourcePath);
+        int sourceKeyCount = sourceKeys.getLength();
+
+        source = JSON.get(namespace, sourceKeys, sourceKeyCount - 1);
+
+        if (source instanceof Map<?, ?>) {
+            sourceMap = (Map<String, Object>)source;
+            sourceMonitor = null;
+        } else {
+            sourceMap = new BeanAdapter(source);
+            sourceMonitor = new BeanMonitor(source);
+        }
+
+        sourceKey = sourceKeys.get(sourceKeyCount - 1);
+
+        if (!sourceMap.containsKey(sourceKey)) {
+            throw new IllegalArgumentException("Source property \"" + sourcePath
+                + "\" does not exist.");
+        }
+
+        // Set the target properties
+        this.targetPath = targetPath;
+
+        Sequence<String> targetKeys = JSON.parse(targetPath);
+        int targetKeyCount = targetKeys.getLength();
+
+        target = JSON.get(namespace, targetKeys, targetKeyCount - 1);
+
+        if (target instanceof Dictionary<?, ?>) {
+            targetDictionary = (Dictionary<String, Object>)target;
+        } else {
+            targetDictionary = new BeanAdapter(target);
+        }
+
+        targetKey = targetKeys.get(targetKeyCount - 1);
+
+        if (!targetDictionary.containsKey(targetKey)) {
+            throw new IllegalArgumentException("Target property \"" + targetPath
+                + "\" does not exist.");
+        }
 
-    public NamespaceBinding(Object object, String key, String path) {
-        this.object = object;
-        this.key = key;
-        this.path = path;
+        // Perform the initial set from source to target
+        targetDictionary.put(targetKey, sourceMap.get(sourceKey));
     }
 
     /**
-     * Returns the bound object.
+     * Returns the namespace.
      */
-    public Object getObject() {
-        return object;
+    public Map<String, Object> getNamespace() {
+        return namespace;
     }
 
     /**
-     * Returns the bound property.
+     * Returns the path to the source property.
      */
-    public String getKey() {
-        return key;
+    public String getSourcePath() {
+        return sourcePath;
     }
 
     /**
-     * Returns the namespace path to which the object property is bound.
+     * Returns the source object.
      */
-    public String getPath() {
-        return path;
+    public Object getSource() {
+        return source;
     }
 
     /**
-     * Binds the object property to the namespace path.
+     * Returns the name of the source property.
+     */
+    public String getSourceKey() {
+        return sourceKey;
+    }
+
+    /**
+     * Returns the path to the target property.
+     */
+    public String getTargetPath() {
+        return targetPath;
+    }
+
+    /**
+     * Returns the target object.
+     */
+    public Object getTarget() {
+        return target;
+    }
+
+    /**
+     * Returns the name of the target property.
+     */
+    public String getTargetKey() {
+        return targetKey;
+    }
+
+    /**
+     * Binds the source property to the target property.
      */
     public void bind() {
-        // TODO
+        if (source instanceof Map<?, ?>) {
+            sourceMap.getMapListeners().add(sourceMapListener);
+        } else {
+            sourceMonitor.getPropertyChangeListeners().add(sourcePropertyChangeListener);
+        }
     }
 
     /**
-     * Unbinds the object property from the namespace path.
+     * Unbinds the source property from the target property.
      */
     public void unbind() {
-        // TODO
+        if (source instanceof Map<?, ?>) {
+            sourceMap.getMapListeners().remove(sourceMapListener);
+        } else {
+            sourceMonitor.getPropertyChangeListeners().remove(sourcePropertyChangeListener);
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        boolean equals = false;
+
+        if (o instanceof NamespaceBinding) {
+            NamespaceBinding namespaceBinding = (NamespaceBinding)o;
+
+            equals = (source == namespaceBinding.source
+                && sourceKey.equals(namespaceBinding.sourceKey)
+                && target == namespaceBinding.target
+                && targetKey.equals(namespaceBinding.targetKey));
+        }
+
+        return equals;
+    }
+
+    @Override
+    public int hashCode() {
+        return (source.hashCode() * sourceKey.hashCode()
+            + target.hashCode() * targetKey.hashCode());
     }
 }

Modified: pivot/trunk/core/src/org/apache/pivot/json/JSON.java
URL: http://svn.apache.org/viewvc/pivot/trunk/core/src/org/apache/pivot/json/JSON.java?rev=961785&r1=961784&r2=961785&view=diff
==============================================================================
--- pivot/trunk/core/src/org/apache/pivot/json/JSON.java (original)
+++ pivot/trunk/core/src/org/apache/pivot/json/JSON.java Thu Jul  8 14:36:08 2010
@@ -57,6 +57,24 @@ public class JSON {
      * Returns the value at the given path.
      *
      * @param root
+     * The root object.
+     *
+     * @param keys
+     * The path to the value.
+     *
+     * @return
+     * The value at the given path.
+     *
+     * @see #get(Object, Sequence, int)
+     */
+    public static Object get(Object root, Sequence<String> keys) {
+        return get(root, keys, keys.getLength());
+    }
+
+    /**
+     * Returns the value at a given depth along a path.
+     *
+     * @param root
      * The root object; must be an instance of {@link org.apache.pivot.collections.Map}
      * or {@link org.apache.pivot.collections.List} or a Java bean object.
      *
@@ -64,13 +82,13 @@ public class JSON {
      * The path to the value, as a set of keys.
      *
      * @return
-     * The value at the given path.
+     * The value at the given depth along the given path.
      */
     @SuppressWarnings("unchecked")
-    public static Object get(Object root, Sequence<String> keys) {
+    public static Object get(Object root, Sequence<String> keys, int depth) {
         Object value = root;
 
-        for (int i = 0, n = keys.getLength(); i < n; i++) {
+        for (int i = 0; i < depth; i++) {
             String key = keys.get(i);
 
             if (value instanceof Sequence<?>) {

Added: pivot/trunk/demos/src/org/apache/pivot/demos/binding/NamespaceBindingDemo.java
URL: http://svn.apache.org/viewvc/pivot/trunk/demos/src/org/apache/pivot/demos/binding/NamespaceBindingDemo.java?rev=961785&view=auto
==============================================================================
--- pivot/trunk/demos/src/org/apache/pivot/demos/binding/NamespaceBindingDemo.java (added)
+++ pivot/trunk/demos/src/org/apache/pivot/demos/binding/NamespaceBindingDemo.java Thu Jul  8 14:36:08 2010
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.pivot.demos.binding;
+
+import java.net.URL;
+
+import org.apache.pivot.beans.Bindable;
+import org.apache.pivot.beans.NamespaceBinding;
+import org.apache.pivot.collections.Map;
+import org.apache.pivot.util.Resources;
+import org.apache.pivot.wtk.Window;
+
+public class NamespaceBindingDemo extends Window implements Bindable {
+    @Override
+    public void initialize(Map<String, Object> namespace, URL location, Resources resources) {
+        // One-way binding
+        NamespaceBinding textInputToLabelBinding = new NamespaceBinding(namespace,
+            "textInput.text", "label.text");
+        textInputToLabelBinding.bind();
+
+        // Two-way binding
+        NamespaceBinding textInput1ToTextInput2Binding = new NamespaceBinding(namespace,
+            "textInput1.text", "textInput2.text");
+        textInput1ToTextInput2Binding.bind();
+
+        NamespaceBinding textInput2ToTextInput1Binding = new NamespaceBinding(namespace,
+            "textInput2.text", "textInput1.text");
+        textInput2ToTextInput1Binding.bind();
+
+        // Style binding
+        NamespaceBinding colorChooserButtonToLabelTextBinding = new NamespaceBinding(namespace,
+            "colorChooserButton.selectedColor", "colorLabel.text");
+        colorChooserButtonToLabelTextBinding.bind();
+
+        NamespaceBinding colorChooserButtonToLabelColorBinding = new NamespaceBinding(namespace,
+            "colorChooserButton.selectedColor", "colorLabel.styles['color']");
+        colorChooserButtonToLabelColorBinding.bind();
+    }
+}

Added: pivot/trunk/demos/src/org/apache/pivot/demos/binding/namespace_binding_demo.bxml
URL: http://svn.apache.org/viewvc/pivot/trunk/demos/src/org/apache/pivot/demos/binding/namespace_binding_demo.bxml?rev=961785&view=auto
==============================================================================
--- pivot/trunk/demos/src/org/apache/pivot/demos/binding/namespace_binding_demo.bxml (added)
+++ pivot/trunk/demos/src/org/apache/pivot/demos/binding/namespace_binding_demo.bxml Thu Jul  8 14:36:08 2010
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to you under the Apache License,
+Version 2.0 (the "License"); you may not use this file except in
+compliance with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<binding:NamespaceBindingDemo title="Namespace Binding Test" maximized="true"
+    xmlns:bxml="http://pivot.apache.org/bxml"
+    xmlns:binding="org.apache.pivot.demos.binding"
+    xmlns="org.apache.pivot.wtk">
+    <Form>
+        <Form.Section heading="One-Way Binding">
+            <TextInput bxml:id="textInput" Form.label="Text Input"/>
+            <Label bxml:id="label" Form.label="Label"/>
+        </Form.Section>
+
+        <Form.Section heading="Two-Way Binding">
+            <TextInput bxml:id="textInput1" Form.label="Text Input 1"/>
+            <TextInput bxml:id="textInput2" Form.label="Text Input 2"/>
+        </Form.Section>
+
+        <Form.Section heading="Style Binding">
+            <ColorChooserButton bxml:id="colorChooserButton" Form.label="Color Chooser Button"
+                selectedColor="#000000"/>
+            <Label bxml:id="colorLabel" Form.label="Color Label"/>
+        </Form.Section>
+    </Form>
+</binding:NamespaceBindingDemo>