Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /* DataBinder.java
   
   {{IS_NOTE
   	Purpose:
   		
   	Description:
   		
   	History:
   		Thu Feb  1 18:27:18     2007, Created by Henri
  }}IS_NOTE
  
  Copyright (C) 2006 Potix Corporation. All Rights Reserved.
  
  {{IS_RIGHT
  }}IS_RIGHT
  */
  package org.zkoss.zkplus.databind;
  
  
  import  org.zkoss.zul.Row;
  import  org.zkoss.zul.Listitem;
  import  org.zkoss.zul.ListModel;
  
  
  import java.util.Map;
  import java.util.Set;
  import java.util.Date;
  import java.util.List;
  import java.util.HashSet;
  import java.util.HashMap;
  import java.util.HashSet;
The DataBinder used for binding ZK UI component and the backend data bean.

Author(s):
Henri Chen
  
  public class DataBinder {
  	public static final String NULLIFY = "none"//used to nullify default configuration
  	public static final String VARNAME = "zkplus.databind.VARNAME"//_var name
  	public static final String TEMPLATEMAP = "zkplus.databind.TEMPLATEMAP"// template -> clone
  	public static final String TEMPLATE = "zkplus.databind.TEMPLATE"//clone -> template
  	private static final String OWNER = "zkplus.databind.OWNER"//the collection owner of the template component
  	private static final String HASTEMPLATEOWNER = "zkplus.databind.HASTEMPLATEOWNER"//whether has template owner (collection in collection)
  	private static final Object NA = new Object();
  
  	private Map _compBindingMap = new LinkedHashMap(29); //(comp, Map(attr, Binding))
  	private Map _beans = new HashMap(29); //bean local to this DataBinder
  	private Map _beanSameNodes = new HashMap(29); //(bean, Set(BindingNode)) bean same nodes, diff expression but actually hold the same bean
  	private BindingNode _pathTree = new BindingNode("/"false"/"false); //path dependency tree.
  	private boolean _defaultConfig = true//whether load default configuration from lang-addon.xml
  	private boolean _init//whether this databinder is initialized. 
  		//Databinder is init automatically when saveXXX or loadXxx is called
  	
  	protected Map _collectionItemMap = new HashMap(3);

Binding bean to UI component. This is the same as addBinding(Component comp, String attr, String expr, (List)null, (List)null, (String)null, (String)null).

Parameters:
comp The component to be associated.
attr The attribute of the component to be associated.
expr The expression to associate the data bean.
  
  	public void addBinding(Component compString attrString expr) {
  		addBinding(compattrexpr, (List)null, (List)nullnullnull);
  	}

Binding bean to UI component.

Parameters:
comp The component to be associated.
attr The attribute of the component to be associated.
expr The expression to associate the data bean.
loadWhenEvents The event list when to load data.
saveWhenEvent The event when to save data.
access In the view of UI component: "load" load only, "both" load/save, "save" save only when doing data binding. null means using the default access natural of the component. e.g. Label.value is "load", but Textbox.value is "both".
converter The converter class used to convert classes between component and the associated bean. null means using the default class conversion method.
 
 	public void addBinding(Component compString attrString expr,
 		String[] loadWhenEventsString saveWhenEventString accessString converter) {
 		List loadEvents = null;
 		if (loadWhenEvents != null && loadWhenEvents.length > 0) {
 			loadEvents = new ArrayList(loadWhenEvents.length);
 			for (int j = 0; j < loadWhenEvents.length; ++j) {
 				loadEvents.add(loadWhenEvents[j]);
 			}
 		}
 		addBinding(compattrexprloadEventssaveWhenEventaccessconverter);
 	}

Binding bean to UI component.

Parameters:
comp The component to be associated.
attr The attribute of the component to be associated.
expr The expression to associate the data bean.
loadWhenEvents The event list when to load data.
saveWhenEvents The event when to save data.
access In the view of UI component: "load" load only, "both" load/save, "save" save only when doing data binding. null means using the default access natural of the component. e.g. Label.value is "load", but Textbox.value is "both".
converter The converter class used to convert classes between component and the associated bean. null means using the default class conversion method.
Since:
3.0.0
 
 	public void addBinding(Component compString attrString expr,
 		String[] loadWhenEventsString[] saveWhenEventsString accessString converter) {
 		List loadEvents = null;
 		if (loadWhenEvents != null && loadWhenEvents.length > 0) {
 			loadEvents = new ArrayList(loadWhenEvents.length);
 			for (int j = 0; j < loadWhenEvents.length; ++j) {
 				loadEvents.add(loadWhenEvents[j]);
 			}
 		}
 		List saveEvents = null;
 		if (saveWhenEvents != null && saveWhenEvents.length > 0) {
 			saveEvents = new ArrayList(saveWhenEvents.length);
 			for (int j = 0; j < saveWhenEvents.length; ++j) {
 				saveEvents.add(saveWhenEvents[j]);
 			}
 		}
 		addBinding(compattrexprloadEventssaveEventsaccessconverter);
 	}

Binding bean to UI component.

Parameters:
comp The component to be associated.
attr The attribute of the component to be associated.
expr The expression to associate the data bean.
loadWhenEvents The event list when to load data.
saveWhenEvent The event when to save data.
access In the view of UI component: "load" load only, "both" load/save, "save" save only when doing data binding. null means using the default access natural of the component. e.g. Label.value is "load", but Textbox.value is "both".
converter The converter class used to convert classes between component and the associated bean. null means using the default class conversion method.
 
 	public void addBinding(Component compString attrString expr,
 		List loadWhenEventsString saveWhenEventString accessString converter) {
 		List saveEvents = new ArrayList(1);
 		saveEvents.add(saveWhenEvent);
 		addBinding(compattrexprloadWhenEventssaveEventsaccessconverter);
 	}

Binding bean to UI component.

Parameters:
comp The component to be associated.
attr The attribute of the component to be associated.
expr The expression to associate the data bean.
loadWhenEvents The event list when to load data.
saveWhenEvents The event list when to save data.
access In the view of UI component: "load" load only, "both" load/save, "save" save only when doing data binding. null means using the default access natural of the component. e.g. Label.value is "load", but Textbox.value is "both".
converter The converter class used to convert classes between component and the associated bean. null means using the default class conversion method.
Since:
3.0.0
 
 	public void addBinding(Component compString attrString expr,
 		List loadWhenEventsList saveWhenEventsString accessString converter) {
 			
 		//Since 2.5, 20070726, Henri Chen: we accept "each" to replace "_var" in collection data binding
 		//Before 2.4.1
 		//<a:bind _var="person">
 		//<listitem...>
 		//After 2.5
 		//<listitem self="@{bind(each='person')}"...>
 		//or <listitem self="@{each='person'}"...>
 		if ("each".equals(attr)) {
 			attr = "_var";
 		}
 			
 		if (isDefaultConfig()) { //use default binding configuration
 			//handle default-bind defined in lang-addon.xml
 			Object[] objs = loadPropertyAnnotation(compattr"default-bind");
 			
 			/* logically impossible to hold "expr" in default binding 
 			if (expr == null && objs[0] != null) {
 				expr = (String) objs[0];
 			}
 			*/
 
 			if (loadWhenEvents == null && objs[1] != null) {
 				loadWhenEvents = (Listobjs[1];
 			}
 			if (saveWhenEvents == null && objs[2] != null) {
 				saveWhenEvents = (Listobjs[2];
 			}
 			if (access == null && objs[3] != null) {
 				access = (Stringobjs[3];
 			}
 			if (converter == null && objs[4] != null) {
 				converter = (Stringobjs[4];
 			}
 		}
 	
 		//nullify check
 		boolean nullify = false;
 		LinkedHashSet loadEvents = null;
 		if (loadWhenEvents != null && loadWhenEvents.size() > 0) {
 			loadEvents = new LinkedHashSet(loadWhenEvents.size());
 			for(final Iterator it = loadWhenEvents.iterator(); it.hasNext();) {
 				final String event = (Stringit.next();
 				if (.equals(event)) {
 					loadEvents.clear();
 					nullify = true;
 				} else {
 					nullify = false;
 					loadEvents.add(event);
 				}
 			}
 			if (loadEvents.isEmpty()) { 
 				loadEvents = null;
 			}
 		}
 
 		nullify = false;
 		Set saveEvents = null;
 		if (saveWhenEvents != null && saveWhenEvents.size() > 0) {
 			saveEvents = new HashSet(saveWhenEvents.size());
 			for(final Iterator it = saveWhenEvents.iterator(); it.hasNext();) {
 				final String event = (Stringit.next();
 				if (.equals(event)) {
 					saveEvents.clear();
 					nullify = true;
 				} else {
 					nullify = false;
 					saveEvents.add(event);
 				}
 			}
 			if (saveEvents.isEmpty()) { 
 				saveEvents = null;
 			}
 		}
 
 		if (.equals(access)) {
 			access = null;
 		}
 		
 		if (.equals(converter)) {
 			converter = null;
 		}
 		
 		Map attrMap = (Map.get(comp);
 		if (attrMap == null) {
 			attrMap = new LinkedHashMap(3);
 			.put(compattrMap);
 		}
 			
 		if (attrMap.containsKey(attr)) { //override
 			final Binding binding = (BindingattrMap.get(attr);
 			binding.setExpression(expr);
 			binding.setLoadWhenEvents(loadEvents);
 			binding.setSaveWhenEvents(saveEvents);
 			binding.setAccess(access);
 			binding.setConverter(converter);
 		} else {
 			attrMap.put(attrnew Binding(thiscompattrexprloadEventssaveEventsaccessconverter));
 		}
 	}

Remove the binding associated with the attribute of the component.

Parameters:
comp The component to be removed the data binding association.
attr The attribute of the component to be removed the data binding association.
 
 	public void removeBinding(Component compString attr) {
 		Map attrMap = (Map.get(comp);
 		if (attrMap != null) {
 			attrMap.remove(attr);
 		}
 	}

Given component and attr, return the associated Binding.

Parameters:
comp the concerned component
attr the concerned attribute
 
 	public Binding getBinding(Component compString attr) {
 		if (isClone(comp)) {
 			comp = (Componentcomp.getAttribute();
 		}
 		Map attrMap = (Map.get(comp);
 		return attrMap != null ?  (BindingattrMap.get(attr) : null;
 	}

Given component, return the associated list of Bindings.

Parameters:
comp the concerned component
 
 	public Collection getBindings(Component comp) {
 		if (isClone(comp)) {
 			comp = (Componentcomp.getAttribute();
 		}
 		Map attrMap = (Map).get(comp);
 		return attrMap != null ?  (CollectionattrMap.values() : null;
 	}

Whether this component associated with any bindings.
 
 	public boolean existsBindings(Component comp) {
 		if (isClone(comp)) {
 			comp = (Componentcomp.getAttribute();
 		}
 	}

Whether this component and attribute associated with a binding.
 
 	public boolean existBinding(Component compString attr) {
 		if (isClone(comp)) {
 			comp = (Componentcomp.getAttribute();
 		}
 			Map attrMap = (Map.get(comp);
 			return attrMap.containsKey(attr);
 		}
 		return false;
 	}		

Whether use the default binding configuration.
 
 	public boolean isDefaultConfig(){
 		return ;
 	}

Whether use the default binding configuration.
 
 	public void setDefaultConfig(boolean b) {
 	}

Bind a real bean object to the specified beanid. You might not need to call this method because this DataBinder would look up the variable via the org.zkoss.zk.ui.Component.getVariable method if it cannot find the specified bean via the given beanid.

Parameters:
beanid The bean id used in data binding.
bean The real bean object to be associated with the bean id.
 
 	public void bindBean(String beanidObject bean) {
 		.put(beanidbean);
 	}


Load value from the data bean property to a specified attribute of the UI component.

Parameters:
comp the UI component to be loaded value.
attr the UI component attribute to be loaded value.
 
 	public void loadAttribute(Component compString attr) {
 		if (isTemplate(comp) || comp.getPage() == null) {
 			return//skip detached component
 		}
 		init();
 		Binding binding = getBinding(compattr);
 		if (binding != null) {
 			binding.loadAttribute(comp);
 		}
 	}			

Save value from a specified attribute of the UI component to a data bean property.

Parameters:
comp the UI component used to save value into backend data bean.
attr the UI component attribute used to save value into backend data bean.
 
 	public void saveAttribute(Component compString attr) {
 		if (isTemplate(comp) || comp.getPage() == null) {
 			return//skip detached component
 		}
 		init();
 		Binding binding = getBinding(compattr);
 		if (binding != null) {
 			binding.saveAttribute(comp);
 		}
 	}

Load values from the data bean properties to all attributes of a specified UI component.

Parameters:
comp the UI component to be loaded value.
 
 	public void loadComponent(Component comp) {
 		if (isTemplate(comp) || comp.getPage() == null) {
 			return//skip detached component
 		}
 		init();
 		Collection bindings = getBindings(comp);
 		if (bindings != null) {
 			loadAttrs(compbindings);
 		}
 			
 		//load kids of this component
 		for(final Iterator it = comp.getChildren().iterator(); it.hasNext();) {
 			loadComponent((Componentit.next()); //recursive
 		}
 	}

Save values from all attributes of a specified UI component to data bean properties.

Parameters:
comp the UI component used to save value into backend data bean.
 
 	public void saveComponent(Component comp) {
 		if (isTemplate(comp) || comp.getPage() == null) {
 			return//skip detached component
 		}
 		init();
 		Collection bindings = getBindings(comp);
 		if (bindings != null) {
 			saveAttrs(compbindings);
 		}
 
 		//save kids of this component
 		for(final Iterator it = comp.getChildren().iterator(); it.hasNext();) {
 			saveComponent((Componentit.next()); //recursive
 		}
 	}

Load all value from data beans to UI components.
 
 	public void loadAll() {
 		init();
 		for (final Iterator it = .keySet().iterator(); it.hasNext(); ) {
 			final Component comp = (Componentit.next();
 		}
 	}

Save all values from UI components to beans.
 
 	public void saveAll() {
 		init();
 		for (final Iterator it = .keySet().iterator(); it.hasNext(); ) {
 			final Component comp = (Componentit.next();
 		}
 	}
 
 	private void loadAttrs(String exprCollection attrs) {
 		for(final Iterator it = attrs.iterator(); it.hasNext();) {
 			Binding binding = (Bindingit.next();
 			Component comp = binding.getComponent();
 			binding.loadAttribute(compexpr);
 		}
 	}	
 
 	private void loadAttrs(Component compCollection attrs) {
 		for(final Iterator it = attrs.iterator(); it.hasNext();) {
 			Binding binding = (Bindingit.next();
 			binding.loadAttribute(comp);
 		}
 	}	
 
 	private void saveAttrs(Component compCollection attrs) {
 		for(final Iterator it = attrs.iterator(); it.hasNext();) {
 			Binding binding = (Bindingit.next();
 			binding.saveAttribute(comp);
 		}
 	}
 
 	//[0] expr, [1] loadWhenEvents, [2] saveWhenEvents, [3] access, [4] converter
 	protected Object[] loadPropertyAnnotation(Component compString propNameString bindName) {
 		ComponentCtrl compCtrl = (ComponentCtrlcomp;
 		Annotation ann = compCtrl.getAnnotation(propNamebindName);
 		if (ann != null) {
 			final Map attrs = ann.getAttributes(); //(tag, tagExpr)
 			List loadWhenEvents = null;
 			List saveWhenEvents = null;
 			String access = null;
 			String converter = null;
 			String expr = null;
 			for (final Iterator it = attrs.entrySet().iterator(); it.hasNext();) {
 				Map.Entry entry = (Map.Entryit.next();
 				String tag = (Stringentry.getKey();
 				String tagExpr = (Stringentry.getValue();
 				if ("save-when".equals(tag)) {
 					saveWhenEvents = parseExpression(tagExpr",");
 				} else if ("access".equals(tag)) {
 					access = tagExpr;
 				} else if ("converter".equals(tag)) {
 					converter = tagExpr;
 				} else if ("load-when".equals(tag)) {
 					loadWhenEvents = parseExpression(tagExpr",");
 				} else if ("value".equals(tag)) {
 					expr = tagExpr;
 				}
 			}
 			return new Object[] {exprloadWhenEventssaveWhenEventsaccessconverter};
 		}
 		return new Object[5];
 	}
 	
 	//late init
 	protected void init() {
 		if (!) {
 			 = true;
 
 			// init CollectionItem
 			
 			//setup all added bindings
 			final Set varnameSet = new HashSet();
 			final LinkedHashSet toBeDetached = new LinkedHashSet();
 			for(final Iterator it = .entrySet().iterator(); it.hasNext(); ) {
 				final Entry me = (Entryit.next();
 				final Component comp = (Componentme.getKey();
 				final Map attrMap = (Mapme.getValue();
 				final Collection bindings = attrMap.values();
 				
 				//_var special case; meaning a template component
 				if (attrMap.containsKey("_var")) {
 					setupTemplateComponent(compgetComponentCollectionOwner(comp)); //setup as template components
 					String varname = ((Binding)attrMap.get("_var")).getExpression();
 					varnameSet.add(varname);
 					comp.setAttribute(varname);
 					setupBindingRenderer(comp); //setup binding renderer
 					toBeDetached.add(comp);
 				}
 
 				if (bindings != null) {
 					//construct the path dependant tree
 					setupPathTree(bindingsvarnameSet);
 				
 					//register save-when event
 					registerSaveEvents(compbindings);
 					
 					//register load-when events
 					registerLoadEvents(compbindings);
 				}
 			}
             
 			//detach template components so they will not interfer the visual part
 			for(final Iterator it = toBeDetached.iterator(); it.hasNext(); ) {
 				final Component comp = (Componentit.next();
 				comp.detach();
 			}
 		}
 	}
 	
 	private void initCollectionItem(){
 		addCollectionItem(Listitem.class.getName(), new ListitemCollectionItem());
 	}

Adds a CollectionItem for this comp.

Since:
3.0.0
See also:
CollectionItem
 
 	public void addCollectionItem(String compCollectionItem decor){
 		.put(compdecor);
 	}
 	
 	//get Collection owner of a given collection item.
 		return decor.getComponentCollectionOwner(comp);		
 	}
Returns a CollectionItem by the comp accordingly.

Since:
3.0.0
See also:
CollectionItem
 
 		String name = comp.getClass().getName();
 		if (comp instanceof Listitem) {
 			name = Listitem.class.getName();
 		} else if (comp instanceof Row) {
 			name = Row.class.getName();
 		}
 		if(decorName != null){
 			return decorName;
 		}else{
 			throw new UiException("Cannot find associated CollectionItem:"+comp);
 		}		
 	}
 	//get Collection owner of a given collection item.
 		if (isTemplate(comp)) {
 			return (Componentcomp.getAttribute();
 		}
 	}
 	
 	//get associated clone of a given bean and template component
 	private Component getCollectionItem(Component compObject bean) {
 		Component owner = getCollectionOwner(comp);	
 		final ListModel xmodel = decor.getModelByOwner(owner);
 		if (xmodel instanceof BindingListModel) {
   			final BindingListModel model = (BindingListModelxmodel;
   			int index = model.indexOf(bean);
   			if (index >= 0) {
     			return lookupClone(decor.getComponentAtIndexByOwner(ownerindex), comp);
     		}
     	}
 		return null;
 	}
   		
 	//set the binding renderer for the template listitem component
 	private void setupBindingRenderer(Component comp) {
 	}
 	
 	private void setupPathTree(Collection bindingsSet varnameSet) {
 		for(final Iterator it = bindings.iterator(); it.hasNext(); ) {
 			final Binding binding = (Bindingit.next();
 			String[] paths = binding.getPaths();
 			for(int j = 0; j < paths.length; ++j) {
 				final String path = (Stringpaths[j];
 				.addBinding(pathbindingvarnameSet);
 			}
 		}
 	}
 	
 	private void registerSaveEvents(Component compCollection bindings) {
 		for(final Iterator it = bindings.iterator(); it.hasNext(); ) {
 			final Binding binding = (Bindingit.next();
 			binding.registerSaveEvents(comp);
 		}
 	}
 
 	private void registerLoadEvents(Component compCollection bindings) {
 		for(final Iterator it = bindings.iterator(); it.hasNext(); ) {
 			final Binding binding = (Bindingit.next();
 			binding.registerLoadEvents(comp);
 		}
 	}
 
 	
 	//whether exists the specified bean in this DataBinder.
 	/* package */ boolean existsBean(String beanid) {
 		return .containsKey(beanid);
 	}
 
 	//get a bean by the beanid from this Data binder
 	/* package */ Object getBean(String beanid) {
 		return .get(beanid);
 	}
 
 	//set a bean into this Data binder
 	/* package */ void setBean(String beanidObject bean) {
 		.put(beanidbean);
 	}

Sets up the specified comp and its decendents to be as template (or not)
 
 	public void setupTemplateComponent(Component compObject owner) {
 		if (existsBindings(comp)) {
 			if (comp.getAttribute() != null) {
 				comp.setAttribute(.); //owner is a template
 			}
 			comp.setAttribute(owner);
 		}
 		List kids = comp.getChildren();
 		for(final Iterator it = kids.iterator(); it.hasNext(); ) {
 			setupTemplateComponent((Componentit.next(), owner); //recursive
 		}
 	}
 	
 	//parse token and return as a List of String
 	/* package */ static List parseExpression(String exprString separator) {
 		if (expr == null) {
 			return null;
 		}
 		List results = new ArrayList(5);
 		while(true) {
 			int j = expr.indexOf(separator);
 			if (j < 0) {
 				results.add(expr.trim());
 				return results;
 			}
 			results.add(expr.substring(0, j).trim());
 
 			if (expr.length() <= (j+1)) {
 				return results;
 			}
 			expr = expr.substring(j+1);
 		}
 	}
 
 	//whether a component is a binding template rather than a real component
 	/* package */ static boolean isTemplate(Component comp) {
 		return comp.getAttribute() != null;
 	}
 	
 	//whether a cloned component from the template.
 	/* package */ static boolean isClone(Component comp) {
 		//bug #1813055  Multiple listboxes with same selectedItem causes NPE
 		return comp != null && (comp.getAttribute(instanceof Component);
 	}
 	
 	//whether has template owner (collection in collection)
 	/* package */ static boolean hasTemplateOwner(Component comp) {
 		//bug #1813055  Multiple listboxes with same selectedItem causes NPE
 		return comp != null && (comp.getAttribute() != null);
 	}
 	
 	//set a bean to SameNode Set 
 	/* package */ void setBeanSameNodes(Object beanSet set) {
 		.put(beanset);
 	}
 	
 	//get SameNode Set of the given bean
 	/* package */ Set getBeanSameNodes(Object bean) {
 		return (Set.get(bean);
 	}
 	
 	//remove SameNode set of the given bean
 	/* package */ Set removeBeanSameNodes(Object bean) {
 		return (Set.remove(bean);
 	}

traverse the path nodes and return the final bean.
 
 	/* package */ Object getBeanAndRegisterBeanSameNodes(Component compString path) {
 		return myGetBeanWithExpression(comppathtrue);
 	}
 	
 	private Object getBeanWithExpression(Component compString path) {
 		return myGetBeanWithExpression(comppathfalse);
 	}
 	
 	private Object myGetBeanWithExpression(Component compString pathboolean registerNode) {
 		Object bean = null;
 		BindingNode currentNode = ;
 		final List nodeids = parseExpression(path".");
 		final Iterator it = nodeids.iterator();
 		if (it != null && it.hasNext()) {
 			String nodeid = (Stringit.next();
 			currentNode = (BindingNodecurrentNode.getKidNode(nodeid);
 			if (currentNode == null) {
 				throw new UiException("Cannot find the specified databind bean expression:" + path);
 			}
 			bean = lookupBean(compnodeid);
 			if (registerNode) {
 				registerBeanNode(beancurrentNode);
 			}
 		} else {
 			throw new UiException("Incorrect format of databind bean expression:" + path);
 		}
 		
 		while(bean != null && it.hasNext()) {
 			String nodeid = (Stringit.next();
 			currentNode = (BindingNodecurrentNode.getKidNode(nodeid);
 			if (currentNode == null) {
 				throw new UiException("Cannot find the specified databind bean expression:" + path);
 			}
 			try {
 				bean = Fields.get(beannodeid);
 			} catch (NoSuchMethodException ex) {
 				throw UiException.Aide.wrap(ex);
 			}
 			if (registerNode) {
 				registerBeanNode(beancurrentNode);
 			}
 		}
 
 		return bean;
 	}
 	
 	/* package */ void setBeanAndRegisterBeanSameNodes(Component compObject valBinding binding
 	String pathboolean autoConvertObject rawvalList loadOnSaveInfos) {
 		Object orgVal = null;
 		Object bean = null;
 		BindingNode currentNode = ;
 		boolean refChanged = false//wether this setting change the reference
 		String beanid = null;
 		final List nodeids = parseExpression(path".");
 		final List nodes = new ArrayList(nodeids.size());
 		final Iterator it = nodeids.iterator();
 		if (it != null && it.hasNext()) {
 			beanid = (Stringit.next();
 			currentNode = (BindingNodecurrentNode.getKidNode(beanid);
 			if (currentNode == null) {
 				throw new UiException("Cannot find the specified databind bean expression:" + path);
 			}
 			nodes.add(currentNode);
 			bean = lookupBean(compbeanid);
 		} else {
 			throw new UiException("Incorrect format of databind bean expression:" + path);
 		}
 		
 		if (!it.hasNext()) { //assign back to where bean is stored
 			orgVal = bean;
 			if(Objects.equals(orgValval)) {
 				return//same value, no need to do anything
 			}
 			if (existsBean(beanid)) {
 				setBean(beanidval);
 			} else if (!setZScriptVariable(compbeanidval)) {
 				comp.setVariable(beanidvalfalse);
 			}
 			refChanged = true;
 		} else {
 			if (bean == null) {
 				return//no bean to set value, skip
 			}
 			int sz = nodeids.size() - 2; //minus first and last beanid in path
 			for(;bean != null && it.hasNext() && sz > 0; --sz) {
 				beanid = (Stringit.next();
 				currentNode = (BindingNodecurrentNode.getKidNode(beanid);
 				if (currentNode == null) {
 					throw new UiException("Cannot find the specified databind bean expression:" + path);
 				}
 				nodes.add(currentNode);
 				try {
 					bean = Fields.get(beanbeanid);
 				} catch (NoSuchMethodException ex) {
 					throw UiException.Aide.wrap(ex);
 				}
 			}
 			if (bean == null) {
 				return//no bean to set value, skip
 			}
 			try {
 				beanid = (Stringit.next();
 				orgVal = Fields.get(beanbeanid);
 				if(Objects.equals(orgValval)) {
 					return//same value, no need to do anything
 				}
 				Fields.set(beanbeanidvalautoConvert);
 				if (!isPrimitive(val) && !isPrimitive(orgVal)) { //val is a bean (null is not primitive)
 					currentNode = (BindingNodecurrentNode.getKidNode(beanid);
 					if (currentNode == null) {
 						throw new UiException("Cannot find the specified databind bean expression:" + path);
 					}
 					nodes.add(currentNode);
 					bean = orgVal;
 					refChanged = true;
 				}
 			} catch (NoSuchMethodException ex) {
 				throw UiException.Aide.wrap(ex);
 			} catch (ModificationException ex) {
 				throw UiException.Aide.wrap(ex);
 			}
 		}
 		
 		if (val != null) {
 			if (refChanged && !binding.isLoadable() && binding.isSavable()) { //the sameNodes should change accordingly.
 				registerBeanNode(valcurrentNode);
 			}
 			//20070309, Henri Chen: Tricky. 
 			//When loading page, listbox.selectedItem == null. The _var Listitem will not be able to 
 			//associate with the selectedItem (no way to associate via null bean). When end user then 
 			//select one Listitem, we have to force such association.
 			if (rawval instanceof Component) {
 				Binding varbinding = getBinding((Componentrawval"_var");
 				if (varbinding != null) {
 					registerBeanNode(valcurrentNode);
 				}
 			}
 		}
 			
 		//TODO:Henri Chen: Is it possible to make the loadOnSave event to be called once only for a
 		//setXxx. So avoid load a node several times?
 			
 		//register "onLoadSave" listener to this component if have not done so.
 		if (!comp.isListenerAvailable("onLoadOnSave"true)) {
 			comp.addEventListener("onLoadOnSave");
 		}
 
 		Object[] loadOnSaveInfo = 
 			new Object[] {thiscurrentNodebinding, (refChanged ? val : bean), Boolean.valueOf(refChanged), nodescomp};
 		if (loadOnSaveInfos != null) {
 			loadOnSaveInfos.add(loadOnSaveInfo);
 		} else {
 			//do loadOnSave immediately
 			Events.postEvent(new Event("onLoadOnSave"comploadOnSaveInfo));
 		}
 	}
 	
 	private void registerBeanNode(Object beanBindingNode node) {
 		if (isPrimitive(bean)) {
 			return;
 		}
 		final Set nodeSameNodes = node.getSameNodes();
 		final Set binderSameNodes = getBeanSameNodes(bean);
 		//variable node(with _var) is special. Assume selectedItem then _var. 
 		//e.g. a Listitem but no selectedItem yet
 		if (node.isVar() && binderSameNodes == null) {
 			return;
 		}
 		
 		if (!nodeSameNodes.contains(bean)) {
 			//remove the old bean
 			for(final Iterator it = nodeSameNodes.iterator(); it.hasNext();) {
 				final Object elm = it.next();
 				if (!(elm instanceof BindingNode)) {
 					it.remove();
 					removeBeanSameNodes(elm); //remove the binderSameNodes of the original bean
 					break;
 				}
 			}
 			//add the new bean if not null
 			if (bean != null) {
 				nodeSameNodes.add(bean);
 			}
 		}
 
 		if (binderSameNodes == null) {
 			if (bean != null) {
 				setBeanSameNodes(beannodeSameNodes);
 			}
 		} else {
 			node.mergeAndSetSameNodes(binderSameNodes);
 		}
 		
 	}
 
 	private boolean isPrimitive(Object bean) {
 		//String is deemed as primitive and null is not primitive
 		return (bean instanceof String
 			|| (bean != null && Primitives.toPrimitive(bean.getClass()) != null)
 			|| (bean instanceof Date)
 			|| (bean instanceof Number);
 	}

Sets the variable to all loaded interpreters, if it was defined in the interpreter.

Returns:
whether it is set to the interpreter
 
 	private boolean setZScriptVariable(Component compString beanidObject val) {
 		//for all loaded interperter, assign val to beanid
 		boolean found = false;
 		final Namespace ns = comp.getNamespace();
 		for(final Iterator it = comp.getPage().getLoadedInterpreters().iterator();
 		it.hasNext();) {
 			final Interpreter ip = (Interpreterit.next();
 			if (ip instanceof HierachicalAware) {
 				final HierachicalAware ha = (HierachicalAware)ip;
 				if (ha.containsVariable(nsbeanid)) {
 					ha.setVariable(nsbeanidval);
 					found = true;
 				}
 			} else if (ip.containsVariable(beanid)) {
 				ip.setVariable(beanidval);
 				found = true;
 			}
 		}
 		return found;
 	}
 
 	/*package*/ Object lookupBean(Component compString beanid) {
 		//fetch the bean object
 		Object bean = null;
 		if (isClone(comp)) {
 			bean = myLookupBean1(compbeanid);
 			if (bean != ) {
 				return bean;
 			}
 		}
 		if (existsBean(beanid)) {
 			bean = getBean(beanid);
 		} else if (beanid.startsWith("/")) { //a absolute component Path: // or /
 			bean = Path.getComponent(beanid);
 		} else if (beanid.startsWith(".")) { //a relative component Path: ./ or ../
 			bean = Path.getComponent(comp.getSpaceOwner(), beanid);
 		} else {
 			final Page page = comp.getPage();
 			if (page != null)
 				bean = page.getZScriptVariable(comp.getNamespace(), beanid);
 			if (bean == null)
 				bean = comp.getVariable(beanidfalse);
 		}
 		return bean;
 	}
 
 	//given a beanid and a template, return the associated bean
 	//return NA if cannot find it
 	private Object myLookupBean1(Component compString beanid) {
 		Map templatemap = (Mapcomp.getAttribute();
 		return myLookupBean2(beanidtemplatemap);
 	}
 	private Object myLookupBean2(String beanidMap templatemap) {
 		if (templatemap != null) {
 			if (templatemap.containsKey(beanid)) { //got it
 				return templatemap.get(beanid);
 			} else { //search up the parent templatemap
 				templatemap = (Maptemplatemap.get();
 				return myLookupBean2(beanidtemplatemap); //recursive
 			}
		return //not available
	//given a clone and a template, return the associated clone of that template.
	/*package*/ static Component lookupClone(Component srcCloneComponent srcTemplate) {
		if (isTemplate(srcTemplate)) {
			Map templatemap = (MapsrcClone.getAttribute();
			return myLookupClone(srcTemplatetemplatemap);
		return null;
	private static Component myLookupClone(Component srcTemplateMap templatemap) {
		if (templatemap != null) {
			if (templatemap.containsKey(srcTemplate)) { //got it
				return (Componenttemplatemap.get(srcTemplate);
else { //search up the parent templatemap
				templatemap = (Maptemplatemap.get();
				return myLookupClone(srcTemplatetemplatemap); //recursive
		return null;
	// Given parentNode, path, and level, return associate same kid nodes of parent
	// a1.b.c -> a2.b.c, a3.b.c, ...
	private Set getAssociateSameNodes(BindingNode parentNodeString pathint level) {
		final List nodeids = DataBinder.parseExpression(path".");
		final int sz = nodeids.size();
		final List subids = nodeids.subList(sz - levelsz);
		Object bean = null;
		for (final Iterator it = parentNode.getSameNodes().iterator(); it.hasNext();) {
			Object obj = it.next();
			if (!(obj instanceof BindingNode)) {
				bean = obj;
				break;
		//for each same node, find the associated kid node
		final Set assocateSameNodes = new HashSet();
		for (final Iterator it = parentNode.getSameNodes().iterator(); it.hasNext();) {
			//locate the associate kid node
			BindingNode currentNode = null;
			final Object obj = it.next();
			if (!(obj instanceof BindingNode) || currentNode == parentNode) {
				continue;
			currentNode = (BindingNodeobj;
			for(final Iterator itx = subids.iterator(); itx.hasNext();) {
				final String nodeid = (Stringitx.next();
				currentNode = (BindingNodecurrentNode.getKidNode(nodeid);
				if (currentNode == null) {
					break;
			if (currentNode != null) {
				if (!currentNode.isVar()) {
					assocateSameNodes.add(currentNode);
else { //a var node, specialcase, find the var root
					Component varRootComp = getVarRootComponent(currentNode);
					assocateSameNodes.add(new Object[] {currentNodevarRootComp});
		return assocateSameNodes;
		final BindingNode varRootNode = node.getRootNode();
		Object bean = null;
		for (final Iterator it = varRootNode.getSameNodes().iterator(); it.hasNext();) {
			Object obj = it.next();
			if (!(obj instanceof BindingNode)) {
				bean = obj;
				break;
		Component comp = null;
		for(final Iterator itx = varRootNode.getBindings().iterator(); itx.hasNext();) {
			Binding binding = (Bindingitx.next();
			if ("_var".equals(binding.getAttr())) {
				comp = binding.getComponent();
				break;
		return getCollectionItem(compbean);		
	private class LoadOnSaveEventListener implements EventListener {
		//-- EventListener --//
		public void onEvent(Event event) {
			final Set walkedNodes = new HashSet(32);
			final Set loadedBindings = new HashSet(32*2);
			Object obj = event.getData();
			if (obj instanceof List) {
				for(final Iterator it = ((List)obj).iterator(); it.hasNext();) {
					final Object[] data = (Object[]) it.next();
					doLoad(datawalkedNodesloadedBindings);
else {
				doLoad((Object[]) objwalkedNodesloadedBindings);
		private void doLoad(Object[] dataSet walkedNodesSet loadedBindings) {
			if (!data[0].equals(DataBinder.this)) {
				return//not for this DataBinder, skip
			final BindingNode node = (BindingNodedata[1]; //to be loaded nodes
			final Binding savebinding = (Bindingdata[2]; //to be excluded binding
			final Object bean = data[3]; //saved bean
			final boolean refChanged = ((Booleandata[4]).booleanValue(); //whether bean itself changed
			final List nodes = (Listdata[5]; //the complete nodes along the path to the node
			final Component savecomp = (Componentdata[6]; //saved comp that trigger this load-on-save event
			if (savecomp != null) {
				loadAllNodes(beannodesavecompsavebindingrefChangednodeswalkedNodesloadedBindings);
		}

Load all associated BindingNodes below the given nodes (depth first traverse).
		private void loadAllNodes(Object beanBindingNode nodeComponent collectionComp
			Binding savebindingboolean refChangedList nodesSet walkedNodesSet loadedBindings) {
			myLoadAllNodes(beannodecollectionCompwalkedNodessavebindingloadedBindingsrefChanged);
			//for each ancestor, find associated same nodes			
			if (!nodes.isEmpty()) {
				final String path = node.getPath();
				int level = 1;
				for(final ListIterator it = nodes.listIterator(nodes.size()-1); it.hasPrevious(); ++level) {
					final BindingNode parentNode = (BindingNodeit.previous();
					final Set associateSameNodes = getAssociateSameNodes(parentNodepathlevel);
					for(final Iterator itx = associateSameNodes.iterator(); itx.hasNext();) {
						Object obj = itx.next();
						if (obj instanceof BindingNode) {
							BindingNode samenode = (BindingNodeobj;
							myLoadAllNodes(beansamenodecollectionCompwalkedNodessavebindingloadedBindingsrefChanged);
else {
							BindingNode samenode = (BindingNode)((Object[])obj)[0];
							Component varRootComp = (Component) ((Object[])obj)[1];
							myLoadAllNodes(beansamenodevarRootCompwalkedNodessavebindingloadedBindingsrefChanged);
		private void myLoadAllNodes(Object beanBindingNode nodeComponent collectionComp,
		Set walkedNodesBinding savebindingSet loadedBindingsboolean refChanged) {
			if (walkedNodes.contains(node)) {
				return//already walked, skip
			//mark as walked already
			walkedNodes.add(node);
			//the component might have been removed
			if (collectionComp == null) {
				return;
			//loading
			collectionComp = loadBindings(beannodecollectionCompsavebindingloadedBindingsrefChanged);
			for(final Iterator it = node.getKidNodes().iterator(); it.hasNext();) {
				final