/* TrackerNodeImpl.java Purpose: Description: History: Aug 25, 2011 9:24:41 AM, Created by henrichen Copyright (C) 2011 Potix Corporation. All Rights Reserved. */ package org.zkoss.bind.tracker.impl; import java.io.Serializable; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import org.zkoss.bind.sys.Binding; import org.zkoss.bind.sys.LoadBinding; import org.zkoss.bind.sys.ReferenceBinding; import org.zkoss.bind.sys.tracker.TrackerNode; import org.zkoss.bind.xel.zel.BindELContext; /** * @author henrichen * @since 6.0.0 */ public class TrackerNodeImpl implements TrackerNode, Serializable { private static final long serialVersionUID = 1463169907348730644L; private final Object _script; //script of this node (e.g. firstname or ['firstname']) private final Map<Object, TrackerNode> _dependents; //kid script -> kid TrackerNode private final Map<Object, Object> _brackets; //property -> bracket script private final Set<LoadBinding> _bindings; //associated bindings private final Set<ReferenceBinding> _refBindings; //associated ReferenceBindings 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 public TrackerNodeImpl(Object property) { _script = property; _dependents = new HashMap<Object, TrackerNode>(4); _bindings = new HashSet<LoadBinding>(4); _refBindings = new HashSet<ReferenceBinding>(2); _brackets = new HashMap<Object, Object>(4); _associates = new HashSet<TrackerNode>(4); } public void addAssociate(TrackerNode node) { _associates.add(node); } public TrackerNode getDependent(Object property) { TrackerNode kid = getDependent0(property); if (kid == null) { //try bracket final Object script = _brackets.get(property); if (script != null) { kid = getDependent0(script); } } return kid; } public Set<TrackerNode> getDependents(Object property) { LinkedHashSet<TrackerNode> set = new LinkedHashSet<TrackerNode>(5); TrackerNode kid = getDependent0(property); if (kid != null) { set.add(kid); } Object script = _brackets.get(property); if (script == null && property instanceof String) { String prop = (String) property; if (BindELContext.isBracket(prop)) script = _brackets.get(prop.substring(1, prop.length() - 1)); } if (script != null) { kid = getDependent0(script); if (kid != null) set.add(kid); } return set; } public Set<TrackerNode> getDependents() { return collectDependents0(new HashSet<TrackerNode>()); } private TrackerNode getDependent0(Object script) { return _dependents.get(script); } public void addDependent(Object script, TrackerNode dependent) { _dependents.put(script, dependent); } public void tieProperty(Object property, Object script) { final Object oldscript = _brackets.get(property); if (script.equals(oldscript)) { return; } if (oldscript != null) { _brackets.remove(property); } for (final Iterator<Object> it = _brackets.values().iterator(); it.hasNext();) { final Object bracket = it.next(); if (script.equals(bracket)) { it.remove(); break; } } if (property != null) { _brackets.put(property, script); } } /* (non-Javadoc) * @see org.zkoss.bind.tracker.TrackerNode#removeDependent(java.lang.String) */ public TrackerNode removeDependent(Object script) { // TODO Auto-generated method stub return null; } public void addBinding(Binding binding) { if (binding instanceof ReferenceBinding) { _refBindings.add((ReferenceBinding) binding); } else { _bindings.add((LoadBinding) binding); } } public Set<Binding> getBindings() { final Set<Binding> bindings = new HashSet<Binding>(); bindings.addAll(getLoadBindings()); bindings.addAll(getReferenceBindings()); return bindings; } public Set<ReferenceBinding> getReferenceBindings() { return _refBindings; } public Set<LoadBinding> getLoadBindings() { return _bindings; } //bug# 1: depends-on is not working in nested C->B->A when A changed private Set<TrackerNode> collectDependents0(Set<TrackerNode> nodes) { final Set<TrackerNode> kids = getDirectDependents(); nodes.addAll(kids); for (TrackerNode kid : kids) { ((TrackerNodeImpl) kid).collectDependents0(nodes); //recursive } for (TrackerNode associate : _associates) { if (!nodes.contains(associate)) { //avoid endless loop nodes.add(associate); ((TrackerNodeImpl) associate).collectDependents0(nodes); //recursive } } return nodes; } public Set<TrackerNode> getDirectDependents() { return new HashSet<TrackerNode>(_dependents.values()); } public Set<TrackerNode> getAssociates() { return _associates; } public Object getBean() { Object bean = _bean == null ? null : _bean.get(); if (bean == null && _bean != null) { //Help GC setBean(null); } return bean; } public void setBean(Object bean) { _bean = bean == null ? null : new WeakReference<Object>(bean); } public Object getFieldScript() { return _script; } public Map<Object, Object> getPropNameMapping() { return _brackets; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[bean:").append(getBean()).append(",script:").append(_script).append("]@") .append(System.identityHashCode(this)); return sb.toString(); } }