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 java.util.Date;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import  org.zkoss.zul.Combobox;
  import  org.zkoss.zul.Comboitem;
  import  org.zkoss.zul.Grid;
  import  org.zkoss.zul.ListModel;
  import  org.zkoss.zul.Listbox;
  import  org.zkoss.zul.Listitem;
  import  org.zkoss.zul.Row;

The DataBinder used for binding ZK UI component and the backend data bean.

Author(s):
Henri Chen
  
  public class DataBinder implements java.io.Serializable {
  	private static final long serialVersionUID = 200808191508L;
  	public static final String NULLIFY = "none"//used to nullify default configuration
  	public static final String ARGS = "bindingArgs"//extra arguments specified in annotation
  	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 ITEM = "zkplus.databind.ITEM"//the collection item of the template component
  	private static final String IAMOWNER = "zkplus.databind.IAMOWNER"//I am the collection owner
  	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);
  	protected Map _collectionOwnerMap = new HashMap(3); //bug#1950313 F - 1764967 bug
  	
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) {
 		addBinding(compattrexprloadWhenEventssaveWhenEventsaccessconverternullnullnull);
 	}

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.
args generic argument map for each binding.
Since:
3.5.0
 
 /*	public void addBinding(Component comp, String attr, String expr,
 			List loadWhenEvents, List saveWhenEvents, String access, String converter, Map args) {
 		addBinding(comp, attr, expr, loadWhenEvents, saveWhenEvents, access, converter, args, null, null);
 	}
 */
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.
args generic argument map for each binding.
loadAfterEvents the event list when to load data after.
saveAfterEvents the event list when to save data after.
Since:
3.6.1
 
 	public void addBinding(Component compString attrString expr,
 		List loadWhenEventsList saveWhenEventsString accessString converterMap argsList loadAfterEventsList saveAfterEvents) {
 		//since 3.1, 20080416, Henri Chen: add a generic arguments map (string, string)
 		
 		//Since 3.0, 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];
 			}
 			if (args == null && objs[5] != null) {
 				args = (Mapobjs[5];
 			}
 			if (loadAfterEvents == null && objs[6] != null) {
 				loadAfterEvents = (Listobjs[6];
 			}
 			if (saveAfterEvents == null && objs[7] != null) {
 				saveAfterEvents = (Listobjs[7];
 			}
 		}
 	
 		//nullify check
 		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();
 				} else {
 					loadEvents.add(event);
 				}
 			}
 			if (loadEvents.isEmpty()) { 
 				loadEvents = null;
 			}
 		}
 
 		LinkedHashSet lafterEvents = null;
 		if (loadAfterEvents != null && loadAfterEvents.size() > 0) {
 			lafterEvents = new LinkedHashSet(loadAfterEvents.size());
 			for(final Iterator it = loadAfterEvents.iterator(); it.hasNext();) {
 				final String event = (Stringit.next();
 				if (.equals(event)) {
 					lafterEvents.clear();
 				} else {
 					lafterEvents.add(event);
 				}
 			}
 			if (lafterEvents.isEmpty()) { 
 				lafterEvents = null;
 			}
 		}
 		
 		LinkedHashSet saveEvents = null;
 		if (saveWhenEvents != null && saveWhenEvents.size() > 0) {
 			saveEvents = new LinkedHashSet(saveWhenEvents.size());
 			for(final Iterator it = saveWhenEvents.iterator(); it.hasNext();) {
 				final String event = (Stringit.next();
 				if (.equals(event)) {
 					saveEvents.clear();
 				} else {
 					saveEvents.add(event);
 				}
 			}
 			if (saveEvents.isEmpty()) { 
 				saveEvents = null;
 			}
 		}
 		
 		LinkedHashSet safterEvents = null;
 		if (saveAfterEvents != null && saveAfterEvents.size() > 0) {
 			safterEvents = new LinkedHashSet(saveAfterEvents.size());
 			for(final Iterator it = saveAfterEvents.iterator(); it.hasNext();) {
 				final String event = (Stringit.next();
 				if (.equals(event)) {
 					safterEvents.clear();
 				} else {
 					safterEvents.add(event);
 				}
 			}
 			if (safterEvents.isEmpty()) { 
 				safterEvents = null;
 			}
 		}
 
 //bug 2129730. Comment out the following to solve this bug
 //		if (NULLIFY.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.setLoadAfterEvents(lafterEvents);
 			binding.setSaveWhenEvents(saveEvents);
 			binding.setSaveAfterEvents(safterEvents);
 			binding.setAccess(access);
 			binding.setConverter(converter);
 		} else {
 			attrMap.put(attrnew Binding(thiscompattrexprloadEventssaveEventsaccessconverterargslafterEventssafterEvents));
 		}
 	}

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;
 	}

Return all Bindings covered by this DataBinder

