001: /* BindingRowRenderer.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Mon Mar 19 11:10:12 2007, Created by Henri
010: }}IS_NOTE
011:
012: Copyright (C) 2007 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: }}IS_RIGHT
016: */
017: package org.zkoss.zkplus.databind;
018:
019: import org.zkoss.zk.ui.Component;
020: import org.zkoss.zk.ui.sys.ComponentsCtrl;
021:
022: import org.zkoss.zul.Listbox;
023: import org.zkoss.zul.Grid;
024: import org.zkoss.zul.Row;
025: import org.zkoss.zul.RowRenderer;
026: import org.zkoss.zul.RowRendererExt;
027:
028: import java.util.Map;
029: import java.util.HashMap;
030: import java.util.List;
031: import java.util.ArrayList;
032: import java.util.Iterator;
033: import java.io.Serializable;
034:
035: /*package*/class BindingRowRenderer implements
036: org.zkoss.zul.RowRenderer, org.zkoss.zul.RowRendererExt,
037: Serializable {
038: private static final String KIDS = "zkplus.databind.KIDS";
039: private Row _template;
040: private DataBinder _binder;
041: private int x = 0;
042:
043: public BindingRowRenderer(Row template, DataBinder binder) {
044: _template = template;
045: _binder = binder;
046: }
047:
048: //-- RowRendererExt --//
049: public Row newRow(Grid grid) {
050: //clone from template
051: final Row clone = (Row) _template.clone();
052:
053: //avoid duplicate id error, will set to new id when render()
054: if (!ComponentsCtrl.isAutoId(clone.getId())) {
055: clone.setId("@" + clone.getUuid() + x++);
056: }
057:
058: //link cloned component with template
059: //each Row and its decendants share the same templatemap
060: Map templatemap = new HashMap(7);
061: linkTemplates(clone, _template, templatemap);
062:
063: //link this template map to parent templatemap (Grid in Grid)
064: Map parenttemplatemap = (Map) grid
065: .getAttribute(_binder.TEMPLATEMAP);
066: if (parenttemplatemap != null) {
067: templatemap.put(_binder.TEMPLATEMAP, parenttemplatemap);
068: }
069: //kept clone kids somewhere to avoid create too many components in browser
070: final List kids = new ArrayList(clone.getChildren());
071: clone.setAttribute(KIDS, kids);
072: clone.getChildren().clear();
073: return clone;
074: }
075:
076: public Component newCell(Row row) {
077: return null;
078: }
079:
080: public int getControls() {
081: //bug 1869003: Add DETACH_ON_UNLOAD. That is, always use a new Row whenever
082: //model is sync; otherwise Row's children is also reused.
083: return DETACH_ON_RENDER | DETACH_ON_UNLOAD;
084: }
085:
086: //-- RowRenderer --//
087: public void render(Row row, java.lang.Object bean) {
088: final List kids = (List) row.getAttribute(KIDS);
089: row.getChildren().addAll(kids);
090: // row.removeAttribute(KIDS);
091:
092: //remove template mark of cloned component and its decendant
093: _binder.setupTemplateComponent(row, null);
094:
095: //setup clone id
096: setupCloneIds(row);
097:
098: //bind bean to the associated listitem and its decendant
099: final String varname = (String) _template
100: .getAttribute(_binder.VARNAME);
101: final Map templatemap = (Map) row
102: .getAttribute(_binder.TEMPLATEMAP);
103: templatemap.put(varname, bean);
104:
105: //apply the data binding
106: _binder.loadComponent(row);
107: }
108:
109: //link cloned components with bindings of templates
110: private void linkTemplates(Component clone, Component template,
111: Map templatemap) {
112: if (_binder.existsBindings(template)) {
113: templatemap.put(template, clone);
114: clone.setAttribute(_binder.TEMPLATEMAP, templatemap);
115: clone.setAttribute(_binder.TEMPLATE, template);
116: }
117:
118: //Listbox in Listbox, Listbox in Grid, Grid in Listbox, Grid in Grid, no need to process
119: if (template instanceof Grid || template instanceof Listbox) {
120: return;
121: }
122:
123: final Iterator itt = template.getChildren().iterator();
124: final Iterator itc = clone.getChildren().iterator();
125: while (itt.hasNext()) {
126: final Component t = (Component) itt.next();
127: final Component c = (Component) itc.next();
128: linkTemplates(c, t, templatemap); //recursive
129: }
130: }
131:
132: //setup id of cloned components (cannot called until the component is attached to Grid)
133: private void setupCloneIds(Component clone) {
134: //bug #1813271: Data binding generates duplicate ids in grids/listboxes
135: clone.setId("@" + clone.getUuid() + x++); //init id to @uuid to avoid duplicate id issue
136:
137: //Listbox in Listbox, Listbox in Grid, Grid in Listbox, Grid in Grid, no need to process
138: if (clone instanceof Grid || clone instanceof Listbox) {
139: return;
140: }
141:
142: for (final Iterator it = clone.getChildren().iterator(); it
143: .hasNext();) {
144: setupCloneIds((Component) it.next()); //recursive
145: }
146: }
147: }
|