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 static org.zkoss.lang.Generics.cast;
  
  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 {
  	public static final String LOAD_ON_SAVE_TRIGGER_COMPONENT = "zkoss.DataBinder.LOAD_ON_SAVE_TRIGGER_COMPONENT";
  	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<ComponentMap<StringBinding>> _compBindingMap = new LinkedHashMap<ComponentMap<StringBinding>>(29); //(comp, Map(attr, Binding))
  	private Map<StringObject_beans = new HashMap<StringObject>(29); //bean local to this DataBinder
  	private Map<ObjectSet<Object>> _beanSameNodes = new HashMap<ObjectSet<Object>>(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<StringCollectionItem_collectionOwnerMap = new HashMap<StringCollectionItem>(3); //bug#1950313 F - 1764967 bug
  	
  	private boolean _loadOnSave = true//whether firing the onLoadOnSave event to automate the loadOnSave operation 
  	
Sets whether this DataBinder shall do load-on-save automatically.

Parameters:
b true to have this DataBinder shall do load-on-save automatically.
Since:
5.0.4
  
  	public void setLoadOnSave(boolean b) {
 		 = b;
 	}

Returns whether this DataBinder shall do load-on-save automatically(default is true).

Returns:
whether this DataBinder shall do load-on-save automatically(default is true).
Since:
5.0.4
 
 	public boolean isLoadOnSave() {
 		return ;
 	}

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<String>)null, (List<String>)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<StringloadEvents = null;
 		if (loadWhenEvents != null && loadWhenEvents.length > 0) {
 			loadEvents = new ArrayList<String>(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<StringloadEvents = null;
 		if (loadWhenEvents != null && loadWhenEvents.length > 0) {
 			loadEvents = new ArrayList<String>(loadWhenEvents.length);
 			for (int j = 0; j < loadWhenEvents.length; ++j) {
 				loadEvents.add(loadWhenEvents[j]);
 			}
 		}
 		List<StringsaveEvents = null;
 		if (saveWhenEvents != null && saveWhenEvents.length > 0) {
 			saveEvents = new ArrayList<String>(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<StringloadWhenEventsString saveWhenEventString accessString converter) {
 		List<StringsaveEvents = new ArrayList<String>();
 		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<StringloadWhenEventsList<StringsaveWhenEventsString 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<String> loadWhenEvents, List<String> 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<StringloadWhenEventsList<StringsaveWhenEventsString accessString converter,
 		Map<ObjectObjectargsList<StringloadAfterEventsList<StringsaveAfterEvents) {
 		//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 = cast((Listobjs[1]);
 			}
 			if (saveWhenEvents == null && objs[2] != null) {
 				saveWhenEvents = cast((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 = cast((Mapobjs[5]);
 			}
 			if (loadAfterEvents == null && objs[6] != null) {
 				loadAfterEvents = cast((Listobjs[6]);
 			}
 			if (saveAfterEvents == null && objs[7] != null) {
 				saveAfterEvents = cast((Listobjs[7]);
 			}
 		}
 	
 		//nullify check
 		LinkedHashSet<StringloadEvents = null;
 		if (loadWhenEvents != null && loadWhenEvents.size() > 0) {
 			loadEvents = new LinkedHashSet<String>(loadWhenEvents.size());
 			for(String eventloadWhenEvents) {
 				if (.equals(event)) {
 					loadEvents.clear();
 				} else {
 					loadEvents.add(event);
 				}
 			}
 			if (loadEvents.isEmpty()) { 
 				loadEvents = null;
 			}
 		}
 
 		LinkedHashSet<StringlafterEvents = null;
 		if (loadAfterEvents != null && loadAfterEvents.size() > 0) {
 			lafterEvents = new LinkedHashSet<String>(loadAfterEvents.size());
 			for(String eventloadAfterEvents) {
 				if (.equals(event)) {
 					lafterEvents.clear();
 				} else {
 					lafterEvents.add(event);
 				}
 			}
 			if (lafterEvents.isEmpty()) { 
 				lafterEvents = null;
 			}
 		}
 		
 		LinkedHashSet<StringsaveEvents = null;
 		if (saveWhenEvents != null && saveWhenEvents.size() > 0) {
 			saveEvents = new LinkedHashSet<String>(saveWhenEvents.size());
 			for(String eventsaveWhenEvents) {
 				if (.equals(event)) {
 					saveEvents.clear();
 				} else {
 					saveEvents.add(event);
 				}
 			}
 			if (saveEvents.isEmpty()) { 
 				saveEvents = null;
 			}
 		}
 		
 		LinkedHashSet<StringsafterEvents = null;
 		if (saveAfterEvents != null && saveAfterEvents.size() > 0) {
 			safterEvents = new LinkedHashSet<String>(saveAfterEvents.size());
 			for(String eventsaveAfterEvents) {
 				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<StringBindingattrMap = .get(comp);
 		if (attrMap == null) {
 			attrMap = new LinkedHashMap<StringBinding>(3);
 			.put(compattrMap);
 		}
 			
 		if (attrMap.containsKey(attr)) { //override
 			final Binding binding = attrMap.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) {
 		//bug #2928837 Cannot remove bindable collection item from the DataBinder
 		if (isClone(comp)) {
 			comp = (Componentcomp.getAttribute();
 		}
 		Map<StringBindingattrMap = .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<StringBindingattrMap = .get(comp);
 		return attrMap != null ?  attrMap.get(attr) : null;
 	}

Given component, return the associated list of Bindings.

Parameters:
comp the concerned component
 
 		if (isClone(comp)) {
 			comp = (Componentcomp.getAttribute();
 		}
 		Map<StringBindingattrMap = .get(comp);
 		return attrMap != null ?  attrMap.values() : null;
 	}

Return all Bindings covered by this DataBinder

Returns:
all Bindings covered by this DataBinder.
Since:
3.5.2
 
 		final List<Bindingbindings = new ArrayList<Binding>(.size() * 2);
 		for (Map<StringBindingmap.values()) {
 			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<StringBindingattrMap = .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.getAttributeOrFellow(java.lang.String,boolean) 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 (Component comp.keySet()) {
 		}
 	}

Save all values from UI components to beans.
 
 	public void saveAll() {
 		init();
 		for (Component comp.keySet()) {
 		}
 	}
 
 	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<StringString[]> attrs = ann.getAttributes(); //(tag, tagExpr)
 			List<StringloadWhenEvents = null;
 			List<StringsaveWhenEvents = null;
 			List<StringloadAfterEvents = null;
 			List<StringsaveAfterEvents = null;
 			String access = null;
 			String converter = null;
 			String expr = null;
 			Map<ObjectObjectargs = null;
 			for (Map.Entry<StringString[]> entryattrs.entrySet()) {
 				String tag = entry.getKey();
 				String[] tagval = entry.getValue();
 				String tagExpr;
 				if (tagval.length != 1)
 					throw new UiException("Array of attribute values not allowed, "+Objects.toString(tagval));
 				tagExpr = tagval[0];
 
 				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<ObjectObject>();
 					}
 					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<StringvarnameSet = new HashSet<String>();
 			final LinkedHashSet<ComponenttoBeDetached = new LinkedHashSet<Component>();
 				final Component comp = me.getKey();
 				final Map<StringBindingattrMap = me.getValue();
 				final Collection<Bindingbindings = 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 = (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(Component comptoBeDetached) {
 				comp.detach();
 			}
 		}
 	}
 	
 	private void initCollectionItem(){
 		addCollectionItem(Row.classGrid.classnew RowCollectionItem());
 	}


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();
 		}
 		CollectionItem decorName = .get(name);
 		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();
 		}
 		CollectionItem decorName = .get(name);
 		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<Componentcomps = new ArrayList<Component>(sz);
 	    		for (int j = 0; j < sz; ++j) {
 	    			final Component xcomp = lookupClone(decor.getComponentAtIndexByOwner(ownerindexes[j]), comp);
 	    			if (xcomp != null)
 	    				comps.add(xcomp);
 	    		}
 	    		return comps.toArray(new Component[comps.size()]);
 	    	} else 	if (xmodel instanceof BindingListModel) {
 	  			final BindingListModel model = (BindingListModelxmodel;
 	  			int index = model.indexOf(bean);
 	  			if (index >= 0) {
 	  				final Component xcomp = lookupClone(decor.getComponentAtIndexByOwner(ownerindex), comp);
 	  				return xcomp != null ? new Component[] {xcomp} : new Component[0];
 	    		}
 	    	}
 		} 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<Componentcomps = new ArrayList<Component>(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);
 	    			if (xcomp != null)
 	    				comps.add(xcomp);
 				}
 			} else {
 				for (int j = 0; j < sz; ++j) {
 	    			final Component xcomp = lookupClone(decor.getComponentAtIndexByOwner(ownerj), comp);
 	    			if (xcomp != null)
 	    				comps.add(xcomp);
 				}
 			}
     		return 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<BindingbindingsSet<StringvarnameSet) {
 		for(Binding bindingbindings) {
 			String[] paths = binding.getPaths();
 			for(int j = 0; j < paths.length; ++j) {
 				final String path = paths[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);
 		}
 
 		for(Component ccomp.getChildren()) {
 			mySetupTemplateComponent(cowneritem); //recursive
 		}
 	}
 	
 	//parse token and return as a List of String
 	/* package */ static List<StringparseExpression(String exprString separator) {
 		if (expr == null) {
 			return null;
 		}
 		List<Stringresults = new ArrayList<String>(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<Objectset) {
 		.put(beanset);
 	}
 	
 	//get SameNode Set of the given bean
 	/* package */ Set<ObjectgetBeanSameNodes(Object bean) {
 		return .get(bean);
 	}
 	
 	//remove SameNode set of the given bean
 	/* package */ Set<ObjectremoveBeanSameNodes(Object bean) {
 		return .remove(bean);
 	}

traverse the path nodes and return the final bean.
 
 	/* package */ Object getBeanAndRegisterBeanSameNodes(Component compString path) {
 		return myGetBeanWithExpression(comppathtrue);
 	}
 	
 	/* package */ 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 = currentNode.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 = currentNode.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) { //regret the change for bug#2987511(follow the EL spec)
				bean = ((Map)bean).get(nodeid);
else {
				try {
					bean = Fields.get(beannodeid);
catch (NoSuchMethodException ex) {
					//bug# 2932475
					//SameNode algorithm not good enough. LoadOnSave might load 
					//implicit objects with same name(but different instance)
					//have to ignore the exception
					//ignore the exception
					//throw UiException.Aide.wrap(ex);
		if (registerNode) {
			registerBeanNode(beannode);
		return bean;
	@SuppressWarnings("unchecked")
	/* package */ void setBeanAndRegisterBeanSameNodes(Component compObject valBinding binding
	String pathboolean autoConvertObject rawvalList<ObjectloadOnSaveInfosString triggerEventName) {
		Object orgVal = null;
		Object bean = null;
		BindingNode currentNode = ;
		boolean refChanged = false// whether this setting change the reference
		String beanid = null;
		final List nodeids = parseExpression(path".");
		final List<BindingNodenodes = new ArrayList<BindingNode>(nodeids.size());
		final Iterator it = nodeids.iterator();
		if (it != null && it.hasNext()) {
			beanid = (Stringit.next();
			currentNode = currentNode.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.getSpaceOwner().setAttribute(beanidvaltrue);
			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 = currentNode.getKidNode(beanid);
				if (currentNode == null) {
					throw new UiException("Cannot find the specified databind bean expression:" + path);
				nodes.add(currentNode);
				// Bug B50-3183438: Access to bean shall be consistent
				if (bean instanceof Map) {
					bean = ((Map)bean).get(beanid); //feature#1766905 Binding to Map
else {
					try {
						bean = Fields.get(beanbeanid);
catch (NoSuchMethodException ex) {
						throw UiException.Aide.wrap(ex);
			if (bean == null) {
				return//no bean to set value, skip
			beanid = (Stringit.next();
			// Bug B50-3183438: Access to bean shall be consistent
			if (bean instanceof Map)
				((Map)bean).put(beanidval); //feature#1766905 Binding to Map
			else {
				try {
					orgVal = Fields.get(beanbeanid);
					if(Objects.equals(orgValval))
						return//same value, no need to do anything
					Fields.set(beanbeanidvalautoConvert);
catch (NoSuchMethodException ex) {
					throw UiException.Aide.wrap(ex);
			if (!isPrimitive(val) && !isPrimitive(orgVal)) { //val is a bean (null is not primitive)
				currentNode = currentNode.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), nodescomptriggerEventName};
		if (loadOnSaveInfos != null) {
			loadOnSaveInfos.add(loadOnSaveInfo);
else if (isLoadOnSave()) { //feature#2990932, allow disable load-on-save mechanism
			//do loadOnSave immediately
			Events.postEvent(new Event("onLoadOnSave"comploadOnSaveInfo));
	private void registerBeanNode(Object beanBindingNode node) {
		if (isPrimitive(bean)) {
			return;
		final Set<ObjectnodeSameNodes = node.getSameNodes();
		final Set<ObjectbinderSameNodes = 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;
		for(final Iterator it = comp.getPage().getLoadedInterpreters().iterator();
		it.hasNext();) {
			final Interpreter ip = (Interpreterit.next();
			if (ip instanceof HierachicalAware) {