Returns:
all Bindings covered by this DataBinder.
Since:
3.5.2
 
 	public Collection getAllBindings() {
 		final List bindings = new ArrayList(.size() * 2);
 		for (final Iterator it = .values().iterator(); it.hasNext();) {
 			final Map map = (Mapit.next();
 			bindings.addAll(map.values());
 		}
 		return bindings;
 	}

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) {
 		init();
 		if (loadComponent0(comp)) { //component detached, skip
 			return;
 		}
 			
 		//load kids of this component
 		for(final Iterator it = comp.getChildren().iterator(); it.hasNext();) {
 			loadComponent((Componentit.next()); //recursive
 		}
 	}
 	
 	private boolean loadComponent0(Component comp) {
 		if (isTemplate(comp) || comp.getPage() == null) {
 			return true//skip detached component
 		}
 		final Collection bindings = getBindings(comp);
 		if (bindings != null) {
 			loadAttrs(compbindings);
 		}
 		return false;
 	}

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(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, [5] args, [6] loadAfterEvents, [7] saveAfterEvents
 	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;
 			List loadAfterEvents = null;
 			List saveAfterEvents = null;
 			String access = null;
 			String converter = null;
 			String expr = null;
 			Map args = 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 ("load-after".equals(tag)) {
 					loadAfterEvents = 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 ("save-after".equals(tag)) {
 					saveAfterEvents = parseExpression(tagExpr",");
 				} else if ("value".equals(tag)) {
 					expr = tagExpr;
 				} else {
 					if (args == null) {
 						args = new HashMap();
 					}
 					args.put(tagtagExpr);
 				}
 			}
 			return new Object[] {exprloadWhenEventssaveWhenEventsaccessconverterargsloadAfterEventssaveAfterEvents};
 		}
 		return new Object[8];
 	}
 	
 	//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")) {
 					comp.setAttribute(comp);
 					final Component owner = getComponentCollectionOwner(comp);
 					//bug#1888911 databind and Grid in Grid not work when no _var in inner Grid
 					setupTemplateComponent(compowner); //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, Listbox.classnew ListitemCollectionItem());
 		addCollectionItem(Row.class, Grid.classnew RowCollectionItem());
 		addCollectionItem(Comboitem.class, Combobox.classnew ComboitemCollectionItem());
 	}

This method is deprecated. Use addCollectionItem(Class item, Class owner, CollectionItem) instead.

Adds a CollectionItem for this comp.

Deprecated:
Since:
3.0.0
See also:
CollectionItem
addCollectionItem(Class, Class, CollectionItem)
 
 	public void addCollectionItem(String compCollectionItem decor){
 		.put(compdecor);
 	}

Adds a CollectionItem for the specified item and owner component; e.g. Listitem and Listbox, Row and Grid, Comoboitem and Combobox.

