Source Code Cross Referenced for DataBinder.java in  » Ajax » zk » org » zkoss » zkplus » databind » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Ajax » zk » org.zkoss.zkplus.databind 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /* DataBinder.java
0002:
0003:        {{IS_NOTE
0004:        	Purpose:
0005:        		
0006:        	Description:
0007:        		
0008:        	History:
0009:        		Thu Feb  1 18:27:18     2007, Created by Henri
0010:        }}IS_NOTE
0011:
0012:        Copyright (C) 2006 Potix Corporation. All Rights Reserved.
0013:
0014:        {{IS_RIGHT
0015:        }}IS_RIGHT
0016:         */
0017:        package org.zkoss.zkplus.databind;
0018:
0019:        import org.zkoss.zk.ui.Page;
0020:        import org.zkoss.zk.ui.Path;
0021:        import org.zkoss.zk.ui.Component;
0022:        import org.zkoss.zk.ui.UiException;
0023:        import org.zkoss.zk.ui.sys.ComponentCtrl;
0024:        import org.zkoss.zk.ui.metainfo.Annotation;
0025:        import org.zkoss.zk.ui.event.Event;
0026:        import org.zkoss.zk.ui.event.Events;
0027:        import org.zkoss.zk.ui.event.EventListener;
0028:        import org.zkoss.zk.scripting.Namespace;
0029:        import org.zkoss.zk.scripting.Interpreter;
0030:        import org.zkoss.zk.scripting.HierachicalAware;
0031:
0032:        import org.zkoss.zul.Comboitem;
0033:        import org.zkoss.zul.Row;
0034:        import org.zkoss.zul.Listitem;
0035:        import org.zkoss.zul.ListModel;
0036:
0037:        import org.zkoss.util.ModificationException;
0038:        import org.zkoss.lang.Objects;
0039:        import org.zkoss.lang.Primitives;
0040:        import org.zkoss.lang.reflect.Fields;
0041:
0042:        import java.util.Map;
0043:        import java.util.Set;
0044:        import java.util.Date;
0045:        import java.util.Map.Entry;
0046:        import java.util.List;
0047:        import java.util.HashSet;
0048:        import java.util.Iterator;
0049:        import java.util.ArrayList;
0050:        import java.util.ListIterator;
0051:        import java.util.HashMap;
0052:        import java.util.HashSet;
0053:        import java.util.Collection;
0054:        import java.util.LinkedHashMap;
0055:        import java.util.LinkedHashSet;
0056:
0057:        /**
0058:         * The DataBinder used for binding ZK UI component and the backend data bean.
0059:         *
0060:         * @author Henri Chen
0061:         */
0062:        public class DataBinder {
0063:            public static final String NULLIFY = "none"; //used to nullify default configuration
0064:            public static final String VARNAME = "zkplus.databind.VARNAME"; //_var name
0065:            public static final String TEMPLATEMAP = "zkplus.databind.TEMPLATEMAP"; // template -> clone
0066:            public static final String TEMPLATE = "zkplus.databind.TEMPLATE"; //clone -> template
0067:            private static final String OWNER = "zkplus.databind.OWNER"; //the collection owner of the template component
0068:            private static final String HASTEMPLATEOWNER = "zkplus.databind.HASTEMPLATEOWNER"; //whether has template owner (collection in collection)
0069:            private static final Object NA = new Object();
0070:
0071:            private Map _compBindingMap = new LinkedHashMap(29); //(comp, Map(attr, Binding))
0072:            private Map _beans = new HashMap(29); //bean local to this DataBinder
0073:            private Map _beanSameNodes = new HashMap(29); //(bean, Set(BindingNode)) bean same nodes, diff expression but actually hold the same bean
0074:            private BindingNode _pathTree = new BindingNode("/", false, "/",
0075:                    false); //path dependency tree.
0076:            private boolean _defaultConfig = true; //whether load default configuration from lang-addon.xml
0077:            private boolean _init; //whether this databinder is initialized. 
0078:            private EventListener _listener = new LoadOnSaveEventListener();
0079:            //Databinder is init automatically when saveXXX or loadXxx is called
0080:
0081:            protected Map _collectionItemMap = new HashMap(3);
0082:
0083:            /** Binding bean to UI component. This is the same as 
0084:             * addBinding(Component comp, String attr, String expr, (List)null, (List)null, (String)null, (String)null). 
0085:             * @param comp The component to be associated.
0086:             * @param attr The attribute of the component to be associated.
0087:             * @param expr The expression to associate the data bean.
0088:             */
0089:            public void addBinding(Component comp, String attr, String expr) {
0090:                addBinding(comp, attr, expr, (List) null, (List) null, null,
0091:                        null);
0092:            }
0093:
0094:            /** Binding bean to UI component. 
0095:             * @param comp The component to be associated.
0096:             * @param attr The attribute of the component to be associated.
0097:             * @param expr The expression to associate the data bean.
0098:             * @param loadWhenEvents The event list when to load data.
0099:             * @param saveWhenEvent The event when to save data.
0100:             * @param access In the view of UI component: "load" load only, 
0101:             * "both" load/save, "save" save only when doing
0102:             * data binding. null means using the default access natural of the component. 
0103:             * e.g. Label.value is "load", but Textbox.value is "both".
0104:             * @param converter The converter class used to convert classes between component 
0105:             *  and the associated bean. null means using the default class conversion method.
0106:             */
0107:            public void addBinding(Component comp, String attr, String expr,
0108:                    String[] loadWhenEvents, String saveWhenEvent,
0109:                    String access, String converter) {
0110:                List loadEvents = null;
0111:                if (loadWhenEvents != null && loadWhenEvents.length > 0) {
0112:                    loadEvents = new ArrayList(loadWhenEvents.length);
0113:                    for (int j = 0; j < loadWhenEvents.length; ++j) {
0114:                        loadEvents.add(loadWhenEvents[j]);
0115:                    }
0116:                }
0117:                addBinding(comp, attr, expr, loadEvents, saveWhenEvent, access,
0118:                        converter);
0119:            }
0120:
0121:            /** Binding bean to UI component. 
0122:             * @param comp The component to be associated.
0123:             * @param attr The attribute of the component to be associated.
0124:             * @param expr The expression to associate the data bean.
0125:             * @param loadWhenEvents The event list when to load data.
0126:             * @param saveWhenEvents The event when to save data.
0127:             * @param access In the view of UI component: "load" load only, 
0128:             * "both" load/save, "save" save only when doing
0129:             * data binding. null means using the default access natural of the component. 
0130:             * e.g. Label.value is "load", but Textbox.value is "both".
0131:             * @param converter The converter class used to convert classes between component 
0132:             *  and the associated bean. null means using the default class conversion method.
0133:             * @since 3.0.0
0134:             */
0135:            public void addBinding(Component comp, String attr, String expr,
0136:                    String[] loadWhenEvents, String[] saveWhenEvents,
0137:                    String access, String converter) {
0138:                List loadEvents = null;
0139:                if (loadWhenEvents != null && loadWhenEvents.length > 0) {
0140:                    loadEvents = new ArrayList(loadWhenEvents.length);
0141:                    for (int j = 0; j < loadWhenEvents.length; ++j) {
0142:                        loadEvents.add(loadWhenEvents[j]);
0143:                    }
0144:                }
0145:                List saveEvents = null;
0146:                if (saveWhenEvents != null && saveWhenEvents.length > 0) {
0147:                    saveEvents = new ArrayList(saveWhenEvents.length);
0148:                    for (int j = 0; j < saveWhenEvents.length; ++j) {
0149:                        saveEvents.add(saveWhenEvents[j]);
0150:                    }
0151:                }
0152:                addBinding(comp, attr, expr, loadEvents, saveEvents, access,
0153:                        converter);
0154:            }
0155:
0156:            /** Binding bean to UI component. 
0157:             * @param comp The component to be associated.
0158:             * @param attr The attribute of the component to be associated.
0159:             * @param expr The expression to associate the data bean.
0160:             * @param loadWhenEvents The event list when to load data.
0161:             * @param saveWhenEvent The event when to save data.
0162:             * @param access In the view of UI component: "load" load only, 
0163:             * "both" load/save, "save" save only when doing
0164:             * data binding. null means using the default access natural of the component. 
0165:             * e.g. Label.value is "load", but Textbox.value is "both".
0166:             * @param converter The converter class used to convert classes between component 
0167:             *  and the associated bean. null means using the default class conversion method.
0168:             */
0169:            public void addBinding(Component comp, String attr, String expr,
0170:                    List loadWhenEvents, String saveWhenEvent, String access,
0171:                    String converter) {
0172:                List saveEvents = new ArrayList(1);
0173:                saveEvents.add(saveWhenEvent);
0174:                addBinding(comp, attr, expr, loadWhenEvents, saveEvents,
0175:                        access, converter);
0176:            }
0177:
0178:            /** Binding bean to UI component. 
0179:             * @param comp The component to be associated.
0180:             * @param attr The attribute of the component to be associated.
0181:             * @param expr The expression to associate the data bean.
0182:             * @param loadWhenEvents The event list when to load data.
0183:             * @param saveWhenEvents The event list when to save data.
0184:             * @param access In the view of UI component: "load" load only, 
0185:             * "both" load/save, "save" save only when doing
0186:             * data binding. null means using the default access natural of the component. 
0187:             * e.g. Label.value is "load", but Textbox.value is "both".
0188:             * @param converter The converter class used to convert classes between component 
0189:             *  and the associated bean. null means using the default class conversion method.
0190:             * @since 3.0.0
0191:             */
0192:            public void addBinding(Component comp, String attr, String expr,
0193:                    List loadWhenEvents, List saveWhenEvents, String access,
0194:                    String converter) {
0195:
0196:                //Since 2.5, 20070726, Henri Chen: we accept "each" to replace "_var" in collection data binding
0197:                //Before 2.4.1
0198:                //<a:bind _var="person">
0199:                //<listitem...>
0200:                //After 2.5
0201:                //<listitem self="@{bind(each='person')}"...>
0202:                //or <listitem self="@{each='person'}"...>
0203:                if ("each".equals(attr)) {
0204:                    attr = "_var";
0205:                }
0206:
0207:                if (isDefaultConfig()) { //use default binding configuration
0208:                    //handle default-bind defined in lang-addon.xml
0209:                    Object[] objs = loadPropertyAnnotation(comp, attr,
0210:                            "default-bind");
0211:
0212:                    /* logically impossible to hold "expr" in default binding 
0213:                    if (expr == null && objs[0] != null) {
0214:                    	expr = (String) objs[0];
0215:                    }
0216:                     */
0217:
0218:                    if (loadWhenEvents == null && objs[1] != null) {
0219:                        loadWhenEvents = (List) objs[1];
0220:                    }
0221:                    if (saveWhenEvents == null && objs[2] != null) {
0222:                        saveWhenEvents = (List) objs[2];
0223:                    }
0224:                    if (access == null && objs[3] != null) {
0225:                        access = (String) objs[3];
0226:                    }
0227:                    if (converter == null && objs[4] != null) {
0228:                        converter = (String) objs[4];
0229:                    }
0230:                }
0231:
0232:                //nullify check
0233:                boolean nullify = false;
0234:                LinkedHashSet loadEvents = null;
0235:                if (loadWhenEvents != null && loadWhenEvents.size() > 0) {
0236:                    loadEvents = new LinkedHashSet(loadWhenEvents.size());
0237:                    for (final Iterator it = loadWhenEvents.iterator(); it
0238:                            .hasNext();) {
0239:                        final String event = (String) it.next();
0240:                        if (NULLIFY.equals(event)) {
0241:                            loadEvents.clear();
0242:                            nullify = true;
0243:                        } else {
0244:                            nullify = false;
0245:                            loadEvents.add(event);
0246:                        }
0247:                    }
0248:                    if (loadEvents.isEmpty()) {
0249:                        loadEvents = null;
0250:                    }
0251:                }
0252:
0253:                nullify = false;
0254:                Set saveEvents = null;
0255:                if (saveWhenEvents != null && saveWhenEvents.size() > 0) {
0256:                    saveEvents = new HashSet(saveWhenEvents.size());
0257:                    for (final Iterator it = saveWhenEvents.iterator(); it
0258:                            .hasNext();) {
0259:                        final String event = (String) it.next();
0260:                        if (NULLIFY.equals(event)) {
0261:                            saveEvents.clear();
0262:                            nullify = true;
0263:                        } else {
0264:                            nullify = false;
0265:                            saveEvents.add(event);
0266:                        }
0267:                    }
0268:                    if (saveEvents.isEmpty()) {
0269:                        saveEvents = null;
0270:                    }
0271:                }
0272:
0273:                if (NULLIFY.equals(access)) {
0274:                    access = null;
0275:                }
0276:
0277:                if (NULLIFY.equals(converter)) {
0278:                    converter = null;
0279:                }
0280:
0281:                Map attrMap = (Map) _compBindingMap.get(comp);
0282:                if (attrMap == null) {
0283:                    attrMap = new LinkedHashMap(3);
0284:                    _compBindingMap.put(comp, attrMap);
0285:                }
0286:
0287:                if (attrMap.containsKey(attr)) { //override
0288:                    final Binding binding = (Binding) attrMap.get(attr);
0289:                    binding.setExpression(expr);
0290:                    binding.setLoadWhenEvents(loadEvents);
0291:                    binding.setSaveWhenEvents(saveEvents);
0292:                    binding.setAccess(access);
0293:                    binding.setConverter(converter);
0294:                } else {
0295:                    attrMap.put(attr, new Binding(this , comp, attr, expr,
0296:                            loadEvents, saveEvents, access, converter));
0297:                }
0298:            }
0299:
0300:            /** Remove the binding associated with the attribute of the component.
0301:             * @param comp The component to be removed the data binding association.
0302:             * @param attr The attribute of the component to be removed the data binding association.
0303:             */
0304:            public void removeBinding(Component comp, String attr) {
0305:                Map attrMap = (Map) _compBindingMap.get(comp);
0306:                if (attrMap != null) {
0307:                    attrMap.remove(attr);
0308:                }
0309:            }
0310:
0311:            /** Given component and attr, return the associated {@link Binding}.
0312:             * @param comp the concerned component
0313:             * @param attr the concerned attribute
0314:             */
0315:            public Binding getBinding(Component comp, String attr) {
0316:                if (isClone(comp)) {
0317:                    comp = (Component) comp.getAttribute(TEMPLATE);
0318:                }
0319:                Map attrMap = (Map) _compBindingMap.get(comp);
0320:                return attrMap != null ? (Binding) attrMap.get(attr) : null;
0321:            }
0322:
0323:            /** Given component, return the associated list of {@link Binding}s.
0324:             * @param comp the concerned component
0325:             */
0326:            public Collection getBindings(Component comp) {
0327:                if (isClone(comp)) {
0328:                    comp = (Component) comp.getAttribute(TEMPLATE);
0329:                }
0330:                Map attrMap = (Map) _compBindingMap.get(comp);
0331:                return attrMap != null ? (Collection) attrMap.values() : null;
0332:            }
0333:
0334:            /** Whether this component associated with any bindings.
0335:             */
0336:            public boolean existsBindings(Component comp) {
0337:                if (isClone(comp)) {
0338:                    comp = (Component) comp.getAttribute(TEMPLATE);
0339:                }
0340:                return _compBindingMap.containsKey(comp);
0341:            }
0342:
0343:            /** Whether this component and attribute associated with a binding.
0344:             */
0345:            public boolean existBinding(Component comp, String attr) {
0346:                if (isClone(comp)) {
0347:                    comp = (Component) comp.getAttribute(TEMPLATE);
0348:                }
0349:                if (_compBindingMap.containsKey(comp)) {
0350:                    Map attrMap = (Map) _compBindingMap.get(comp);
0351:                    return attrMap.containsKey(attr);
0352:                }
0353:                return false;
0354:            }
0355:
0356:            /** Whether use the default binding configuration.
0357:             */
0358:            public boolean isDefaultConfig() {
0359:                return _defaultConfig;
0360:            }
0361:
0362:            /** Whether use the default binding configuration.
0363:             */
0364:            public void setDefaultConfig(boolean b) {
0365:                _defaultConfig = b;
0366:            }
0367:
0368:            /** Bind a real bean object to the specified beanid. You might not need to call this method because this
0369:             * DataBinder would look up the variable via the {@link org.zkoss.zk.ui.Component#getVariable} method
0370:             * if it cannot find the specified bean via the given beanid.
0371:             *
0372:             * @param beanid The bean id used in data binding.
0373:             * @param bean The real bean object to be associated with the bean id.
0374:             */
0375:            public void bindBean(String beanid, Object bean) {
0376:                _beans.put(beanid, bean);
0377:            }
0378:
0379:            /** Load value from the data bean property to a specified attribute of the UI component.
0380:             * @param comp the UI component to be loaded value.
0381:             * @param attr the UI component attribute to be loaded value.
0382:             */
0383:            public void loadAttribute(Component comp, String attr) {
0384:                if (isTemplate(comp) || comp.getPage() == null) {
0385:                    return; //skip detached component
0386:                }
0387:                init();
0388:                Binding binding = getBinding(comp, attr);
0389:                if (binding != null) {
0390:                    binding.loadAttribute(comp);
0391:                }
0392:            }
0393:
0394:            /** Save value from a specified attribute of the UI component to a data bean property.
0395:             * @param comp the UI component used to save value into backend data bean.
0396:             * @param attr the UI component attribute used to save value into backend data bean.
0397:             */
0398:            public void saveAttribute(Component comp, String attr) {
0399:                if (isTemplate(comp) || comp.getPage() == null) {
0400:                    return; //skip detached component
0401:                }
0402:                init();
0403:                Binding binding = getBinding(comp, attr);
0404:                if (binding != null) {
0405:                    binding.saveAttribute(comp);
0406:                }
0407:            }
0408:
0409:            /** Load values from the data bean properties to all attributes of a specified UI component. 
0410:             * @param comp the UI component to be loaded value.
0411:             */
0412:            public void loadComponent(Component comp) {
0413:                if (isTemplate(comp) || comp.getPage() == null) {
0414:                    return; //skip detached component
0415:                }
0416:                init();
0417:                Collection bindings = getBindings(comp);
0418:                if (bindings != null) {
0419:                    loadAttrs(comp, bindings);
0420:                }
0421:
0422:                //load kids of this component
0423:                for (final Iterator it = comp.getChildren().iterator(); it
0424:                        .hasNext();) {
0425:                    loadComponent((Component) it.next()); //recursive
0426:                }
0427:            }
0428:
0429:            /** Save values from all attributes of a specified UI component to data bean properties.
0430:             * @param comp the UI component used to save value into backend data bean.
0431:             */
0432:            public void saveComponent(Component comp) {
0433:                if (isTemplate(comp) || comp.getPage() == null) {
0434:                    return; //skip detached component
0435:                }
0436:                init();
0437:                Collection bindings = getBindings(comp);
0438:                if (bindings != null) {
0439:                    saveAttrs(comp, bindings);
0440:                }
0441:
0442:                //save kids of this component
0443:                for (final Iterator it = comp.getChildren().iterator(); it
0444:                        .hasNext();) {
0445:                    saveComponent((Component) it.next()); //recursive
0446:                }
0447:            }
0448:
0449:            /** Load all value from data beans to UI components. */
0450:            public void loadAll() {
0451:                init();
0452:                for (final Iterator it = _compBindingMap.keySet().iterator(); it
0453:                        .hasNext();) {
0454:                    final Component comp = (Component) it.next();
0455:                    loadComponent(comp);
0456:                }
0457:            }
0458:
0459:            /** Save all values from UI components to beans. */
0460:            public void saveAll() {
0461:                init();
0462:                for (final Iterator it = _compBindingMap.keySet().iterator(); it
0463:                        .hasNext();) {
0464:                    final Component comp = (Component) it.next();
0465:                    saveComponent(comp);
0466:                }
0467:            }
0468:
0469:            private void loadAttrs(String expr, Collection attrs) {
0470:                for (final Iterator it = attrs.iterator(); it.hasNext();) {
0471:                    Binding binding = (Binding) it.next();
0472:                    Component comp = binding.getComponent();
0473:                    binding.loadAttribute(comp, expr);
0474:                }
0475:            }
0476:
0477:            private void loadAttrs(Component comp, Collection attrs) {
0478:                for (final Iterator it = attrs.iterator(); it.hasNext();) {
0479:                    Binding binding = (Binding) it.next();
0480:                    binding.loadAttribute(comp);
0481:                }
0482:            }
0483:
0484:            private void saveAttrs(Component comp, Collection attrs) {
0485:                for (final Iterator it = attrs.iterator(); it.hasNext();) {
0486:                    Binding binding = (Binding) it.next();
0487:                    binding.saveAttribute(comp);
0488:                }
0489:            }
0490:
0491:            //[0] expr, [1] loadWhenEvents, [2] saveWhenEvents, [3] access, [4] converter
0492:            protected Object[] loadPropertyAnnotation(Component comp,
0493:                    String propName, String bindName) {
0494:                ComponentCtrl compCtrl = (ComponentCtrl) comp;
0495:                Annotation ann = compCtrl.getAnnotation(propName, bindName);
0496:                if (ann != null) {
0497:                    final Map attrs = ann.getAttributes(); //(tag, tagExpr)
0498:                    List loadWhenEvents = null;
0499:                    List saveWhenEvents = null;
0500:                    String access = null;
0501:                    String converter = null;
0502:                    String expr = null;
0503:                    for (final Iterator it = attrs.entrySet().iterator(); it
0504:                            .hasNext();) {
0505:                        Map.Entry entry = (Map.Entry) it.next();
0506:                        String tag = (String) entry.getKey();
0507:                        String tagExpr = (String) entry.getValue();
0508:                        if ("save-when".equals(tag)) {
0509:                            saveWhenEvents = parseExpression(tagExpr, ",");
0510:                        } else if ("access".equals(tag)) {
0511:                            access = tagExpr;
0512:                        } else if ("converter".equals(tag)) {
0513:                            converter = tagExpr;
0514:                        } else if ("load-when".equals(tag)) {
0515:                            loadWhenEvents = parseExpression(tagExpr, ",");
0516:                        } else if ("value".equals(tag)) {
0517:                            expr = tagExpr;
0518:                        }
0519:                    }
0520:                    return new Object[] { expr, loadWhenEvents, saveWhenEvents,
0521:                            access, converter };
0522:                }
0523:                return new Object[5];
0524:            }
0525:
0526:            //late init
0527:            protected void init() {
0528:                if (!_init) {
0529:                    _init = true;
0530:
0531:                    // init CollectionItem
0532:                    initCollectionItem();
0533:
0534:                    //setup all added bindings
0535:                    final Set varnameSet = new HashSet();
0536:                    final LinkedHashSet toBeDetached = new LinkedHashSet();
0537:                    for (final Iterator it = _compBindingMap.entrySet()
0538:                            .iterator(); it.hasNext();) {
0539:                        final Entry me = (Entry) it.next();
0540:                        final Component comp = (Component) me.getKey();
0541:                        final Map attrMap = (Map) me.getValue();
0542:                        final Collection bindings = attrMap.values();
0543:
0544:                        //_var special case; meaning a template component
0545:                        if (attrMap.containsKey("_var")) {
0546:                            setupTemplateComponent(comp,
0547:                                    getComponentCollectionOwner(comp)); //setup as template components
0548:                            String varname = ((Binding) attrMap.get("_var"))
0549:                                    .getExpression();
0550:                            varnameSet.add(varname);
0551:                            comp.setAttribute(VARNAME, varname);
0552:                            setupBindingRenderer(comp); //setup binding renderer
0553:                            toBeDetached.add(comp);
0554:                        }
0555:
0556:                        if (bindings != null) {
0557:                            //construct the path dependant tree
0558:                            setupPathTree(bindings, varnameSet);
0559:
0560:                            //register save-when event
0561:                            registerSaveEvents(comp, bindings);
0562:
0563:                            //register load-when events
0564:                            registerLoadEvents(comp, bindings);
0565:                        }
0566:                    }
0567:
0568:                    //detach template components so they will not interfer the visual part
0569:                    for (final Iterator it = toBeDetached.iterator(); it
0570:                            .hasNext();) {
0571:                        final Component comp = (Component) it.next();
0572:                        comp.detach();
0573:                    }
0574:                }
0575:            }
0576:
0577:            private void initCollectionItem() {
0578:                addCollectionItem(Listitem.class.getName(),
0579:                        new ListitemCollectionItem());
0580:                addCollectionItem(Row.class.getName(), new RowCollectionItem());
0581:                addCollectionItem(Comboitem.class.getName(),
0582:                        new ComboitemCollectionItem());
0583:            }
0584:
0585:            /**
0586:             * Adds a CollectionItem for this comp.
0587:             * @see CollectionItem
0588:             * @since 3.0.0
0589:             */
0590:            public void addCollectionItem(String comp, CollectionItem decor) {
0591:                _collectionItemMap.put(comp, decor);
0592:            }
0593:
0594:            //get Collection owner of a given collection item.
0595:            private Component getComponentCollectionOwner(Component comp) {
0596:                CollectionItem decor = getBindingCollectionItem(comp);
0597:                return decor.getComponentCollectionOwner(comp);
0598:            }
0599:
0600:            /**
0601:             * Returns a CollectionItem by the comp accordingly.
0602:             * @see CollectionItem
0603:             * @since 3.0.0
0604:             */
0605:            protected CollectionItem getBindingCollectionItem(Component comp) {
0606:                String name = comp.getClass().getName();
0607:                if (comp instanceof  Listitem) {
0608:                    name = Listitem.class.getName();
0609:                } else if (comp instanceof  Row) {
0610:                    name = Row.class.getName();
0611:                }
0612:                CollectionItem decorName = (CollectionItem) _collectionItemMap
0613:                        .get(name);
0614:                if (decorName != null) {
0615:                    return decorName;
0616:                } else {
0617:                    throw new UiException(
0618:                            "Cannot find associated CollectionItem:" + comp);
0619:                }
0620:            }
0621:
0622:            //get Collection owner of a given collection item.
0623:            private Component getCollectionOwner(Component comp) {
0624:                if (isTemplate(comp)) {
0625:                    return (Component) comp.getAttribute(OWNER);
0626:                }
0627:                return getComponentCollectionOwner(comp);
0628:            }
0629:
0630:            //get associated clone of a given bean and template component
0631:            private Component getCollectionItem(Component comp, Object bean) {
0632:                Component owner = getCollectionOwner(comp);
0633:                CollectionItem decor = getBindingCollectionItem(comp);
0634:                final ListModel xmodel = decor.getModelByOwner(owner);
0635:                if (xmodel instanceof  BindingListModel) {
0636:                    final BindingListModel model = (BindingListModel) xmodel;
0637:                    int index = model.indexOf(bean);
0638:                    if (index >= 0) {
0639:                        return lookupClone(decor.getComponentAtIndexByOwner(
0640:                                owner, index), comp);
0641:                    }
0642:                }
0643:                return null;
0644:            }
0645:
0646:            //set the binding renderer for the template listitem component
0647:            private void setupBindingRenderer(Component comp) {
0648:                getBindingCollectionItem(comp).setupBindingRenderer(comp, this );
0649:            }
0650:
0651:            private void setupPathTree(Collection bindings, Set varnameSet) {
0652:                for (final Iterator it = bindings.iterator(); it.hasNext();) {
0653:                    final Binding binding = (Binding) it.next();
0654:                    String[] paths = binding.getPaths();
0655:                    for (int j = 0; j < paths.length; ++j) {
0656:                        final String path = (String) paths[j];
0657:                        _pathTree.addBinding(path, binding, varnameSet);
0658:                    }
0659:                }
0660:            }
0661:
0662:            private void registerSaveEvents(Component comp, Collection bindings) {
0663:                for (final Iterator it = bindings.iterator(); it.hasNext();) {
0664:                    final Binding binding = (Binding) it.next();
0665:                    binding.registerSaveEvents(comp);
0666:                }
0667:            }
0668:
0669:            private void registerLoadEvents(Component comp, Collection bindings) {
0670:                for (final Iterator it = bindings.iterator(); it.hasNext();) {
0671:                    final Binding binding = (Binding) it.next();
0672:                    binding.registerLoadEvents(comp);
0673:                }
0674:            }
0675:
0676:            //whether exists the specified bean in this DataBinder.
0677:            /* package */boolean existsBean(String beanid) {
0678:                return _beans.containsKey(beanid);
0679:            }
0680:
0681:            //get a bean by the beanid from this Data binder
0682:            /* package */Object getBean(String beanid) {
0683:                return _beans.get(beanid);
0684:            }
0685:
0686:            //set a bean into this Data binder
0687:            /* package */void setBean(String beanid, Object bean) {
0688:                _beans.put(beanid, bean);
0689:            }
0690:
0691:            /**
0692:             * Sets up the specified comp and its decendents to be as template (or not)
0693:             */
0694:            public void setupTemplateComponent(Component comp, Object owner) {
0695:                if (existsBindings(comp)) {
0696:                    if (comp.getAttribute(OWNER) != null) {
0697:                        comp.setAttribute(HASTEMPLATEOWNER, Boolean.TRUE); //owner is a template
0698:                    }
0699:                    comp.setAttribute(OWNER, owner);
0700:                }
0701:                List kids = comp.getChildren();
0702:                for (final Iterator it = kids.iterator(); it.hasNext();) {
0703:                    setupTemplateComponent((Component) it.next(), owner); //recursive
0704:                }
0705:            }
0706:
0707:            //parse token and return as a List of String
0708:            /* package */static List parseExpression(String expr,
0709:                    String separator) {
0710:                if (expr == null) {
0711:                    return null;
0712:                }
0713:                List results = new ArrayList(5);
0714:                while (true) {
0715:                    int j = expr.indexOf(separator);
0716:                    if (j < 0) {
0717:                        results.add(expr.trim());
0718:                        return results;
0719:                    }
0720:                    results.add(expr.substring(0, j).trim());
0721:
0722:                    if (expr.length() <= (j + 1)) {
0723:                        return results;
0724:                    }
0725:                    expr = expr.substring(j + 1);
0726:                }
0727:            }
0728:
0729:            //whether a component is a binding template rather than a real component
0730:            /* package */static boolean isTemplate(Component comp) {
0731:                return comp.getAttribute(OWNER) != null;
0732:            }
0733:
0734:            //whether a cloned component from the template.
0735:            /* package */static boolean isClone(Component comp) {
0736:                //bug #1813055  Multiple listboxes with same selectedItem causes NPE
0737:                return comp != null
0738:                        && (comp.getAttribute(TEMPLATE) instanceof  Component);
0739:            }
0740:
0741:            //whether has template owner (collection in collection)
0742:            /* package */static boolean hasTemplateOwner(Component comp) {
0743:                //bug #1813055  Multiple listboxes with same selectedItem causes NPE
0744:                return comp != null
0745:                        && (comp.getAttribute(HASTEMPLATEOWNER) != null);
0746:            }
0747:
0748:            //set a bean to SameNode Set 
0749:            /* package */void setBeanSameNodes(Object bean, Set set) {
0750:                _beanSameNodes.put(bean, set);
0751:            }
0752:
0753:            //get SameNode Set of the given bean
0754:            /* package */Set getBeanSameNodes(Object bean) {
0755:                return (Set) _beanSameNodes.get(bean);
0756:            }
0757:
0758:            //remove SameNode set of the given bean
0759:            /* package */Set removeBeanSameNodes(Object bean) {
0760:                return (Set) _beanSameNodes.remove(bean);
0761:            }
0762:
0763:            /** traverse the path nodes and return the final bean.
0764:             */
0765:            /* package */Object getBeanAndRegisterBeanSameNodes(
0766:                    Component comp, String path) {
0767:                return myGetBeanWithExpression(comp, path, true);
0768:            }
0769:
0770:            private Object getBeanWithExpression(Component comp, String path) {
0771:                return myGetBeanWithExpression(comp, path, false);
0772:            }
0773:
0774:            private Object myGetBeanWithExpression(Component comp, String path,
0775:                    boolean registerNode) {
0776:                Object bean = null;
0777:                BindingNode currentNode = _pathTree;
0778:                final List nodeids = parseExpression(path, ".");
0779:                final Iterator it = nodeids.iterator();
0780:                if (it != null && it.hasNext()) {
0781:                    String nodeid = (String) it.next();
0782:                    currentNode = (BindingNode) currentNode.getKidNode(nodeid);
0783:                    if (currentNode == null) {
0784:                        throw new UiException(
0785:                                "Cannot find the specified databind bean expression:"
0786:                                        + path);
0787:                    }
0788:                    bean = lookupBean(comp, nodeid);
0789:                    if (registerNode) {
0790:                        registerBeanNode(bean, currentNode);
0791:                    }
0792:                } else {
0793:                    throw new UiException(
0794:                            "Incorrect format of databind bean expression:"
0795:                                    + path);
0796:                }
0797:
0798:                while (bean != null && it.hasNext()) {
0799:                    String nodeid = (String) it.next();
0800:                    currentNode = (BindingNode) currentNode.getKidNode(nodeid);
0801:                    if (currentNode == null) {
0802:                        throw new UiException(
0803:                                "Cannot find the specified databind bean expression:"
0804:                                        + path);
0805:                    }
0806:                    bean = fetchValue(bean, currentNode, nodeid, registerNode);
0807:                }
0808:
0809:                return bean;
0810:            }
0811:
0812:            private Object fetchValue(Object bean, BindingNode node,
0813:                    String nodeid, boolean registerNode) {
0814:                if (bean != null) {
0815:                    try {
0816:                        bean = Fields.get(bean, nodeid);
0817:                    } catch (NoSuchMethodException ex) {
0818:                        //feature#1766905 Binding to Map
0819:                        if (bean instanceof  Map) {
0820:                            bean = ((Map) bean).get(nodeid);
0821:                        } else {
0822:                            throw UiException.Aide.wrap(ex);
0823:                        }
0824:                    }
0825:                }
0826:                if (registerNode) {
0827:                    registerBeanNode(bean, node);
0828:                }
0829:                return bean;
0830:            }
0831:
0832:            /* package */void setBeanAndRegisterBeanSameNodes(Component comp,
0833:                    Object val, Binding binding, String path,
0834:                    boolean autoConvert, Object rawval, List loadOnSaveInfos) {
0835:                Object orgVal = null;
0836:                Object bean = null;
0837:                BindingNode currentNode = _pathTree;
0838:                boolean refChanged = false; //wether this setting change the reference
0839:                String beanid = null;
0840:                final List nodeids = parseExpression(path, ".");
0841:                final List nodes = new ArrayList(nodeids.size());
0842:                final Iterator it = nodeids.iterator();
0843:                if (it != null && it.hasNext()) {
0844:                    beanid = (String) it.next();
0845:                    currentNode = (BindingNode) currentNode.getKidNode(beanid);
0846:                    if (currentNode == null) {
0847:                        throw new UiException(
0848:                                "Cannot find the specified databind bean expression:"
0849:                                        + path);
0850:                    }
0851:                    nodes.add(currentNode);
0852:                    bean = lookupBean(comp, beanid);
0853:                } else {
0854:                    throw new UiException(
0855:                            "Incorrect format of databind bean expression:"
0856:                                    + path);
0857:                }
0858:
0859:                if (!it.hasNext()) { //assign back to where bean is stored
0860:                    orgVal = bean;
0861:                    if (Objects.equals(orgVal, val)) {
0862:                        return; //same value, no need to do anything
0863:                    }
0864:                    if (existsBean(beanid)) {
0865:                        setBean(beanid, val);
0866:                    } else if (!setZScriptVariable(comp, beanid, val)) {
0867:                        comp.setVariable(beanid, val, false);
0868:                    }
0869:                    refChanged = true;
0870:                } else {
0871:                    if (bean == null) {
0872:                        return; //no bean to set value, skip
0873:                    }
0874:                    int sz = nodeids.size() - 2; //minus first and last beanid in path
0875:                    for (; bean != null && it.hasNext() && sz > 0; --sz) {
0876:                        beanid = (String) it.next();
0877:                        currentNode = (BindingNode) currentNode
0878:                                .getKidNode(beanid);
0879:                        if (currentNode == null) {
0880:                            throw new UiException(
0881:                                    "Cannot find the specified databind bean expression:"
0882:                                            + path);
0883:                        }
0884:                        nodes.add(currentNode);
0885:                        try {
0886:                            bean = Fields.get(bean, beanid);
0887:                        } catch (NoSuchMethodException ex) {
0888:                            //feature#1766905 Binding to Map
0889:                            if (bean instanceof  Map) {
0890:                                bean = ((Map) bean).get(beanid);
0891:                            } else {
0892:                                throw UiException.Aide.wrap(ex);
0893:                            }
0894:                        }
0895:                    }
0896:                    if (bean == null) {
0897:                        return; //no bean to set value, skip
0898:                    }
0899:                    beanid = (String) it.next();
0900:                    try {
0901:                        orgVal = Fields.get(bean, beanid);
0902:                        if (Objects.equals(orgVal, val)) {
0903:                            return; //same value, no need to do anything
0904:                        }
0905:                        Fields.set(bean, beanid, val, autoConvert);
0906:                    } catch (NoSuchMethodException ex) {
0907:                        //feature#1766905 Binding to Map
0908:                        if (bean instanceof  Map) {
0909:                            ((Map) bean).put(beanid, val);
0910:                        } else {
0911:                            throw UiException.Aide.wrap(ex);
0912:                        }
0913:                    } catch (ModificationException ex) {
0914:                        throw UiException.Aide.wrap(ex);
0915:                    }
0916:                    if (!isPrimitive(val) && !isPrimitive(orgVal)) { //val is a bean (null is not primitive)
0917:                        currentNode = (BindingNode) currentNode
0918:                                .getKidNode(beanid);
0919:                        if (currentNode == null) {
0920:                            throw new UiException(
0921:                                    "Cannot find the specified databind bean expression:"
0922:                                            + path);
0923:                        }
0924:                        nodes.add(currentNode);
0925:                        bean = orgVal;
0926:                        refChanged = true;
0927:                    }
0928:                }
0929:
0930:                if (val != null) {
0931:                    if (refChanged && !binding.isLoadable()
0932:                            && binding.isSavable()) { //the sameNodes should change accordingly.
0933:                        registerBeanNode(val, currentNode);
0934:                    }
0935:                    //20070309, Henri Chen: Tricky. 
0936:                    //When loading page, listbox.selectedItem == null. The _var Listitem will not be able to 
0937:                    //associate with the selectedItem (no way to associate via null bean). When end user then 
0938:                    //select one Listitem, we have to force such association.
0939:                    if (rawval instanceof  Component) {
0940:                        Binding varbinding = getBinding((Component) rawval,
0941:                                "_var");
0942:                        if (varbinding != null) {
0943:                            registerBeanNode(val, currentNode);
0944:                            getBeanAndRegisterBeanSameNodes((Component) rawval,
0945:                                    varbinding.getExpression());
0946:                        }
0947:                    }
0948:                }
0949:
0950:                //TODO:Henri Chen: Is it possible to make the loadOnSave event to be called once only for a
0951:                //setXxx. So avoid load a node several times?
0952:
0953:                //register "onLoadSave" listener to this component if have not done so.
0954:                if (!comp.isListenerAvailable("onLoadOnSave", true)) {
0955:                    comp.addEventListener("onLoadOnSave", _listener);
0956:                }
0957:
0958:                Object[] loadOnSaveInfo = new Object[] { this , currentNode,
0959:                        binding, (refChanged ? val : bean),
0960:                        Boolean.valueOf(refChanged), nodes, comp };
0961:                if (loadOnSaveInfos != null) {
0962:                    loadOnSaveInfos.add(loadOnSaveInfo);
0963:                } else {
0964:                    //do loadOnSave immediately
0965:                    Events.postEvent(new Event("onLoadOnSave", comp,
0966:                            loadOnSaveInfo));
0967:                }
0968:            }
0969:
0970:            private void registerBeanNode(Object bean, BindingNode node) {
0971:                if (isPrimitive(bean)) {
0972:                    return;
0973:                }
0974:                final Set nodeSameNodes = node.getSameNodes();
0975:                final Set binderSameNodes = getBeanSameNodes(bean);
0976:                //variable node(with _var) is special. Assume selectedItem then _var. 
0977:                //e.g. a Listitem but no selectedItem yet
0978:                if (node.isVar() && binderSameNodes == null) {
0979:                    return;
0980:                }
0981:
0982:                if (!nodeSameNodes.contains(bean)) {
0983:                    //remove the old bean
0984:                    for (final Iterator it = nodeSameNodes.iterator(); it
0985:                            .hasNext();) {
0986:                        final Object elm = it.next();
0987:                        if (!(elm instanceof  BindingNode)) {
0988:                            it.remove();
0989:                            removeBeanSameNodes(elm); //remove the binderSameNodes of the original bean
0990:                            break;
0991:                        }
0992:                    }
0993:                    //add the new bean if not null
0994:                    if (bean != null) {
0995:                        nodeSameNodes.add(bean);
0996:                    }
0997:                }
0998:
0999:                if (binderSameNodes == null) {
1000:                    if (bean != null) {
1001:                        setBeanSameNodes(bean, nodeSameNodes);
1002:                    }
1003:                } else {
1004:                    node.mergeAndSetSameNodes(binderSameNodes);
1005:                }
1006:
1007:            }
1008:
1009:            private boolean isPrimitive(Object bean) {
1010:                //String is deemed as primitive and null is not primitive
1011:                return (bean instanceof  String)
1012:                        || (bean != null && Primitives.toPrimitive(bean
1013:                                .getClass()) != null) || (bean instanceof  Date)
1014:                        || (bean instanceof  Number);
1015:            }
1016:
1017:            /** Sets the variable to all loaded interpreters, if it was defined in
1018:             * the interpreter.
1019:             *
1020:             * @return whether it is set to the interpreter
1021:             */
1022:            private boolean setZScriptVariable(Component comp, String beanid,
1023:                    Object val) {
1024:                //for all loaded interperter, assign val to beanid
1025:                boolean found = false;
1026:                final Namespace ns = comp.getNamespace();
1027:                for (final Iterator it = comp.getPage().getLoadedInterpreters()
1028:                        .iterator(); it.hasNext();) {
1029:                    final Interpreter ip = (Interpreter) it.next();
1030:                    if (ip instanceof  HierachicalAware) {
1031:                        final HierachicalAware ha = (HierachicalAware) ip;
1032:                        if (ha.containsVariable(ns, beanid)) {
1033:                            ha.setVariable(ns, beanid, val);
1034:                            found = true;
1035:                        }
1036:                    } else if (ip.containsVariable(beanid)) {
1037:                        ip.setVariable(beanid, val);
1038:                        found = true;
1039:                    }
1040:                }
1041:                return found;
1042:            }
1043:
1044:            /*package*/Object lookupBean(Component comp, String beanid) {
1045:                //fetch the bean object
1046:                Object bean = null;
1047:
1048:                //bug#1871833: Data binder should read "self".
1049:                if ("self".equals(beanid)) {
1050:                    return comp;
1051:                }
1052:                if (isClone(comp)) {
1053:                    bean = myLookupBean1(comp, beanid);
1054:                    if (bean != NA) {
1055:                        return bean;
1056:                    }
1057:                }
1058:                if (existsBean(beanid)) {
1059:                    bean = getBean(beanid);
1060:                } else if (beanid.startsWith("/")) { //a absolute component Path: // or /
1061:                    bean = Path.getComponent(beanid);
1062:                } else if (beanid.startsWith(".")) { //a relative component Path: ./ or ../
1063:                    bean = Path.getComponent(comp.getSpaceOwner(), beanid);
1064:                } else {
1065:                    final Page page = comp.getPage();
1066:                    if (page != null)
1067:                        bean = page.getZScriptVariable(comp.getNamespace(),
1068:                                beanid);
1069:                    if (bean == null)
1070:                        bean = comp.getVariable(beanid, false);
1071:                }
1072:                return bean;
1073:            }
1074:
1075:            //given a beanid and a template, return the associated bean
1076:            //return NA if cannot find it
1077:            private Object myLookupBean1(Component comp, String beanid) {
1078:                Map templatemap = (Map) comp.getAttribute(TEMPLATEMAP);
1079:                return myLookupBean2(beanid, templatemap);
1080:            }
1081:
1082:            private Object myLookupBean2(String beanid, Map templatemap) {
1083:                if (templatemap != null) {
1084:                    if (templatemap.containsKey(beanid)) { //got it
1085:                        return templatemap.get(beanid);
1086:                    } else { //search up the parent templatemap
1087:                        templatemap = (Map) templatemap.get(TEMPLATEMAP);
1088:                        return myLookupBean2(beanid, templatemap); //recursive
1089:                    }
1090:                }
1091:                return NA; //not available
1092:            }
1093:
1094:            //given a clone and a template, return the associated clone of that template.
1095:            /*package*/static Component lookupClone(Component srcClone,
1096:                    Component srcTemplate) {
1097:                if (isTemplate(srcTemplate)) {
1098:                    Map templatemap = (Map) srcClone.getAttribute(TEMPLATEMAP);
1099:                    return myLookupClone(srcTemplate, templatemap);
1100:                }
1101:                return null;
1102:            }
1103:
1104:            private static Component myLookupClone(Component srcTemplate,
1105:                    Map templatemap) {
1106:                if (templatemap != null) {
1107:                    if (templatemap.containsKey(srcTemplate)) { //got it
1108:                        return (Component) templatemap.get(srcTemplate);
1109:                    } else { //search up the parent templatemap
1110:                        templatemap = (Map) templatemap.get(TEMPLATEMAP);
1111:                        return myLookupClone(srcTemplate, templatemap); //recursive
1112:                    }
1113:                }
1114:                return null;
1115:            }
1116:
1117:            // Given parentNode, path, and level, return associate same kid nodes of parent
1118:            // a1.b.c -> a2.b.c, a3.b.c, ...
1119:            private Set getAssociateSameNodes(BindingNode parentNode,
1120:                    String path, int level) {
1121:                final List nodeids = DataBinder.parseExpression(path, ".");
1122:                final int sz = nodeids.size();
1123:                final List subids = nodeids.subList(sz - level, sz);
1124:
1125:                Object bean = null;
1126:                for (final Iterator it = parentNode.getSameNodes().iterator(); it
1127:                        .hasNext();) {
1128:                    Object obj = it.next();
1129:                    if (!(obj instanceof  BindingNode)) {
1130:                        bean = obj;
1131:                        break;
1132:                    }
1133:                }
1134:
1135:                //for each same node, find the associated kid node
1136:                final Set assocateSameNodes = new HashSet();
1137:                for (final Iterator it = parentNode.getSameNodes().iterator(); it
1138:                        .hasNext();) {
1139:                    //locate the associate kid node
1140:                    BindingNode currentNode = null;
1141:                    final Object obj = it.next();
1142:                    if (!(obj instanceof  BindingNode)
1143:                            || currentNode == parentNode) {
1144:                        continue;
1145:                    }
1146:
1147:                    currentNode = (BindingNode) obj;
1148:                    for (final Iterator itx = subids.iterator(); itx.hasNext();) {
1149:                        final String nodeid = (String) itx.next();
1150:                        currentNode = (BindingNode) currentNode
1151:                                .getKidNode(nodeid);
1152:                        if (currentNode == null) {
1153:                            break;
1154:                        }
1155:                    }
1156:
1157:                    if (currentNode != null) {
1158:                        if (!currentNode.isVar()) {
1159:                            assocateSameNodes.add(currentNode);
1160:                        } else { //a var node, specialcase, find the var root
1161:                            Component varRootComp = getVarRootComponent(currentNode);
1162:                            assocateSameNodes.add(new Object[] { currentNode,
1163:                                    varRootComp });
1164:                        }
1165:                    }
1166:                }
1167:                return assocateSameNodes;
1168:            }
1169:
1170:            private Component getVarRootComponent(BindingNode node) {
1171:                final BindingNode varRootNode = node.getRootNode(_pathTree);
1172:
1173:                Object bean = null;
1174:                for (final Iterator it = varRootNode.getSameNodes().iterator(); it
1175:                        .hasNext();) {
1176:                    Object obj = it.next();
1177:                    if (!(obj instanceof  BindingNode)) {
1178:                        bean = obj;
1179:                        break;
1180:                    }
1181:                }
1182:
1183:                Component comp = null;
1184:                for (final Iterator itx = varRootNode.getBindings().iterator(); itx
1185:                        .hasNext();) {
1186:                    Binding binding = (Binding) itx.next();
1187:                    if ("_var".equals(binding.getAttr())) {
1188:                        comp = binding.getComponent();
1189:                        break;
1190:                    }
1191:                }
1192:
1193:                return getCollectionItem(comp, bean);
1194:            }
1195:
1196:            private class LoadOnSaveEventListener implements  EventListener {
1197:                public LoadOnSaveEventListener() {
1198:                }
1199:
1200:                //-- EventListener --//
1201:                public void onEvent(Event event) {
1202:                    final Set walkedNodes = new HashSet(32);
1203:                    final Set loadedBindings = new HashSet(32 * 2);
1204:
1205:                    Object obj = event.getData();
1206:                    if (obj instanceof  List) {
1207:                        for (final Iterator it = ((List) obj).iterator(); it
1208:                                .hasNext();) {
1209:                            final Object[] data = (Object[]) it.next();
1210:                            doLoad(data, walkedNodes, loadedBindings);
1211:                        }
1212:                    } else {
1213:                        doLoad((Object[]) obj, walkedNodes, loadedBindings);
1214:                    }
1215:                }
1216:
1217:                private void doLoad(Object[] data, Set walkedNodes,
1218:                        Set loadedBindings) {
1219:                    if (!data[0].equals(DataBinder.this )) {
1220:                        return; //not for this DataBinder, skip
1221:                    }
1222:                    final BindingNode node = (BindingNode) data[1]; //to be loaded nodes
1223:                    final Binding savebinding = (Binding) data[2]; //to be excluded binding
1224:                    final Object bean = data[3]; //saved bean
1225:                    final boolean refChanged = ((Boolean) data[4])
1226:                            .booleanValue(); //whether bean itself changed
1227:                    final List nodes = (List) data[5]; //the complete nodes along the path to the node
1228:                    final Component savecomp = (Component) data[6]; //saved comp that trigger this load-on-save event
1229:                    if (savecomp != null) {
1230:                        loadAllNodes(bean, node, savecomp, savebinding,
1231:                                refChanged, nodes, walkedNodes, loadedBindings);
1232:                    }
1233:                }
1234:
1235:                /** Load all associated BindingNodes below the given nodes (depth first traverse).
1236:                 */
1237:                private void loadAllNodes(Object bean, BindingNode node,
1238:                        Component collectionComp, Binding savebinding,
1239:                        boolean refChanged, List nodes, Set walkedNodes,
1240:                        Set loadedBindings) {
1241:                    myLoadAllNodes(bean, node, collectionComp, walkedNodes,
1242:                            savebinding, loadedBindings, refChanged);
1243:
1244:                    //for each ancestor, find associated same nodes			
1245:                    if (!nodes.isEmpty()) {
1246:                        final String path = node.getPath();
1247:                        int level = 1;
1248:                        for (final ListIterator it = nodes.listIterator(nodes
1249:                                .size() - 1); it.hasPrevious(); ++level) {
1250:                            final BindingNode parentNode = (BindingNode) it
1251:                                    .previous();
1252:                            final Set associateSameNodes = getAssociateSameNodes(
1253:                                    parentNode, path, level);
1254:                            for (final Iterator itx = associateSameNodes
1255:                                    .iterator(); itx.hasNext();) {
1256:                                Object obj = itx.next();
1257:                                if (obj instanceof  BindingNode) {
1258:                                    BindingNode samenode = (BindingNode) obj;
1259:                                    myLoadAllNodes(bean, samenode,
1260:                                            collectionComp, walkedNodes,
1261:                                            savebinding, loadedBindings,
1262:                                            refChanged);
1263:                                } else {
1264:                                    BindingNode samenode = (BindingNode) ((Object[]) obj)[0];
1265:                                    Component varRootComp = (Component) ((Object[]) obj)[1];
1266:                                    myLoadAllNodes(bean, samenode, varRootComp,
1267:                                            walkedNodes, savebinding,
1268:                                            loadedBindings, refChanged);
1269:
1270:                                }
1271:                            }
1272:                        }
1273:                    }
1274:                }
1275:
1276:                private void myLoadAllNodes(Object bean, BindingNode node,
1277:                        Component collectionComp, Set walkedNodes,
1278:                        Binding savebinding, Set loadedBindings,
1279:                        boolean refChanged) {
1280:                    if (walkedNodes.contains(node)) {
1281:                        return; //already walked, skip
1282:                    }
1283:                    //mark as walked already
1284:                    walkedNodes.add(node);
1285:
1286:                    //the component might have been removed
1287:                    if (collectionComp == null) {
1288:                        return;
1289:                    }
1290:                    //loading
1291:                    collectionComp = loadBindings(bean, node, collectionComp,
1292:                            savebinding, loadedBindings, refChanged);
1293:
1294:                    for (final Iterator it = node.getKidNodes().iterator(); it
1295:                            .hasNext();) {
1296:                        final BindingNode kidnode = (BindingNode) it.next();
1297:                        final Object kidbean = fetchValue(bean, kidnode,
1298:                                kidnode.getNodeId(), true);
1299:                        myLoadAllNodes(kidbean, kidnode, collectionComp,
1300:                                walkedNodes, savebinding, loadedBindings, true); //recursive
1301:                    }
1302:
1303:                    for (final Iterator it = new ArrayList(node.getSameNodes())
1304:                            .iterator(); it.hasNext();) {
1305:                        final Object obj = it.next();
1306:                        if (obj instanceof  BindingNode) {
1307:                            final BindingNode samenode = (BindingNode) obj;
1308:                            if (node == samenode) {
1309:                                continue;
1310:                            }
1311:                            if (samenode.isVar()) { // -> var node
1312:                                //var node must traverse from the root 
1313:                                //even a root, must make sure the samebean (could be diff)
1314:                                //even the same bean, if a inner var root(collection in collection), not a real root
1315:                                if (!samenode.isRoot()
1316:                                        || !isSameBean(samenode, bean)
1317:                                        || samenode.isInnerCollectionNode()) {
1318:                                    continue;
1319:                                }
1320:                            } else if (node.isVar()
1321:                                    && !isSameBean(samenode, bean)) { //var -> !var, must same bean
1322:                                continue;
1323:                            }
1324:                            myLoadAllNodes(bean, samenode, collectionComp,
1325:                                    walkedNodes, savebinding, loadedBindings,
1326:                                    refChanged); //recursive
1327:                        }
1328:                    }
1329:                }
1330:
1331:                //return nearest collection item Component (i.e. Listitem)
1332:                private Component loadBindings(Object bean, BindingNode node,
1333:                        Component collectionComp, Binding savebinding,
1334:                        Set loadedBindings, boolean refChanged) {
1335:                    final Collection bindings = node.getBindings();
1336:                    for (final Iterator it = bindings.iterator(); it.hasNext();) {
1337:                        final Binding binding = (Binding) it.next();
1338:                        if (loadedBindings.contains(binding)) {
1339:                            continue;
1340:                        }
1341:                        loadedBindings.add(binding);
1342:
1343:                        // bug 1775051: a multiple selection Listbox. When onSelect and loadOnSave cause 
1344:                        // setSelectedItem (loading) to be called and cause deselection of other multiple 
1345:                        // selected items. Must skip such case.
1346:
1347:                        // save binding that cause this loadOnSave, no need to load again.
1348:                        if (binding == savebinding) {
1349:                            continue;
1350:                        }
1351:
1352:                        Component comp = binding.getComponent();
1353:                        if (isTemplate(comp)) { //a template component, locate the listitem
1354:                            Component clonecomp = null;
1355:                            if (isClone(collectionComp)) { //A listbox in listbox
1356:                                clonecomp = lookupClone(collectionComp, comp);
1357:                            } else {
1358:                                clonecomp = getCollectionItem(comp, bean);
1359:                            }
1360:                            if ("_var".equals(binding.getAttr())) {
1361:                                if (clonecomp == null) { //the comp is in another Listbox
1362:                                    clonecomp = getCollectionItem(comp, bean);
1363:                                }
1364:                                collectionComp = clonecomp;
1365:                            }
1366:                            comp = clonecomp;
1367:                        }
1368:
1369:                        if (refChanged) {
1370:                            binding.loadAttribute(comp);
1371:                        }
1372:                    }
1373:                    return collectionComp;
1374:                }
1375:
1376:                private boolean isSameBean(BindingNode node, Object bean) {
1377:                    final Collection bindings = node.getBindings();
1378:                    if (bindings.isEmpty()) {
1379:                        return true;
1380:                    }
1381:                    final Component comp = ((Binding) bindings.iterator()
1382:                            .next()).getComponent();
1383:                    if (isTemplate(comp)) {
1384:                        return true;
1385:                    }
1386:                    final Object nodebean = getBeanWithExpression(comp, node
1387:                            .getPath());
1388:                    return Objects.equals(nodebean, bean);
1389:                }
1390:            }
1391:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.