Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /* Binding.java
  
  {{IS_NOTE
  	Purpose:
  		
  	Description:
  		
  	History:
  		Thu Feb  1 17:13:40     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.List;
 import java.util.Map;
 import java.util.Set;
 
 import  org.zkoss.zul.impl.InputElement;

A Data Binding that associate component+attr to an bean expression.

Author(s):
Henri
Since:
3.0.0
 
 public class Binding implements java.io.Serializable {
 	private static final long serialVersionUID = 200808191512L;
 	private DataBinder _binder;
 	private Component _comp;
 	private String _attr;
 	private String _expression//the bean expression
 	private Set _saveWhenEvents;
 	private boolean _loadable = true;
 	private boolean _savable;
 	private String[] _paths//bean reference path (a.b.c)
 	private Map _args//generic arguments
 	
Constrcutor to form a binding between UI component and backend data bean.

Parameters:
binder the associated Data Binder.
comp The concerned component
attr The component attribute
expr The bean expression.
loadWhenEvents The event set when to load data.
saveWhenEvents The event set 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.expr is "load", but Textbox.expr is "both".
converter The converter class used to convert classes between component attribute and the associated bean expression. null means using the default class conversion method.
 
 	/*package*/ Binding(DataBinder binderComponent compString attrString expr
 		LinkedHashSet loadWhenEventsSet saveWhenEventsString accessString converter) {
 		this(bindercompattrexprloadWhenEventssaveWhenEventsaccessconverternull);
 	}

Constrcutor to form a binding between UI component and backend data bean.

Parameters:
binder the associated Data Binder.
comp The concerned component
attr The component attribute
expr The bean expression.
loadWhenEvents The event set when to load data.
saveWhenEvents The event set 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.expr is "load", but Textbox.expr is "both".
converter The converter class used to convert classes between component attribute and the associated bean expression. null means using the default class conversion method.
args generic arguments
Since:
3.1
 
 	/*package*/ Binding(DataBinder binderComponent compString attrString expr
 		LinkedHashSet loadWhenEventsSet saveWhenEventsString accessString converterMap args) {
 		 = binder;
 		 = comp;
 		setAttr(attr);
		setLoadWhenEvents(loadWhenEvents);
		setSaveWhenEvents(saveWhenEvents);
		setAccess(access);
		setConverter(converter);
		setArgs(args);
	}

Gets the associated Data Binder of this Binding.
	public DataBinder getBinder() {
		return ;
	}

Gets the associated Component of this Binding.
	public Component getComponent() {
		return ;
	}

Set component attribute name.

Parameters:
attr component attribute.
	/*package*/void setAttr(String attr) {
		 = attr;
	}

Get component attribute name.
	public String getAttr() {
		return ;
	}
	/*package*/void setArgs(Map args) {
		 = args;
	}

Get generic arguments.
	public Map getArgs() {
		return ;
	}

Set bean expression (a.b.c).
	/*package*/ void setExpression(String expr) {
		 = expr;
	}
	//:TODO: handle function parsing	
	private String[] parseBeanExpression(String expr) {
		String[] paths = new String[1];
		paths[0] = expr;
		return paths;
	}

Get bean expression, e.g. a.b.c.
	public String getExpression() {
		return ;
	}

Internal methods. DO NOT USE THIS. Get bean reference paths.
	/*package*/ String[] getPaths() {
		return ;
	}

Set save-when event expression.

Parameters:
saveWhenEvents the save-when expression.
	/*package*/ void setSaveWhenEvents(Set saveWhenEvents) {
		 = saveWhenEvents;
	}

Get save-when event expression.
	public Set getSaveWhenEvents() {
	}

Add load-when event expression.

Parameters:
loadWhenEvent the load-when expression.
	/*package*/ void setLoadWhenEvents(LinkedHashSet loadWhenEvents) {
		 = loadWhenEvents;
	}

Get load-when event expression set.
	}