Parameters:
item the item class
owner the owner class
decor the associated CollectionItem decorator
Since:
3.0.5
See also:
CollectionItem
 
 	public void addCollectionItem(Class itemClass ownerCollectionItem decor) {
 		.put(item.getName(), decor);
 		.put(owner.getName(), decor);
 	}
 	
 	//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();
 		} else if (comp instanceof Comboitem) {
 			name = Comboitem.class.getName();
 		}
 		if(decorName != null){
 			return decorName;
 		}else{
 			throw new UiException("Cannot find associated CollectionItem:"+comp);
 		}		
 	}
 	//Get CollectionItem per the given owner.
 	//@since 3.0.4 
 	//@since 3.0.5, bug#1950313 F - 1764967 bug
 		final CollectionItem decorName = myGetCollectionItemByOwner(comp);
 		if (decorName == null) {
 			throw new UiException("Cannot find associated CollectionItem by owner: "+comp);
 		}
 		return decorName;
 	}
 	
 		String name = comp.getClass().getName();
 		if (comp instanceof Listbox) {
 			name = Listbox.class.getName();
 		} else if (comp instanceof Grid) {
 			name = Grid.class.getName();
 		} else if (comp instanceof Combobox) {
 			name = Combobox.class.getName();
 		}
 		return decorName;
 	}
 	
 	//get Collection owner of a given collection item.
 	/*package*/ Component getCollectionOwner(Component comp) {
 		if (isTemplate(comp)) {
 			return (Componentcomp.getAttribute();
 		}
 	}
 
 	//since 3.1
 	//get associated clone of a given bean and template component
 	private Component getCollectionItem(Component compObject beanboolean isCollectionItem) {
 		final Component[] comps = getCollectionItems(compbeanisCollectionItem);
 		
 		return comps.length == 0 ? null : comps[0];
 	}
 
 	//since 3.1
 	//get associated clone components of a given bean and template component
 	//note that same bean can be used in multiple components
 	//assume comp is template component
 	private Component[] getCollectionItems(Component compObject beanboolean isCollectionItem) {
 		Component owner = getCollectionOwner(comp);
 		//For backward compatible, if by owner failed, try again with by item
 		if (decor == null) {
 			decor = getBindingCollectionItem(item);
 		}
 		final ListModel xmodel = decor.getModelByOwner(owner);
 		if (isCollectionItem && comp == item) {
 	    	if (xmodel instanceof BindingListModelExt && !((BindingListModelExt)xmodel).isDistinct()) {
 	    		final BindingListModelExt model = (BindingListModelExtxmodel;
 	    		final int[] indexes = model.indexesOf(bean);
 	    		final int sz = indexes.length;
 	    		final List comps = new ArrayList(sz);
 	    		for (int j = 0; j < sz; ++j) {
 	    			final Component xcomp = lookupClone(decor.getComponentAtIndexByOwner(ownerindexes[j]), comp);
 	    			comps.add(xcomp);
 	    		}
 	    		return (Component[]) comps.toArray(new Component[comps.size()]);
 	    	} else 	if (xmodel instanceof BindingListModel) {
 	  			final BindingListModel model = (BindingListModelxmodel;
 	  			int index = model.indexOf(bean);
 	  			if (index >= 0) {
 	    			return new Component[] {lookupClone(decor.getComponentAtIndexByOwner(ownerindex), comp)};
 	    		}
 	    	}
 		} else { 
 			//though the comp is in collection but the binding does not relate to _var, 
 			//have to scan through the whole cloned children items   
 			final int sz = xmodel.getSize();
     		final List comps = new ArrayList(sz);
 			if (decor instanceof CollectionItemExt) {
 				final List items = ((CollectionItemExt)decor).getItems(owner);
 				for (final Iterator it = items.iterator(); it.hasNext(); ) {
 					final Component cloneitem = (Componentit.next();
 	    			final Component xcomp = lookupClone(cloneitemcomp);
 	    			comps.add(xcomp);
 				}
 			} else {
 				for (int j = 0; j < sz; ++j) {
 	    			final Component xcomp = lookupClone(decor.getComponentAtIndexByOwner(ownerj), comp);
 	    			comps.add(xcomp);
 				}
 			}
     		return (Component[]) comps.toArray(new Component[sz]);
 		}
 		return new Component[0];
 	}
 
 	//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) {
 		mySetupTemplateComponent(compownercomp);
 	}
 	
 	private void mySetupTemplateComponent(Component compObject ownerComponent item) {
 		if (existsBindings(comp)) {
 			if (comp.getAttribute() != null) {
 				comp.setAttribute(.); //owner is a template
 			}
 			comp.setAttribute(owner);
 			comp.setAttribute(item);
 		}
 		List kids = comp.getChildren();
 		for(final Iterator it = kids.iterator(); it.hasNext(); ) {
 			mySetupTemplateComponent((Componentit.next(), owneritem); //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(6);
 		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 collection owner component. (e.g. Grid, Listbox with _var)
 	/*package*/ static boolean isCollectionOwner(Component owner) {
 		return owner.getAttribute() != null;
 	}
 	
 	//whether a component is a binding template rather than a real component
 	/* package */ static boolean isTemplate(Component comp) {
 		//bug #1941947 Cannot find associated CollectionItem error
 		return comp != null && 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);
 	}
 	
 	//Returns template component of a given clone component
 	/* package */ static Component getComponent(Component clone) {
 		return (Componentclone.getAttribute();
 	}
 
 	//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);
 			}
 			bean = fetchValue(beancurrentNodenodeidregisterNode);
 		}
 
 		return bean;
 	}
 	
 	private Object fetchValue(Object beanBindingNode nodeString nodeidboolean registerNode) {
 		if (bean != null) {
 			//feature#1766905 Binding to Map
 			//bug# 2630168, check Map case first and avoid throw unnecessary exception
 			if (bean instanceof Map) {
				bean = ((Map)bean).get(nodeid);
else {
				try {
					bean = Fields.get(beannodeid);
catch (NoSuchMethodException ex) {
					throw UiException.Aide.wrap(ex);
		if (registerNode) {
			registerBeanNode(beannode);
		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) {
					//feature#1766905 Binding to Map
					if (bean instanceof Map) {
						bean = ((Map)bean).get(beanid);
else {
						throw UiException.Aide.wrap(ex);
			if (bean == null) {
				return//no bean to set value, skip
			beanid = (Stringit.next();
			try {
				orgVal = Fields.get(beanbeanid);
				if(Objects.equals(orgValval)) {
					return//same value, no need to do anything
				Fields.set(beanbeanidvalautoConvert);
catch (NoSuchMethodException ex) {
				//feature#1766905 Binding to Map
				if (bean instanceof Map) {
					((Map)bean).put(beanidval);
else {
					throw UiException.Aide.wrap(ex);
catch (ModificationException ex) {
				throw UiException.Aide.wrap(ex);
			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;
		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) {
				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;
		//bug#1871833: Data binder should read "self".
		if ("self".equals(beanid)) {
			return comp;
		if (isClone(comp)) {
			bean = myLookupBean1(compbeanid);