Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

ZK-950: The expression reference doesn't update while change the inst…

…ant of the reference


http://www.zkoss.org/services/zk/tracker/?ZK-950


git-svn-id: https://svn.potix.com/svn/zk1/branches/6.0@19370 dd50bd9b-9913-0410-b9ba-e07a3075be86
  • Loading branch information...
commit 9dde41d0ab038854443d16c70c989eb8b396b137 1 parent 8ee5107
@henrichen henrichen authored
View
2  zkbind/src/org/zkoss/bind/impl/BinderImpl.java
@@ -189,6 +189,8 @@ private static void initAppValidators() {
public static final String RENDERER_INSTALLED = "$RENDERER_INSTALLED$";
public static final String IGNORE_TRACKER = "$IGNORE_TRACKER$"; //ignore adding currently binding to tracker, ex in init
+ public static final String REF_BINDING = "$REF_BINDING$"; //associated ReferenceBinding
+
public static final String SAVE_BASE = "$SAVE_BASE$"; //bean base of a save operation
public static final String ON_BIND_INIT = "onBindInit"; //do component binding initialization
public static final String MODEL = "$MODEL$"; //collection model for index tracking
View
1  zkbind/src/org/zkoss/bind/impl/InitFormBindingImpl.java
@@ -25,7 +25,6 @@
import org.zkoss.bind.sys.InitPropertyBinding;
import org.zkoss.xel.ExpressionX;
import org.zkoss.zk.ui.Component;
-import org.zkoss.zk.ui.UiException;
/**
* Implementation of {@link InitPropertyBinding}.
View
5 zkbind/src/org/zkoss/bind/impl/ReferenceBindingImpl.java
@@ -64,4 +64,9 @@ public String getPropertyString() {
public void invalidateCache() {
_cacheValue = null;
}
+
+ public String toString(){
+ return new StringBuilder().append(getClass().getSimpleName()).append("@").append(Integer.toHexString(hashCode()))
+ .append(",component:").append(getComponent()).toString();
+ }
}
View
70 zkbind/src/org/zkoss/bind/tracker/impl/TrackerImpl.java
@@ -27,6 +27,7 @@
import java.util.Set;
import java.util.WeakHashMap;
+import org.zkoss.bind.impl.BinderImpl;
import org.zkoss.bind.impl.WeakIdentityMap;
import org.zkoss.bind.sys.Binding;
import org.zkoss.bind.sys.ChildrenBinding;
@@ -37,7 +38,6 @@
import org.zkoss.bind.sys.tracker.Tracker;
import org.zkoss.bind.sys.tracker.TrackerNode;
import org.zkoss.bind.xel.zel.BindELContext;
-import org.zkoss.io.Serializables;
import org.zkoss.util.IdentityHashSet;
import org.zkoss.zk.ui.Component;
@@ -163,7 +163,7 @@ private void collectLoadBindings(Object base, String prop, Set<LoadBinding> bind
}
} else {
final Set<TrackerNode> nodes = getAllTrackerNodesByBean(base);
- if (nodes != null) {
+ if (nodes != null && !nodes.isEmpty()) {
getLoadBindingsPerProperty(nodes, prop, bindings, kidbases, visited);
}
}
@@ -192,6 +192,10 @@ public void tieValue(Object comp, Object base, Object script, Object propName, O
//ZK-877: NPE in a save only binding
//No corresponding LoadBinding with the head script in the specified component.
if (node != null) {
+ //ZK-950: The expression reference doesn't update while change the instant of the reference
+ //Update if the node refer to a ReferenceBinding
+ ((TrackerNodeImpl)node)
+ .setReferenceBinding((ReferenceBinding)((Component)comp).getAttribute(BinderImpl.REF_BINDING));
if (value != null) {
addBeanMap(node, value);
} else {
@@ -203,23 +207,76 @@ public void tieValue(Object comp, Object base, Object script, Object propName, O
} else {
final Set<TrackerNode> baseNodes = getAllTrackerNodesByBean(base);
if (baseNodes != null) { //FormBinding will keep base nodes only (so no associated dependent nodes)
+ final Set<TrackerNode> propNodes = new LinkedHashSet<TrackerNode>(); //normal nodes; i.e. a base + property node. e.g. vm.selectedPerson
+ Object bean = null;
for (TrackerNode baseNode : baseNodes) {
final TrackerNode node = baseNode.getDependent(script);
if (node == null) { //FormBinding will keep base nodes only (so no associated dependent nodes)
continue;
}
+ if (bean == null) {
+ bean = node.getBean();
+ }
+ propNodes.add(node);
if (BindELContext.isBracket((String)script)) {
((TrackerNodeImpl)baseNode).tieProperty(propName, script);
}
- if (value != null) {
+ }
+
+ //ZK-950: The expression reference doesn't update while change the instant of the reference
+ //Collect those nodes that refers to the same bean minus normal nodes
+ @SuppressWarnings("unchecked")
+ final Set<TrackerNode> beanNodes = (Set<TrackerNode>) (bean == null ? Collections.emptySet() : getAllTrackerNodesByBean(bean));
+ if (bean != null) {
+ beanNodes.removeAll(propNodes);
+ }
+
+ //Collect target ReferenceBinding in normal nodes. These are ReferenceBinding modified.
+ final Set<ReferenceBinding> targetRefBindings = collectReferenceBindings(propNodes);
+
+ if (value != null) {
+ for (TrackerNode node : propNodes) { //normal nodes
addBeanMap(node, value);
- } else {
+ }
+ for (TrackerNode node : beanNodes) { //other nodes
+ final ReferenceBinding rbinding = ((TrackerNodeImpl)node).getReferenceBinding();
+ //a node refers no ReferenceBinding or refer to the target ReferenceBinding
+ if (rbinding == null || targetRefBindings.contains(rbinding)) {
+ addBeanMap(node, value);
+ }
+ }
+ } else { //value == null
+ for (TrackerNode node : propNodes) { //normal nodes
removeAllBeanMap(node); //dependent nodes shall be null, too. Remove them from _beanMap
}
+ for (TrackerNode node : beanNodes) { //other nodes
+ final ReferenceBinding rbinding = ((TrackerNodeImpl)node).getReferenceBinding();
+ //refers no ReferenceBinding, normal cases
+ if (rbinding == null) {
+ removeAllBeanMap(node); //dependent nodes shall be null, too. Remove them from _beanMap
+ } else if (targetRefBindings.contains(rbinding)) { //refers to target ReferenceBinding
+ addBeanMap(node, rbinding);
+ }
+ }
}
}
}
}
+
+ //Collect ReferenceBinding within the node tree
+ private Set<ReferenceBinding> collectReferenceBindings(Collection<TrackerNode> nodes) {
+ final Set<LoadBinding> bindings = new LinkedHashSet<LoadBinding>();
+ final Set<TrackerNode> visited = new HashSet<TrackerNode>();
+ getLoadBindingsPerProperty(nodes, ".", bindings, null, visited);
+ final Set<ReferenceBinding> refBindings = new LinkedHashSet<ReferenceBinding>();
+ for (LoadBinding binding : bindings) {
+ if (binding instanceof ReferenceBinding) {
+ refBindings.add((ReferenceBinding)binding);
+ }
+ }
+ return refBindings;
+ }
+
//add node into the _beanMap
private void addBeanMap(TrackerNode node, Object value) {
//add node into _beanMap
@@ -319,6 +376,9 @@ private void getLoadBindings0(TrackerNode node, Set<LoadBinding> bindings, Set<O
if (binding instanceof LoadBinding) {
if (binding instanceof ReferenceBinding) {
((ReferenceBinding)binding).invalidateCache();
+ //ZK-950: The expression reference doesn't update while change the instant of the reference
+ //Try reload bindings that might refer this ReferenceBinding
+ collectLoadBindings(binding, ".", bindings, visited); //recursive
}
bindings.add((LoadBinding)binding);
}
@@ -330,7 +390,7 @@ private void getLoadBindings0(TrackerNode node, Set<LoadBinding> bindings, Set<O
}
final Object kidbase = node.getBean();
- if (kidbase != null) {
+ if (kidbases != null && kidbase != null) {
kidbases.add(kidbase);
} else {
//check all dependents
View
14 zkbind/src/org/zkoss/bind/tracker/impl/TrackerNodeImpl.java
@@ -21,6 +21,7 @@
import java.util.Set;
import org.zkoss.bind.sys.Binding;
+import org.zkoss.bind.sys.ReferenceBinding;
import org.zkoss.bind.sys.tracker.TrackerNode;
/**
@@ -35,6 +36,7 @@
private final Set<Binding> _bindings; //associated bindings
private final Set<TrackerNode> _associates; //dependent nodes of this node (e.g. fullname node is dependent node of this firstname node)
private transient WeakReference<Object> _bean; //associated bean value
+ private transient WeakReference<ReferenceBinding> _referBinding; //if this node refer to a ReferenceBinding, the ReferenceBinding; or null.
public TrackerNodeImpl(Object property) {
_script = property;
@@ -43,7 +45,17 @@ public TrackerNodeImpl(Object property) {
_brackets = new HashMap<Object, Object>(4);
_associates = new HashSet<TrackerNode>(4);
}
-
+ /*package*/ ReferenceBinding getReferenceBinding() {
+ ReferenceBinding rbinding = _referBinding == null ? null : _referBinding.get();
+ if (rbinding == null && _referBinding != null) { //Help GC
+ setReferenceBinding(null);
+ }
+ return rbinding;
+ }
+
+ /*package*/ void setReferenceBinding(ReferenceBinding rbinding) {
+ _referBinding = rbinding == null ? null : new WeakReference<ReferenceBinding>(rbinding);
+ }
/*package*/ void addAssociate(TrackerNode node) {
_associates.add(node);
}
View
23 zkbind/src/org/zkoss/bind/xel/zel/BindELResolver.java
@@ -62,10 +62,14 @@ protected ELResolver getELResolver() {
public Object getValue(ELContext ctx, Object base, Object property)
throws PropertyNotFoundException, ELException {
Object value = super.getValue(ctx, base, property);
- if (value instanceof ReferenceBinding) {
- value = ((ReferenceBinding)value).getValue((BindELContext)((EvaluationContext)ctx).getELContext());
- }
- tieValue(ctx, base, property, value, false);
+ //ZK-950: The expression reference doesn't update while change the instant of the reference
+ final ReferenceBinding rbinding = value instanceof ReferenceBinding ? (ReferenceBinding)value : null;
+ if (rbinding != null) {
+ value = rbinding.getValue((BindELContext) ((EvaluationContext)ctx).getELContext());
+ }
+ //If a ReferenceBinding evaluated to null, tie the ReferenceBinding itself as the
+ //evaluated bean, @see TrackerImpl#getLoadBindings0()
+ tieValue(ctx, base, property, value != null ? value : rbinding, false, rbinding);
return value;
}
@@ -75,7 +79,7 @@ public void setValue(ELContext ctx, Object base, Object property, Object value)
base = ((ReferenceBinding)base).getValue((BindELContext)((EvaluationContext)ctx).getELContext());
}
super.setValue(ctx, base, property, value);
- tieValue(ctx, base, property, value, true);
+ tieValue(ctx, base, property, value, true, null);
}
private static Path getPathList(BindELContext ctx){
@@ -105,7 +109,7 @@ private void saveEqualBeans(ELContext elCtx, Object base, String prop, Object va
}
//update dependency and notify changed
- private void tieValue(ELContext elCtx, Object base, Object propName, Object value,boolean allownotify) {
+ private void tieValue(ELContext elCtx, Object base, Object propName, Object value, boolean allownotify, ReferenceBinding refBinding) {
final BindELContext ctx = (BindELContext)((EvaluationContext)elCtx).getELContext();
if(ctx.ignoreTracker()) return;
final Binding binding = ctx.getBinding();
@@ -127,7 +131,12 @@ private void tieValue(ELContext elCtx, Object base, Object propName, Object valu
final Binder binder = binding.getBinder();
final BindContext bctx = (BindContext) ctx.getAttribute(BinderImpl.BINDCTX);
final Component ctxcomp = bctx != null ? bctx.getComponent() : binding.getComponent();
- ((BinderCtrl)binder).getTracker().tieValue(ctxcomp, base, script, propName, value);
+ final Object old = ctxcomp.setAttribute(BinderImpl.REF_BINDING, refBinding);
+ try {
+ ((BinderCtrl)binder).getTracker().tieValue(ctxcomp, base, script, propName, value);
+ } finally {
+ ctxcomp.setAttribute(BinderImpl.REF_BINDING, old);
+ }
if (base != null) {
if (binding instanceof SaveBinding) {
View
1  zkdoc/release-note
@@ -28,6 +28,7 @@ ZK 6.0.1
ZK-927: zkplus databinding1 should auto-wrapping BindingListModelXxx with setMultiple() and Selectable handled
ZK-939: TreeNode's getChildren() shall not return List<? extends TreeNode<E>>
ZK-919: ELSupport can't handle java.sql.Timestamp
+ ZK-950: The expression reference doesn't update while change the instant of the reference
* Upgrade Notes
+ TreeNode's getChildren() returns List<TreeNode<E>> instead of List<? extends TreeNode<E>>.
View
8 zktest/src/archive/bind/issue/B00950ReferenceChange.zul
@@ -4,6 +4,9 @@
<label multiline="true">
1.Select a person, both Direct and Reference should change to selected one
2.Select another person and edit it, both Direct and Reference should change also.
+ 3.Select the third empty one, both Direct and Reference should clear
+ 4.Select 1st person, both Direct and Reference should change to the selected one
+ 5.Click "Clear Selection" button, both Direct and Reference should clear
</label>
<listbox width="500px" model="@load(vm.personList)"
selectedItem="@bind(vm.selectedPerson)" checkmark="true">
@@ -22,11 +25,12 @@
<label value="@load(vm.selectedPerson.lastName)" />
<label value="@load(vm.selectedPerson.fullName)" />
==Reference==
- <div p="@ref(vm.selectedPerson)">
+ <vbox p="@ref(vm.selectedPerson)">
<label value="@load(p.firstName)" />
<label value="@load(p.lastName)" />
<label value="@load(p.fullName)" />
- </div>
</vbox>
+ </vbox>
+ <button label="Clear Selection" onClick="@command('clearSelection')"/>
<button label="dump" onClick="binder.tracker.dump()" />
</window>
View
15 zktest/src/org/zkoss/zktest/bind/issue/B00950ReferenceChange.java
@@ -3,17 +3,23 @@
import java.util.ArrayList;
import java.util.List;
+import org.zkoss.bind.annotation.Command;
import org.zkoss.bind.annotation.DependsOn;
+import org.zkoss.bind.annotation.NotifyChange;
+import org.zkoss.zul.ListModel;
+import org.zkoss.zul.ListModelList;
+import org.zkoss.zul.ext.Selectable;
public class B00950ReferenceChange {
- private List<Person> personList;
+ private ListModelList<Person> personList;
private Person selectedPerson;
public B00950ReferenceChange() {
- personList = new ArrayList<Person>();
+ personList = new ListModelList<Person>();
personList.add(new Person("Dennis","Chen"));
personList.add(new Person("Alice","Lin"));
+ personList.add(null);
}
public List<Person> getPersonList() {
@@ -27,6 +33,11 @@ public Person getSelectedPerson() {
public void setSelectedPerson(Person selectedPerson) {
this.selectedPerson = selectedPerson;
}
+
+ @Command @NotifyChange("selectedPerson")
+ public void clearSelection() {
+ selectedPerson = null;
+ }
public class Person {
private String firstName;
Please sign in to comment.
Something went wrong with that request. Please try again.