Set accessibility.
	/*package*/ void setAccess(String access) {
		if (access == null) { //default access to none
			return;
		}
		if ("both".equals(access)) {
			 = true;
			 = true;
else if ("load".equals(access)) {
			 = true;
			 = false;
else if ("save".equals(access)) {
			 = false;
			 = true;
else if ("none".equals(access)) { 
			 = false;
			 = false;
else {//unknow access mode
			throw new UiException("Unknown DataBinder access mode. Should be \"both\", \"load\", \"save\", or \"none\": "+access);
		}
	}

Whether the binding is loadable.
	public boolean isLoadable() {
		return ;
	}

Whether the binding is savable.
	public boolean isSavable() {
		return ;
	}

Set the TypeConverter.

Parameters:
cvtClsName the converter class name.
	/*package*/ void setConverter(String cvtClsName) {
		if (cvtClsName != null) {
			//bug #2129992
			Class cls = null;
			if ( == null || .getPage() == null) {
				try {
					cls = Classes.forNameByThread(cvtClsName);
catch (ClassNotFoundException ex) {
					throw UiException.Aide.wrap(ex);
				}
else {
				cls = .getPage().getZScriptClass(cvtClsName); 
				if (cls == null) {
					throw UiException.Aide.wrap(new ClassNotFoundException(cvtClsName));
				}
			}
			try {
catch (Exception ex) {
				throw UiException.Aide.wrap(ex);
			}
		}
	}

Get the TypeConverter.
		return ;
	}

load bean value into the attribute of the specified component.

Parameters:
comp the component.
	public void loadAttribute(Component comp) {
		if (!isLoadable() 
				|| .startsWith("_"
				|| DataBinder.isTemplate(comp)
				|| comp == null //bug #1941947 Cannot find associated CollectionItem
				|| comp.getPage() == null) { 
			return//cannot load, a control attribute, or a detached component, skip!
		}
		myLoadAttribute(compbean);
	}

load bean value into the attribute of the specified component.

Parameters:
comp the component.
bean the bean value.
	public void loadAttribute(Component compObject bean) {
		if (!isLoadable() || .startsWith("_") || DataBinder.isTemplate(comp) || comp.getPage() == null) { 
			return//cannot load, a control attribute, or a detached component, skip!
		}
		myLoadAttribute(compbean);
	}
	private void myLoadAttribute(Component compObject bean) {
		try {
			//since 3.1, 20080416, support bindingArgs for non-supported tag
			if ( != null) {
				bean = .coerceToUi(beancomp);
				if (bean == .)
					return//ignore, so don't do Fields.set()
			}
			//Bug #1876198 Error msg appears when load page (databind+CustomConstraint)
			//catching WrongValueException no longer works, check special case and 
			//use setRowValue() method directly
			if ((comp instanceof InputElement) && "value".equals()) {
				Object value = bean;
				try { //Bug 1879389
					final Method m = comp.getClass().getMethod("getValue"null);
					value = Classes.coerce(m.getReturnType(), bean);
catch (NoSuchMethodException ex) { //ignore it
				}
				Fields.set(comp"rawValue"value == null);
else {
				Fields.set(compbean == null);
			}
catch (ClassCastException ex) {
			throw UiException.Aide.wrap(ex);
catch (NoSuchMethodException ex) {
			//Bug #1813278, Annotations do not work with xhtml tags
			if (comp instanceof DynamicPropertied) {
				final DynamicPropertied dpcomp = (DynamicPropertiedcomp;
 				if (dpcomp.hasDynamicProperty()) {
					//no way to know destination type of the property, use bean as is
 					dpcomp.setDynamicProperty(bean);
 				} else {
 					throw UiException.Aide.wrap(ex);
 				}
else {
				throw UiException.Aide.wrap(ex);
			}
catch (ModificationException ex) {
			throw UiException.Aide.wrap(ex);
		//Bug #1876198 Error msg appears when load page (databind+CustomConstraint)
		//catching WrongValueException no longer works, so mark it out
		/*} catch (WrongValueException ex) {
			//Bug #1615371, try to use setRawValue()
			if ("value".equals(_attr)) {
				try {
					Fields.set(comp, "rawValue", bean, _converter == null);
				} catch (Exception ex1) {
					//exception
					throw ex;
				}
			} else {
				throw ex;
			}
		*/
		}
	}

save into bean value from the attribute of the specified component.

Parameters:
comp the component.
	public void saveAttribute(Component comp) {
		final Object[] vals = getAttributeValues(comp);
		if (vals != null)
			saveAttributeValue(compvalsnull);
	}
	private void saveAttributeValue(Component compObject[] valsList loadOnSaveInfos) {
		if (vals == nullreturn;
		final Object val = vals[0];
		final Object rawval = vals[1];
		.setBeanAndRegisterBeanSameNodes(compvalthis == nullrawvalloadOnSaveInfos);
	}		

Get converted value and original value of this Binding.
	private Object[] getAttributeValues(Component comp) {
		if (!isSavable() || .startsWith("_") || DataBinder.isTemplate(comp) || comp.getPage() == null) { 
			return null//cannot save, a control attribute, or a detached component, skip!
		}
		Object rawval = null;
		try {
			rawval = Fields.get(comp);
catch (NoSuchMethodException ex) {
			//Bug #1813278, Annotations do not work with xhtml tags
			if (comp instanceof DynamicPropertied) {
				final DynamicPropertied dpcomp = (DynamicPropertiedcomp;
 				if (dpcomp.hasDynamicProperty()) {
 					rawval = dpcomp.getDynamicProperty();
 				} else {
 					throw UiException.Aide.wrap(ex);
 				}
else {
				throw UiException.Aide.wrap(ex);
			}
		}
		try {
			final Object val = ( == null) ? rawval : .coerceToBean(rawvalcomp);
			return val == . ? null : new Object[] {valrawval};
catch (ClassCastException ex) {
			throw UiException.Aide.wrap(ex);
		}
	}		
	/*package*/ void registerSaveEvents(Component comp) {
		if ( != null && isSavable()) { //bug 1804356
			for(final Iterator it = .iterator(); it.hasNext(); ) {
				final String expr = (Stringit.next();
				final Object[] objs =
					ComponentsCtrl.parseEventExpression(compexprcompfalse);
				//objs[0] component, objs[1] event name
				final Component target = (Componentobjs[0];
				final String evtname = (Stringobjs[1];
					target.getAttribute("zk.SaveEventListener."+evtname);
				if (listener == null) {
					listener = new SaveEventListener();
					target.setAttribute("zk.SaveEventListener."+evtnamelistener);
					target.addEventListener(evtnamelistener);
				}
				listener.addDataTarget(thiscomp);
			}
		}
	}
	/*package*/ void registerLoadEvents(Component comp) {
		if ( != null && isLoadable()) { //bug 1804356
			for(final Iterator it = .iterator(); it.hasNext(); ) {
				final String expr = (Stringit.next();
				final Object[] objs =
					ComponentsCtrl.parseEventExpression(compexprcompfalse);
				//objs[0] component, objs[1] event name
				final Component target = (Componentobjs[0];
				final String evtname = (Stringobjs[1];
					target.getAttribute("zk.LoadEventListener."+evtname);
				if (listener == null) {
					listener = new LoadEventListener();
					target.setAttribute("zk.LoadEventListener."+evtnamelistener);
					target.addEventListener(evtnamelistener);
				}
				listener.addDataTarget(thiscomp);
			}
		}
	}
	//-- Object --//
	public String toString() {
		return "[binder:"++", comp:"++", attr:"++", expr:"+
			+", load-when:"++", save-when:"+
			+", load:"++", save:"++", converter:"++"]";
	}
	private static class BindingInfo implements Serializable {
		private static final long serialVersionUID = 200808191315L;
		private Binding _binding;
		private Component _comp;
		private Object[] _vals;
		public BindingInfo(Binding bindingComponent compObject[] vals) {
			 = binding;
			 = comp;
			 = vals;
		}
		public Component getComponent() {
			return ;
		}
		public Binding getBinding() {
			return ;
		}
		public Object[] getAttributeValues() {
			return ;
		}
	}
	private static abstract class BaseEventListener implements EventListenerExpressjava.io.Serializable {
		protected List _dataTargets;
		public BaseEventListener() {
		}
		public void addDataTarget(Binding bindingComponent comp) {
			.add(new BindingInfo(bindingcompnull));
		}
	}
	private static class LoadEventListener extends BaseEventListener {
		private static final long serialVersionUID = 200808191313L;
		public LoadEventListener() {
			super();
		}
		public void onEvent(Event event) {
			for(final Iterator it = .iterator();it.hasNext();) {
				final BindingInfo bi = (BindingInfoit.next();
				final Component dt = bi.getComponent();
				final Binding binding = bi.getBinding();
				final Component dataTarget = DataBinder.isTemplate(dt) ? 
					DataBinder.lookupClone(event.getTarget(), dt) : dt;
				if (dataTarget != null) {
					binding.loadAttribute(dataTarget);
				}
			}
		}
	}
	private static class SaveEventListener extends BaseEventListener {
		private static final long serialVersionUID = 200808191313L;
		public SaveEventListener() {
			super();
		}
		public void onEvent(Event event) {
			final Component target = event.getTarget();
			final List tmplist = new ArrayList(.size());
			//fire onSave for each binding
			for(final Iterator it = .iterator();it.hasNext();) {
				final BindingInfo bi = (BindingInfoit.next();
				final Component dt = bi.getComponent();
				final Binding binding = bi.getBinding();
				final DataBinder binder = binding.getBinder();
				final Component dataTarget = DataBinder.isTemplate(dt) ? 
					DataBinder.lookupClone(targetdt) : dt;
				//bug# 1904389: NullPointerException if save-when in collection binding
				//event.getTarget() might not inside the collection item (i.e. row, listitem, etc.)
				//then binder.lookupClone() will return null dataTarget.
				if (dataTarget != null) {
					final Object[] vals = binding.getAttributeValues(dataTarget);
					if (vals != null) {
						tmplist.add(new BindingInfo(bindingdataTargetvals));
						Events.sendEvent(new BindingSaveEvent("onBindingSave"dataTargettargetbindingvals[0]));
					}
else {
					//bi.getComponent a template and a null dataTarget, meaning all collection items has to
					//be handled. Search the owner to iterate all cloned items.
					final List clones = scanClones(binderdt);
					for (final Iterator itc = clones.iterator(); itc.hasNext();) {
						final Component dataTarget1 = (Component)itc.next();
						final Object[] vals = binding.getAttributeValues(dataTarget1);
						if (vals != null) {
							tmplist.add(new BindingInfo(bindingdataTarget1vals));
							Events.sendEvent(new BindingSaveEvent("onBindingSave"dataTarget1targetbindingvals[0]));
						}
					}
				}
			}
			//fire onValidate for target component
			Events.sendEvent(new Event("onBindingValidate"target));
			//saveAttribute for each binding
			Component loadOnSaveProxy = null;
			Component dataTarget = null;
			final List loadOnSaveInfos = new ArrayList(tmplist.size());
			for(final Iterator it = tmplist.iterator();it.hasNext();) {
				final BindingInfo bi = (BindingInfoit.next();
				dataTarget = bi.getComponent();
				final Binding binding = bi.getBinding();
				final Object[] vals = bi.getAttributeValues();
				binding.saveAttributeValue(dataTargetvalsloadOnSaveInfos);
				if (loadOnSaveProxy == null && dataTarget.isListenerAvailable("onLoadOnSave"true)) {
					loadOnSaveProxy = dataTarget;
				}
			}
			//bug #1895856 : data binding LoadOnSave works only on the last save-when component
			//do loadOnSave
			//if (dataTarget != null) {
			//		Events.postEvent(new Event("onLoadOnSave", dataTarget, loadOnSaveInfos));
			//	}
			// (use first working dataTarget as proxy)
			if (loadOnSaveProxy != null) {
				Events.postEvent(new Event("onLoadOnSave"loadOnSaveProxyloadOnSaveInfos));
			}
		}
		//scan up the component hierarchy until the real owner is found and collect all cloned components.
		private List scanClones(DataBinder binderComponent comp) {
			if (DataBinder.isTemplate(comp)) {
				final List owners = scanClones(binderbinder.getCollectionOwner(comp)); //recursive
				final List kidowners = new ArrayList(1024);
				for (final Iterator it = owners.iterator(); it.hasNext();) {
					final Component owner = (Componentit.next();
					final CollectionItem decor = binder.getCollectionItemByOwner(owner);
					//backward compatible, CollectionItemEx.getItems() is faster
					if (decor instanceof CollectionItemExt) {
						final CollectionItemExt decorex = (CollectionItemExtdecor;
						for (final Iterator iti = decorex.getItems(owner).iterator(); iti.hasNext();) {
							final Component item = (Componentiti.next();
							kidowners.add(DataBinder.lookupClone(itemcomp));
						}
else {
						try {
							for (int j = 0; true; ++j) { //iterate until out of bound
								final Component item = decor.getComponentAtIndexByOwner(ownerj);
								kidowners.add(DataBinder.lookupClone(itemcomp));
							}
catch (IndexOutOfBoundsException ex) {
							//ignore, iterate until out of bound
						}
					}
				}
				return kidowners;
else {
				final List owners = new ArrayList(1);
				owners.add(comp);
				return owners;
			}
		}
	}
New to GrepCode? Check out our FAQ X