Source Code Cross Referenced for DomSynchronizer.java in  » IDE-Netbeans » visualweb.api.designer » org » netbeans » modules » visualweb » designer » jsf » 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 » IDE Netbeans » visualweb.api.designer » org.netbeans.modules.visualweb.designer.jsf 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003:         *
0004:         * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005:         *
0006:         * The contents of this file are subject to the terms of either the GNU
0007:         * General Public License Version 2 only ("GPL") or the Common
0008:         * Development and Distribution License("CDDL") (collectively, the
0009:         * "License"). You may not use this file except in compliance with the
0010:         * License. You can obtain a copy of the License at
0011:         * http://www.netbeans.org/cddl-gplv2.html
0012:         * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013:         * specific language governing permissions and limitations under the
0014:         * License.  When distributing the software, include this License Header
0015:         * Notice in each file and include the License file at
0016:         * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
0017:         * particular file as subject to the "Classpath" exception as provided
0018:         * by Sun in the GPL Version 2 section of the License file that
0019:         * accompanied this code. If applicable, add the following below the
0020:         * License Header, with the fields enclosed by brackets [] replaced by
0021:         * your own identifying information:
0022:         * "Portions Copyrighted [year] [name of copyright owner]"
0023:         *
0024:         * Contributor(s):
0025:         *
0026:         * The Original Software is NetBeans. The Initial Developer of the Original
0027:         * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028:         * Microsystems, Inc. All Rights Reserved.
0029:         *
0030:         * If you wish your version of this file to be governed by only the CDDL
0031:         * or only the GPL Version 2, indicate your decision by adding
0032:         * "[Contributor] elects to include this software in this distribution
0033:         * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034:         * single choice of license, a recipient has the option to distribute
0035:         * your version of this file under either the CDDL, the GPL Version 2 or
0036:         * to extend the choice of license to its licensees as provided above.
0037:         * However, if you add GPL Version 2 code and therefore, elected the GPL
0038:         * Version 2 license, then the option applies only if the new code is
0039:         * made subject to such option by the copyright holder.
0040:         */
0041:        package org.netbeans.modules.visualweb.designer.jsf;
0042:
0043:        import org.netbeans.modules.visualweb.api.designer.markup.MarkupService;
0044:        import org.netbeans.modules.visualweb.insync.CustomizerDisplayer;
0045:        import org.netbeans.modules.visualweb.insync.InSyncServiceProvider;
0046:        import org.netbeans.modules.visualweb.insync.faces.FacesPageUnit;
0047:        import org.netbeans.modules.visualweb.insync.markup.MarkupUnit;
0048:        import org.netbeans.modules.visualweb.insync.models.FacesModel;
0049:        import javax.faces.component.UIComponent;
0050:        import javax.swing.SwingUtilities;
0051:        import org.apache.xerces.dom.events.MutationEventImpl;
0052:        import org.openide.filesystems.FileObject;
0053:
0054:        import org.w3c.dom.DocumentFragment;
0055:        import org.w3c.dom.Element;
0056:        import org.w3c.dom.Node;
0057:        import org.w3c.dom.NodeList;
0058:        import org.w3c.dom.events.EventTarget;
0059:
0060:        import com.sun.rave.designtime.DesignBean;
0061:        import com.sun.rave.designtime.DesignContext;
0062:        import com.sun.rave.designtime.DesignEvent;
0063:        import com.sun.rave.designtime.DesignProperty;
0064:        import com.sun.rave.designtime.Position;
0065:        import com.sun.rave.designtime.event.DesignContextListener;
0066:        import com.sun.rave.designtime.markup.MarkupDesignBean;
0067:        import java.util.ArrayList;
0068:        import java.util.HashMap;
0069:        import java.util.List;
0070:        import java.util.Map;
0071:        import org.netbeans.modules.visualweb.designer.html.HtmlTag;
0072:        import org.openide.ErrorManager;
0073:        import org.w3c.dom.Attr;
0074:        import org.w3c.dom.Document;
0075:        import org.w3c.dom.NamedNodeMap;
0076:
0077:        /**
0078:         * XXX Moved from designer.
0079:         * <p>
0080:         * This class is responsible for updating the HTML DOM when changes in the master JSP DOM, or
0081:         * associated properties in Java files or JavaScript event occur.
0082:         * </p>
0083:         *
0084:         * @author Tor Norbye
0085:         */
0086:        class DomSynchronizer implements  /*DesignContextListener,*/Runnable,
0087:                org.w3c.dom.events.EventListener,
0088:                FacesDndSupport.UpdateSuspender {
0089:
0090:            static {
0091:                // XXX Very suspicious, why it is not more robust?
0092:                CustomizerDisplayer.setBatchListener(new BeanModifyListener());
0093:            }
0094:
0095:            private static final boolean DEBUG = false;
0096:            private static final int TYPE_NONE = 0;
0097:            private static final int TYPE_INSERT = 1;
0098:            private static final int TYPE_CHANGE = 2;
0099:            private static final int TYPE_DELETE = 3;
0100:            private static final int TYPE_REFRESH = 4;
0101:
0102:            /** The currently planned bean to be changed, inserted or deleted (see pendingEventType) */
0103:            private MarkupDesignBean pendingBean;
0104:            //    private WebForm webform;
0105:            private JsfForm jsfForm;
0106:            private org.w3c.dom.Document currentDOM;
0107:            private Node deleteParent;
0108:            //    private DesignContext context;
0109:
0110:            /** If true we're suspending all updates until setUpdatesSuspended is called to turn it off */
0111:            private boolean suspendUpdates;
0112:
0113:            /**
0114:             * If true there's a timeout pending - we've enqueued a Runnable on the Swing event dispatch
0115:             * thread
0116:             */
0117:            private boolean timeOutPending;
0118:
0119:            /** If not TYPE_NONE, the type of pending update we're waiting for */
0120:            private int pendingEventType = TYPE_NONE;
0121:
0122:            // XXX #112216.
0123:            private boolean inlineEditingUpdate;
0124:
0125:            /**
0126:             * Create a new DomSynchronizer for the given webform
0127:             */
0128:            public DomSynchronizer(JsfForm jsfForm) {
0129:                this .jsfForm = jsfForm;
0130:
0131:                currentDOM = jsfForm.getJspDom();
0132:
0133:                if (currentDOM == null) {
0134:                    //            jsfForm.getFacesModel().sync();
0135:                    jsfForm.syncModel();
0136:                    currentDOM = jsfForm.getJspDom();
0137:
0138:                    // XXX What was this good for?
0139:                    //                if (currentDOM == null) {
0140:                    //                    currentDOM = MarkupUnit.createEmptyDocument(true);
0141:                    //                }
0142:                }
0143:
0144:                if (currentDOM != null) {
0145:                    registerDomListeners();
0146:                }
0147:            }
0148:
0149:            //    /**
0150:            //     * Destroy this synchronizer - detach from listeners etc. when the object
0151:            //     * is no longer to be used
0152:            //     */
0153:            //    public void destroy() {
0154:            //        if (currentDOM != null) {
0155:            //            unregisterDomListeners();
0156:            //        }
0157:            //
0158:            ////        detachContext();
0159:            //        jsfForm.detachContext();
0160:            //    }
0161:
0162:            //    /**
0163:            //     * Detach this synchronizer from the current DesignContext, if any
0164:            //     */
0165:            //    public void detachContext() {
0166:            //        if (context != null) {
0167:            //            context.removeDesignContextListener(this);
0168:            //        }
0169:            //
0170:            //        context = null;
0171:            //    }
0172:            //
0173:            //    /**
0174:            //     * Attach the synchronizer to the given DesignContext.
0175:            //     * The reason this is done "lazily", not part of the constructor,
0176:            //     * is that if we try to access a DesignContext after startup
0177:            //     * and the context has an error, the FacesModel will provide a null
0178:            //     * DesignContext. We don't obtain it until there has been a successful
0179:            //     * sync.
0180:            //     */
0181:            //    public void attachContext(DesignContext context) {
0182:            //        if (this.context == context) {
0183:            //            return;
0184:            //        }
0185:            //
0186:            //        detachContext();
0187:            //        this.context = context;
0188:            //
0189:            //        if (context != null) {
0190:            //            context.addDesignContextListener(this);
0191:            //        }
0192:            //    }
0193:
0194:            // ----------- Implements DesignContextListener ---------------------------
0195:            public void instanceNameChanged(DesignBean designBean,
0196:                    String oldInstanceName) {
0197:            }
0198:
0199:            /**
0200:             * {@inheritDoc}
0201:             */
0202:            public void beanChanged(DesignBean designBean) {
0203:                // TODO We need to find furthest ancestor that renders its own children.
0204:                // It may be replicating this changed bean in multiple places, so needs
0205:                // to be consulted.
0206:                if (!(designBean instanceof  MarkupDesignBean)) {
0207:                    // Could be something like a rowset or select items list.
0208:                    // We can't know visual beans are affected by this (at least we can't yet;
0209:                    // perhaps when we do a databinding view we'll know more about component
0210:                    // connections via value binding etc.) so we need to do a global re-render
0211:                    // Special case: when batch updates are suspended we know we can ignore
0212:                    // non markup beans are modified. Otherwise, dropping a listbox would
0213:                    // also have a select items object added and modified which would result
0214:                    // in a refresh if we didn't do this. (Similarly for DataTable; the non-markup
0215:                    // TableDataModel would also force a refresh). But in both cases we know
0216:                    // that the model itself is associated with the suspended/batched markup bean.
0217:                    if (suspendUpdates) {
0218:                        requestRefresh();
0219:                    }
0220:
0221:                    return;
0222:                }
0223:
0224:                // Insert new version of component
0225:                MarkupDesignBean bean = (MarkupDesignBean) designBean;
0226:                bean = getRenderTarget(bean);
0227:                requestChange(bean);
0228:            }
0229:
0230:            public void beanCreated(DesignBean designBean) {
0231:                // TODO We need to find furthest ancestor that renders its own children.
0232:                // It may be replicating this changed bean in multiple places, so needs
0233:                // to be consulted.
0234:                if (!(designBean instanceof  MarkupDesignBean)) {
0235:                    // We only care about markup beans when beans
0236:                    // are created (the same is not true for beanChanged
0237:                    // since an existing non-markup DesignBean can be
0238:                    // bound to a visual bean in some way and can affect
0239:                    // rendering - for example, the items property of
0240:                    // a SelectItems non-markup bean
0241:                    return;
0242:                }
0243:
0244:                MarkupDesignBean bean = (MarkupDesignBean) designBean;
0245:                bean = getRenderTarget(bean);
0246:
0247:                // Instead of inserting a new component directly we're re-rendering
0248:                // a parent - this is a change, not an insert so we must delete the
0249:                // old markup
0250:                if (bean != designBean) {
0251:                    requestChange(bean);
0252:
0253:                    return;
0254:                } else {
0255:                    requestInsert(bean);
0256:                }
0257:            }
0258:
0259:            public void propertyChanged(DesignProperty prop, Object oldValue) {
0260:                DesignBean designBean = prop.getDesignBean();
0261:
0262:                if (!(designBean instanceof  MarkupDesignBean)) {
0263:                    // Could be something like a rowset or select items list.
0264:                    // We can't know visual beans are affected by this (at least we can't yet;
0265:                    // perhaps when we do a databinding view we'll know more about component
0266:                    // connections via value binding etc.) so we need to do a global re-render
0267:                    requestRefresh();
0268:
0269:                    return;
0270:                }
0271:
0272:                MarkupDesignBean bean = (MarkupDesignBean) designBean;
0273:
0274:                if (bean.getElement() == null) {
0275:                    // No element: the DesignBean does not correspond to a visual
0276:                    // component, or it might be one we're inserting; insync seems
0277:                    // to set some properties on these before actually inserting it
0278:                    // into the view hiearchy; we can safely ignore these requests
0279:                    // since the eventual DOM modification will cause a refresh.
0280:                    return;
0281:                }
0282:
0283:                bean = getRenderTarget(bean);
0284:                requestChange(bean);
0285:            }
0286:
0287:            public void beanDeleted(DesignBean designBean) {
0288:                if (!(designBean instanceof  MarkupDesignBean)) {
0289:                    // Bean deletion of non-markup beans should have no effect on the visible
0290:                    // beans since if they are bound to this non-markup bean in some way,
0291:                    // insync should go and remove that relationship, and that will show up as
0292:                    // a property change on the markup bean which we will handle
0293:                    return;
0294:                }
0295:
0296:                MarkupDesignBean bean = (MarkupDesignBean) designBean;
0297:                // XXX FIXME This can't work, the bean is supposed to be already removed from the tree.
0298:                // How come it was working before and based on what hack? (see getRenderTarget).
0299:                bean = getRenderTarget(bean);
0300:
0301:                if (bean != designBean) {
0302:                    // If the bean to be deleted is inside a parent that must be
0303:                    // re-rendered, we should -change- the ancestor, not delete it.
0304:                    requestChange(bean);
0305:                } else {
0306:                    requestDelete(bean);
0307:                }
0308:            }
0309:
0310:            public void contextChanged(DesignContext context) {
0311:                // The entire HTML needs to be replaced
0312:                requestRefresh();
0313:            }
0314:
0315:            public void beanMoved(DesignBean designBean, DesignBean oldParent,
0316:                    Position pos) {
0317:                if (!(designBean instanceof  MarkupDesignBean)) {
0318:                    // Position on non-markup beans never matter to the HTML rendered
0319:                    return;
0320:                } else if (!(oldParent instanceof  MarkupDesignBean)) {
0321:                    // This should never be possible - and we should make sure this is
0322:                    // prevented in the UI to move beans around in e.g. the app outline and
0323:                    // the design surface
0324:                    return;
0325:                }
0326:
0327:                MarkupDesignBean bean = (MarkupDesignBean) designBean;
0328:                bean = getRenderTarget(bean);
0329:
0330:                if (bean == null) {
0331:                    requestRefresh();
0332:                } else if (isBelow(oldParent, bean)) {
0333:                    // We've moving the bean from one position to another inside
0334:                    // the same encodes-children bean that we need to render
0335:                    // Nothing else to do
0336:                } else if (isBelow(bean, oldParent)) {
0337:                    bean = (MarkupDesignBean) oldParent;
0338:                } else {
0339:                    bean = (MarkupDesignBean) getCommonAncestor(bean, oldParent);
0340:                }
0341:
0342:                requestChange(bean);
0343:            }
0344:
0345:            public void contextActivated(DesignContext context) {
0346:                // No-op: No impact on the rendered HTML DOM
0347:            }
0348:
0349:            public void contextDeactivated(DesignContext context) {
0350:                // No-op: No impact on the rendered HTML DOM
0351:            }
0352:
0353:            public void eventChanged(DesignEvent event) {
0354:                // No-op: No impact on the rendered HTML DOM
0355:            }
0356:
0357:            public void beanContextActivated(DesignBean designBean) {
0358:                // No-op: No impact on the rendered HTML DOM
0359:            }
0360:
0361:            public void beanContextDeactivated(DesignBean designBean) {
0362:                // No-op: No impact on the rendered HTML DOM
0363:            }
0364:
0365:            // ------- Coalesce Events --------------------------------
0366:            // Queue up multiple simultaneous requests by tracking the "top level" bean that must
0367:            // be inserted or replaced.
0368:            // This is necessary because we often get "bursts" of activity; for example, when a component
0369:            // is dragged from a gridpanel to a form, we have a deletion under the grid panel,
0370:            // and addition under the form, and possibly multiple "set style property" calls.
0371:            // We keep a list of changed beans and new requests are checked
0372:            // for siblingness. Clients can also send hints to this code.
0373:
0374:            /**
0375:             * <p>
0376:             * Add the ability to turn off updates for a while. When the suspend parameter is true, suspend
0377:             * all updates to the DOM until the method is called again turning off the suspend.
0378:             * </p>
0379:             *
0380:             * <p>
0381:             * <b>NOTE:</b>: While updates are suspended, modifying any non-markup beans will NOT cause a
0382:             * global refresh (which is usually the case since we don't know how a non-markup bean will
0383:             * markup beans since there's no association map). When updates are suspended we're assuming
0384:             * that any non-markup beans we are notified of are associated with the markup bean whose
0385:             * updates are suspended.
0386:             * </p>
0387:             *
0388:             * <p>
0389:             * <b>NOTE:</b>: Calls to the batch facility CANNOT be nested!
0390:             * </p>
0391:             */
0392:            public void setUpdatesSuspended(MarkupDesignBean bean,
0393:                    boolean suspend) {
0394:                if (suspend) {
0395:                    assert !suspendUpdates;
0396:                    suspendUpdates = true;
0397:                } else {
0398:                    suspendUpdates = false;
0399:                    bean = getRenderTarget(bean);
0400:                    requestChange(bean);
0401:                }
0402:            }
0403:
0404:            /**
0405:             * Dispatch the right type of event to the update manager only on the dispatch thread.
0406:             * This is necessary because I don't do locking on the request variables like pendingEventType
0407:             */
0408:            private void requestUpdate(final int type,
0409:                    final MarkupDesignBean bean) {
0410:                if (!SwingUtilities.isEventDispatchThread()) {
0411:                    inlineEditingUpdate = jsfForm.isInlineEditing();
0412:                    SwingUtilities.invokeLater(new Runnable() {
0413:                        public void run() {
0414:                            requestUpdate(type, bean);
0415:                        }
0416:                    });
0417:
0418:                    return;
0419:                }
0420:
0421:                switch (type) {
0422:                case TYPE_INSERT:
0423:                    requestInsert(bean);
0424:
0425:                    break;
0426:
0427:                case TYPE_DELETE:
0428:                    requestDelete(bean);
0429:
0430:                    break;
0431:
0432:                case TYPE_CHANGE:
0433:                    requestChange(bean);
0434:
0435:                    break;
0436:
0437:                case TYPE_REFRESH:
0438:                    requestRefresh();
0439:
0440:                    break;
0441:
0442:                default:
0443:                    assert false;
0444:                }
0445:            }
0446:
0447:            //    /**
0448:            //     * Return true iff a refresh is pending
0449:            //     */
0450:            //    public boolean isRefreshPending() {
0451:            //        return pendingEventType == TYPE_REFRESH;
0452:            //    }
0453:
0454:            /**
0455:             * Return true iff any type of change is pending
0456:             */
0457:            public boolean isUpdatePending() {
0458:                return pendingEventType != TYPE_NONE;
0459:            }
0460:
0461:            /**
0462:             * Schedule a request to refresh the whole HTML DOM - this needs to be done after a synch() for
0463:             * example. This will coalesce existing events if necessary.
0464:             */
0465:            public void requestRefresh() {
0466:                if (!SwingUtilities.isEventDispatchThread()) {
0467:                    requestUpdate(TYPE_REFRESH, null);
0468:
0469:                    return;
0470:                }
0471:
0472:                if (!timeOutPending) { // XXX what about pending insert?
0473:                    pendingBean = null;
0474:                    pendingEventType = TYPE_REFRESH;
0475:                    timeOutPending = true;
0476:                    inlineEditingUpdate = jsfForm.isInlineEditing();
0477:                    SwingUtilities.invokeLater(this );
0478:                } else {
0479:                    // A refresh supercedes all other types of events since if we're going
0480:                    // to do a global refresh we don't need to worry about individual inserts
0481:                    // or changes - their new content will be rendered by the global recompute
0482:                    pendingBean = null;
0483:                    pendingEventType = TYPE_REFRESH;
0484:                }
0485:            }
0486:
0487:            /**
0488:             * <p>
0489:             * Plan to change the given bean. This schedules a request to change this bean, unless one is
0490:             * already pending; if it is, and the pending request includes the given bean position do
0491:             * nothing, if it does not but this request would cover the pending one, change the request,
0492:             * and if they are independent, leave both requests in place.
0493:             * </p>
0494:             *
0495:             * <p>
0496:             * <b>NOTE:</b> This method must be run on the event dispatch thread!
0497:             * </p>
0498:             */
0499:            public void requestChange(MarkupDesignBean bean) {
0500:                if (bean == null) {
0501:                    requestRefresh();
0502:
0503:                    return;
0504:                }
0505:
0506:                if (!SwingUtilities.isEventDispatchThread()) {
0507:                    requestUpdate(TYPE_CHANGE, bean);
0508:
0509:                    return;
0510:                }
0511:
0512:                if (!timeOutPending) {
0513:                    pendingBean = bean;
0514:                    pendingEventType = TYPE_CHANGE;
0515:                    timeOutPending = true;
0516:                    adjustRequestIfRoot();
0517:                    inlineEditingUpdate = jsfForm.isInlineEditing();
0518:                    SwingUtilities.invokeLater(this );
0519:                } else {
0520:                    if ((pendingEventType == TYPE_REFRESH)
0521:                            || (bean == pendingBean)
0522:                            || isBelow(bean, pendingBean)) {
0523:                        // The pending request will already take care of this bean, either because
0524:                        // it's going to render this or an ancestor node, or do a global refresh,
0525:                        // so we have nothing to worry about
0526:                        // In particular, don't change the type to TYPE_CHANGE even if bean==pendingBean;
0527:                        // if we have a pending insert then we still haven't inserted it so it's not
0528:                        // a change it's an insert; we'll just be inserting data that is now more
0529:                        // up to date!
0530:                        return;
0531:                    } else if (isBelow(pendingBean, bean)) {
0532:                        // We've now changed an anscestor of the already scheduled bean -
0533:                        // so just change the ancestor and the bean will be fine
0534:                        pendingBean = bean;
0535:                        pendingEventType = TYPE_CHANGE;
0536:                        adjustRequestIfRoot();
0537:                    } else {
0538:                        // They're in different parts of the tree. For now, just
0539:                        // re-render (change) their common ancestor. Later I can try to be smart and
0540:                        // look at their distances and perhaps just re-render each child -
0541:                        // this will be an advantage if you're just tweaking a couple of
0542:                        // leaves in a large tree. But it means I can't have a single
0543:                        // pendingBean field anymore, I need to maintain a list of pending nodes.
0544:                        pendingEventType = TYPE_CHANGE;
0545:                        pendingBean = (MarkupDesignBean) getCommonAncestor(
0546:                                bean, pendingBean);
0547:                        assert pendingBean != null;
0548:                        adjustRequestIfRoot();
0549:                    }
0550:                }
0551:            }
0552:
0553:            /**
0554:             * Check if the computed bean is at root level and if so change the
0555:             * current request into a refresh
0556:             */
0557:            private void adjustRequestIfRoot() {
0558:                // If the common ancestor has moved up to a fragment or document
0559:                // root we need to refresh globally
0560:                // refresh
0561:                //        RaveElement e = (RaveElement)pendingBean.getElement();
0562:                //        if (e.getRendered() != null) {
0563:                //            e = (RaveElement)e.getRendered();
0564:                //        }
0565:                Element e = pendingBean.getElement();
0566:                Element rendered = MarkupService
0567:                        .getRenderedElementForElement(e);
0568:                if (rendered != null) {
0569:                    e = rendered;
0570:                }
0571:
0572:                if ((e == null)
0573:                        || (e.getParentNode() == null)
0574:                        || (e.getParentNode().getNodeType() != Node.ELEMENT_NODE)
0575:                        || e.getTagName().equals(HtmlTag.BODY.name)) {
0576:                    // We need to refresh the whole document
0577:                    pendingBean = null;
0578:                    pendingEventType = TYPE_REFRESH;
0579:                }
0580:            }
0581:
0582:            /**
0583:             * <p>
0584:             * Plan to insert the given bean. This schedules a request to insert this bean, unless this
0585:             * needs to be coalesced with an existing pending event.
0586:             * </p>
0587:             *
0588:             * <p>
0589:             * <b>NOTE:</b> This method must be run on the event dispatch thread!
0590:             * </p>
0591:             */
0592:            public void requestInsert(MarkupDesignBean bean) {
0593:                if (!SwingUtilities.isEventDispatchThread()) {
0594:                    requestUpdate(TYPE_INSERT, bean);
0595:
0596:                    return;
0597:                }
0598:
0599:                if (!timeOutPending) {
0600:                    pendingBean = bean;
0601:                    pendingEventType = TYPE_INSERT;
0602:                    timeOutPending = true;
0603:                    adjustRequestIfRoot();
0604:                    inlineEditingUpdate = jsfForm.isInlineEditing();
0605:                    SwingUtilities.invokeLater(this );
0606:                } else {
0607:                    if ((pendingEventType == TYPE_REFRESH)
0608:                            || (bean == pendingBean) || // is bean==pendingBean even possible?
0609:                            isBelow(bean, pendingBean)) {
0610:                        // The pending request will already take care of this bean, either because
0611:                        // it's going to render this or an ancestor node, or do a global refresh,
0612:                        // so we have nothing to worry about
0613:                        return;
0614:
0615:                        /* This should not be possible -- you can't insert a bean
0616:                           where we already know its descendants!
0617:                           } else if (isBelow(pendingBean, bean)) {
0618:                         */
0619:                    } else {
0620:                        // They're in different parts of the tree. For now, just
0621:                        // re-render (change) their common ancestor. Later I can try to be smart and
0622:                        // look at their distances and perhaps just re-render each child -
0623:                        // this will be an advantage if you're just tweaking a couple of
0624:                        // leaves in a large tree. But it means I can't have a single
0625:                        // pendingBean field anymore, I need to maintain a list of pending nodes.
0626:                        pendingBean = (MarkupDesignBean) getCommonAncestor(
0627:                                bean, pendingBean);
0628:                        pendingEventType = TYPE_CHANGE;
0629:                        assert pendingBean != null;
0630:                        adjustRequestIfRoot();
0631:                    }
0632:                }
0633:            }
0634:
0635:            /**
0636:             * <p>
0637:             * Plan to remove the given bean. This schedules a request to remove this bean.
0638:             * </p>
0639:             *
0640:             * <p>
0641:             * <b>NOTE:</b> This method must be run on the event dispatch thread!
0642:             * </p>
0643:             */
0644:            public void requestDelete(MarkupDesignBean bean) {
0645:                if (!SwingUtilities.isEventDispatchThread()) {
0646:                    requestUpdate(TYPE_DELETE, bean);
0647:
0648:                    return;
0649:                }
0650:
0651:                if (!timeOutPending) {
0652:                    pendingBean = bean;
0653:                    pendingEventType = TYPE_DELETE;
0654:                    timeOutPending = true;
0655:                    adjustRequestIfRoot();
0656:                    inlineEditingUpdate = jsfForm.isInlineEditing();
0657:                    SwingUtilities.invokeLater(this );
0658:                } else {
0659:                    if (pendingEventType == TYPE_REFRESH) {
0660:                        return;
0661:                    } else if (bean == pendingBean) {
0662:                        pendingEventType = TYPE_DELETE; // no need to change something we're about to remove!
0663:
0664:                        return;
0665:                    } else if (isBelow(bean, pendingBean)) {
0666:                        // Will be handled by higher-up bean!
0667:                        return;
0668:                    } else if (isBelow(pendingBean, bean)) {
0669:                        pendingEventType = TYPE_DELETE;
0670:                        pendingBean = bean;
0671:                        adjustRequestIfRoot();
0672:                    } else {
0673:                        // They're in different parts of the tree. For now, just
0674:                        // re-render (change) their common ancestor. Later I can try to be smart and
0675:                        // look at their distances and perhaps just re-render each child -
0676:                        // this will be an advantage if you're just tweaking a couple of
0677:                        // leaves in a large tree. But it means I can't have a single
0678:                        // pendingBean field anymore, I need to maintain a list of pending nodes.
0679:                        pendingBean = (MarkupDesignBean) getCommonAncestor(
0680:                                bean, pendingBean);
0681:                        pendingEventType = TYPE_CHANGE;
0682:                        assert pendingBean != null;
0683:                        adjustRequestIfRoot();
0684:                    }
0685:                }
0686:            }
0687:
0688:            /**
0689:             * <p>
0690:             * The given bean has either had text inserted as a child, or has had its existing text
0691:             * changed.  Either way the text needs to be reflown. This should be called when we listen for
0692:             * the JSP DOM and see a change in nodes of the types Node.TEXT_NODE,  Node.CDATA_SECTION_NODE
0693:             * or Node.ENTITY_REFERENCE_NODE.
0694:             * </p>
0695:             *
0696:             * @param bean The bean whose text child has been modified
0697:             */
0698:            public void requestTextUpdate(MarkupDesignBean bean) {
0699:                beanChanged(bean);
0700:            }
0701:
0702:            /**
0703:             * Process pending changes
0704:             */
0705:            public void run() {
0706:                timeOutPending = false;
0707:                processUpdates();
0708:            }
0709:
0710:            /**
0711:             * Process all the changes that have been queued up
0712:             */
0713:            private void processUpdates() { // XXX Why is this called when we're editing the JSP doc?
0714:
0715:                if ((pendingEventType == TYPE_NONE) || suspendUpdates) {
0716:                    return;
0717:                }
0718:
0719:                // XXX #114813 The model could get destroyed inbetween.
0720:                if (!jsfForm.isModelValid()) {
0721:                    return;
0722:                }
0723:
0724:                MarkupDesignBean bean = pendingBean;
0725:                pendingBean = null;
0726:
0727:                int type = pendingEventType;
0728:                pendingEventType = TYPE_NONE;
0729:
0730:                if (DEBUG) {
0731:                    //            DocumentFragment html = jsfForm.getDomProvider().getHtmlDocumentFragment();
0732:                    //            DocumentFragment html = jsfForm.getHtmlDomFragment();
0733:                    Document html = jsfForm.getHtmlDom();
0734:                    if (html != null) {
0735:                        System.out.println("\nBefore delayed updates to bean "
0736:                                + bean
0737:                                + " the html is\n"
0738:                                + InSyncServiceProvider.get().getHtmlStream(
0739:                                        html));
0740:                    }
0741:                }
0742:
0743:                if (type == TYPE_CHANGE) {
0744:                    // Delete old version of component
0745:                    //            Node previouslyRendered = ((RaveElement)bean.getElement()).getRendered();
0746:                    Node previouslyRendered = MarkupService
0747:                            .getRenderedElementForElement(bean.getElement());
0748:
0749:                    //            if (!processDelete(bean)) {
0750:                    //                processRefresh();
0751:                    ////                webform.getPane().getPaneUI().modelChanged();
0752:                    //                jsfForm.modelChanged();
0753:                    //
0754:                    //                return;
0755:                    //            }
0756:                    //
0757:                    //            // Insert new version of component
0758:                    //            if (!processInsert(bean)) {
0759:                    //                processRefresh();
0760:                    ////                webform.getPane().getPaneUI().modelChanged();
0761:                    //                jsfForm.modelChanged();
0762:                    //
0763:                    //                return;
0764:                    //            }
0765:                    List<Element> changedElements = new ArrayList<Element>();
0766:                    if (!processUpdate(bean, changedElements)) {
0767:                        processRefresh();
0768:                        jsfForm.modelChanged();
0769:                        return;
0770:                    }
0771:
0772:                    deleteParent = null;
0773:
0774:                    //            Node rendered = ((RaveElement)bean.getElement()).getRendered();
0775:                    Node rendered = MarkupService
0776:                            .getRenderedElementForElement(bean.getElement());
0777:
0778:                    if (rendered != null) {
0779:                        //                if (rendered != previouslyRendered) {
0780:                        //                    Node parent = rendered.getParentNode();
0781:                        ////                    PageBox pageBox = webform.getPane().getPaneUI().getPageBox();
0782:                        ////                    pageBox.changed(rendered, parent, false);
0783:                        //                    jsfForm.nodeChanged(rendered, parent, false);
0784:                        //                } else {
0785:                        //                    // We've re-rendered but the bean-reference doesn't point
0786:                        //                    // to a new node.  This means that the component must have
0787:                        //                    // supplied bogus component references when rendering.
0788:                        //                    // Note: With wrong element referenced we're not well off anyway -
0789:                        //                    // the component won't be selectable!
0790:                        ////                    PageBox pageBox = webform.getPane().getPaneUI().getPageBox();
0791:                        ////                    webform.getPane().getPaneUI().modelChanged();
0792:                        //                    // XXX Now it means, that the node was updated, see the tryUpdateOriginalNode.
0793:                        //                    jsfForm.modelChanged();
0794:                        //                }
0795:                        // Now the original node could be reused (see the tryUpdateOriginalNode).
0796:                        Node parent = rendered.getParentNode();
0797:                        if (parent == null) {
0798:                            // XXX #117192 The node was most probably removed already (e.g. after undo).
0799:                            // Do nothing.
0800:                        } else {
0801:                            jsfForm
0802:                                    .nodeChanged(
0803:                                            rendered,
0804:                                            parent,
0805:                                            changedElements
0806:                                                    .toArray(new Element[changedElements
0807:                                                            .size()]));
0808:                        }
0809:                    } else if (previouslyRendered != null) {
0810:                        // It was just deleted - for example when you change a component by
0811:                        // switching off its "rendered" property
0812:                        //webform.getPane().getPaneUI().modelChanged();
0813:                        Node parent = previouslyRendered.getParentNode();
0814:                        //                PageBox pageBox = webform.getPane().getPaneUI().getPageBox();
0815:                        //                pageBox.removed(previouslyRendered, parent);
0816:                        jsfForm.nodeRemoved(previouslyRendered, parent);
0817:                    }
0818:                } else if (type == TYPE_INSERT) {
0819:                    // Insert new version of component
0820:                    if (!processInsert(bean)) {
0821:                        processRefresh();
0822:                        //                webform.getPane().getPaneUI().modelChanged();
0823:                        jsfForm.modelChanged();
0824:
0825:                        return;
0826:                    }
0827:
0828:                    //            Node rendered = ((RaveElement)bean.getElement()).getRendered();
0829:                    Node rendered = MarkupService
0830:                            .getRenderedElementForElement(bean.getElement());
0831:
0832:                    if (rendered != null) {
0833:                        Node parent = rendered.getParentNode();
0834:                        //                webform.getPane().getPaneUI().getPageBox().inserted(rendered, parent);
0835:                        jsfForm.nodeInserted(rendered, parent);
0836:                    }
0837:                } else if (type == TYPE_REFRESH) {
0838:                    // The entire HTML needs to be replaced
0839:                    processRefresh();
0840:
0841:                    //            if ((webform.getPane() != null) && (webform.getPane().getPaneUI() != null)) {
0842:                    //                webform.getPane().getPaneUI().modelChanged();
0843:                    //            }
0844:                    jsfForm.modelChanged();
0845:                } else if (type == TYPE_DELETE) {
0846:                    // Delete old version of component
0847:                    if (!processDelete(bean)) {
0848:                        processRefresh();
0849:                        //                webform.getPane().getPaneUI().modelChanged();
0850:                        jsfForm.modelChanged();
0851:
0852:                        return;
0853:                    }
0854:
0855:                    deleteParent = null;
0856:
0857:                    //            Node rendered = ((RaveElement)bean.getElement()).getRendered();
0858:                    Node rendered = MarkupService
0859:                            .getRenderedElementForElement(bean.getElement());
0860:
0861:                    if (rendered != null) {
0862:                        Node parent = rendered.getParentNode();
0863:                        //                webform.getPane().getPaneUI().getPageBox().removed(rendered, parent);
0864:                        jsfForm.nodeRemoved(rendered, parent);
0865:                    }
0866:                }
0867:
0868:                if (DEBUG) {
0869:                    //            DocumentFragment html = jsfForm.getDomProvider().getHtmlDocumentFragment();
0870:                    //            DocumentFragment html = jsfForm.getHtmlDomFragment();
0871:                    Document html = jsfForm.getHtmlDom();
0872:                    if (html != null) {
0873:                        System.out.println("\nAfter delayed updates to bean "
0874:                                + bean
0875:                                + " the html is\n"
0876:                                + InSyncServiceProvider.get().getHtmlStream(
0877:                                        html));
0878:                    }
0879:                }
0880:            }
0881:
0882:            private boolean processUpdate(MarkupDesignBean bean,
0883:                    List<Element> changedElements) {
0884:                // Deleting
0885:                //        if (!processDelete(bean)) {
0886:                //            return false;
0887:                //        }
0888:
0889:                Element element = bean.getElement();
0890:                Node rendered = MarkupService
0891:                        .getRenderedElementForElement(element);
0892:                if (rendered == null) {
0893:                    return false;
0894:                }
0895:
0896:                Node parent = rendered.getParentNode();
0897:                // Find leftmost node
0898:                Node curr = rendered.getPreviousSibling();
0899:                while (curr != null) {
0900:                    if (curr.getNodeType() == Node.ELEMENT_NODE) {
0901:                        Element xel = (Element) curr;
0902:                        if (MarkupUnit.getMarkupDesignBeanForElement(xel) == bean) {
0903:                            rendered = curr;
0904:                        } else {
0905:                            break;
0906:                        }
0907:                    }
0908:                    curr = curr.getPreviousSibling();
0909:                }
0910:
0911:                deleteParent = parent;
0912:
0913:                List<Node> originalNodes = new ArrayList<Node>();
0914:                Node before = null;
0915:                // TODO - I ought to assert here that the parent is in the
0916:                // HTML DOM I'm attached to
0917:                // Remove all the rendered nodes
0918:                while (rendered != null) {
0919:                    //            Node before = rendered.getNextSibling();
0920:                    before = rendered.getNextSibling();
0921:                    //            parent.removeChild(rendered);
0922:                    originalNodes.add(rendered);
0923:                    if (before == null) {
0924:                        break;
0925:                    }
0926:                    // See if I need to delete additional siblings. This is slightly
0927:                    // tricky because I can't just look at the next sibling. Components
0928:                    // may separate tags with text nodes that are associated with the
0929:                    // parent rather than the component being rendered; for example,
0930:                    // a component renderer may do this:
0931:                    //    writer.startElement(foo, component);
0932:                    //    writer.endElement(foo);
0933:                    //    writer.write("\n");
0934:                    //    writer.startElement(bar, component);
0935:                    //    ....
0936:                    // Note that we want to delete both foo and bar even though there's
0937:                    // a text node in between which is not associated with the component
0938:                    // since it's not within a startElement for "component", it's within
0939:                    // the element for the parent these children (foo and bar) are being
0940:                    // added to.
0941:                    // So, my strategy is to peek ahead and see if the next element
0942:                    // found is associated with the same DesignBean, and if so, delete
0943:                    // everything up to it.
0944:                    if (!moreElements(before, bean)) {
0945:                        break;
0946:                    }
0947:                    rendered = before;
0948:                }
0949:
0950:                // Inserting
0951:                //        if (!processInsert(bean)) {
0952:                //            return false;
0953:                //        }
0954:
0955:                // XXX TODO There is not needed webform here.
0956:                // Render the new element
0957:                // TODO - this should not necessarily have to involve FacesBeans!
0958:                // XXX Do not mark the new nodes as rendered for this case yet.
0959:                DocumentFragment df = jsfForm.renderMarkupDesignBean(bean,
0960:                        false);
0961:
0962:                // This seems to be not the correct place.
0963:                //        // XXX FIXME Is this correct here?
0964:                //        jsfForm.updateErrorsInComponent();
0965:
0966:                if (DEBUG) {
0967:                    System.out.println("Got fragment from insert render html: "
0968:                            + InSyncServiceProvider.get().getHtmlStream(df));
0969:                }
0970:
0971:                List<Node> newNodes = new ArrayList<Node>();
0972:
0973:                if (df != null) {
0974:                    // Insert nodes into the rendered dom
0975:                    NodeList nl = df.getChildNodes();
0976:                    int num = nl.getLength();
0977:
0978:                    if (num == 0) {
0979:                        // Rendered to nothing. This happens if you switch the Rendered attribute
0980:                        // to off for example - this comes in as a change request rather than
0981:                        // as a delete request - but we need to go and update the source element's
0982:                        // rendered reference since it would now be obsolete in the future
0983:                        // and we shouldn't attempt to use it
0984:                        //                ((RaveElement)bean.getElement()).setRendered(null);
0985:                        // XXX FIXME Modifying the data structure!
0986:                        MarkupService.setRenderedElementForElement(bean
0987:                                .getElement(), null);
0988:
0989:                        // XXX #112220 Remove the original nodes (they are not to be rendered now.
0990:                        for (Node originalNode : originalNodes) {
0991:                            parent.removeChild(originalNode);
0992:                        }
0993:
0994:                        return true;
0995:                    }
0996:
0997:                    // Can't remove from a NodeList while iterating over it - causes
0998:                    // surprises like null siblings. So we need to copy first.
0999:                    int n = nl.getLength();
1000:                    Node[] nodes = new Node[n];
1001:
1002:                    for (int i = 0; i < n; i++) {
1003:                        nodes[i] = nl.item(i);
1004:                    }
1005:
1006:                    for (int i = 0; i < n; i++) {
1007:                        Node nn = nodes[i];
1008:                        if (DEBUG) {
1009:                            if (nn != null) {
1010:                                System.out.println("next node is: "
1011:                                        + InSyncServiceProvider.get()
1012:                                                .getHtmlStream(nn));
1013:                                System.out.println("Fragment is now: "
1014:                                        + InSyncServiceProvider.get()
1015:                                                .getHtmlStream(df));
1016:                            }
1017:                        }
1018:
1019:                        if (nn != null) {
1020:                            assert !isJspNode(nn);
1021:                            //                    parent.insertBefore(nn, before);
1022:                            newNodes.add(nn);
1023:                        }
1024:                    }
1025:                } else {
1026:                    ErrorManager.getDefault().notify(
1027:                            ErrorManager.INFORMATIONAL,
1028:                            new NullPointerException(
1029:                                    "Null DocumentFragment for JsfForm, jsfForm="
1030:                                            + jsfForm)); // NOI18N
1031:                    return false;
1032:                }
1033:
1034:                if (originalNodes.size() == 1 && newNodes.size() == 1) {
1035:                    Node originalNode = originalNodes.get(0);
1036:                    Node newNode = newNodes.get(0);
1037:                    // XXX #110662 Doesn't work for inline editing (the newly created elements need to be used).
1038:                    // XXX #112216 Improved the check.
1039:                    try {
1040:                        if (!inlineEditingUpdate
1041:                                && tryUpdateOriginalNode(originalNode, newNode,
1042:                                        changedElements)) {
1043:                            // XXX The original nodes are updated, do not mark the new as rendered, they are not used.
1044:                            return true;
1045:                        } else {
1046:                            // XXX Clear possibly added elements.
1047:                            changedElements.clear();
1048:
1049:                            // XXX Mark the newly used nodes as rendered.
1050:                            MarkupService.markRenderedNodes(df);
1051:                            parent.replaceChild(newNode, originalNode);
1052:                        }
1053:                    } finally {
1054:                        inlineEditingUpdate = false;
1055:                    }
1056:                } else {
1057:                    // XXX Mark the newly used nodes as rendered.
1058:                    MarkupService.markRenderedNodes(df);
1059:                    for (Node toRemove : originalNodes) {
1060:                        parent.removeChild(toRemove);
1061:                    }
1062:                    for (Node toAdd : newNodes) {
1063:                        parent.insertBefore(toAdd, before);
1064:                    }
1065:                }
1066:
1067:                return true;
1068:            }
1069:
1070:            private static boolean tryUpdateOriginalNode(Node originalNode,
1071:                    Node newNode, List<Element> changedElements) {
1072:                if (originalNode.getNodeType() != newNode.getNodeType()) {
1073:                    return false;
1074:                }
1075:
1076:                // XXX #110845 It doesn't work for elements pointing to external forms (fragments, frames, etc.).
1077:                if (originalNode instanceof  Element
1078:                        && isExternalElement((Element) originalNode)) {
1079:                    return false;
1080:                }
1081:
1082:                if (originalNode.isEqualNode(newNode)) {
1083:                    return true;
1084:                }
1085:
1086:                // Update children
1087:                NodeList originalNodeChildren = originalNode.getChildNodes();
1088:                NodeList newNodeChildren = newNode.getChildNodes();
1089:                if (originalNodeChildren == null) {
1090:                    if (newNodeChildren != null) {
1091:                        return false;
1092:                    }
1093:                } else {
1094:                    if (newNodeChildren == null) {
1095:                        return false;
1096:                    }
1097:                    int originalNodeChildrenSize = originalNodeChildren
1098:                            .getLength();
1099:                    int newNodeChildrenSize = newNodeChildren.getLength();
1100:                    if (originalNodeChildrenSize != newNodeChildrenSize) {
1101:                        return false;
1102:                    }
1103:
1104:                    for (int i = 0; i < originalNodeChildrenSize; i++) {
1105:                        boolean childOK = tryUpdateOriginalNode(
1106:                                originalNodeChildren.item(i), newNodeChildren
1107:                                        .item(i), changedElements);
1108:                        if (!childOK) {
1109:                            return false;
1110:                        }
1111:                    }
1112:                }
1113:
1114:                // Update value.
1115:                String originalValue = originalNode.getNodeValue();
1116:                String newValue = newNode.getNodeValue();
1117:                if ((originalValue == null && newValue != null)
1118:                        || (originalValue != null && !originalValue
1119:                                .equals(newValue))) {
1120:                    originalNode.setNodeValue(newValue);
1121:                }
1122:
1123:                // Update attributes.
1124:                if (originalNode instanceof  Element
1125:                        && newNode instanceof  Element) {
1126:                    Element originalElement = (Element) originalNode;
1127:                    Element newElement = (Element) newNode;
1128:
1129:                    Map<String, Attr> originalMap = getAttributesMap(originalElement);
1130:                    Map<String, Attr> newMap = getAttributesMap(newElement);
1131:
1132:                    // Remove redundant attributes.
1133:                    for (String name : originalMap.keySet()) {
1134:                        if (newMap.containsKey(name)) {
1135:                            continue;
1136:                        }
1137:                        originalElement.removeAttribute(name);
1138:                        if (!changedElements.contains(originalElement)) {
1139:                            changedElements.add(originalElement);
1140:                        }
1141:                    }
1142:                    // Add/update the remaining attributes.
1143:                    for (String name : newMap.keySet()) {
1144:                        Attr newAttribute = newMap.get(name);
1145:                        Attr originalAttribute = originalMap.get(name);
1146:
1147:                        if (originalAttribute == null) {
1148:                            originalElement
1149:                                    .setAttributeNode((Attr) newAttribute
1150:                                            .cloneNode(false));
1151:                            if (!changedElements.contains(originalElement)) {
1152:                                changedElements.add(originalElement);
1153:                            }
1154:                        } else {
1155:                            String oldAttributeValue = originalAttribute
1156:                                    .getValue();
1157:                            String newAttributeValue = newAttribute.getValue();
1158:                            if (newAttributeValue == null) {
1159:                                if (oldAttributeValue != null) {
1160:                                    originalElement.removeAttribute(name);
1161:                                    if (!changedElements
1162:                                            .contains(originalElement)) {
1163:                                        changedElements.add(originalElement);
1164:                                    }
1165:                                }
1166:                            } else {
1167:                                if (!newAttributeValue
1168:                                        .equals(oldAttributeValue)) {
1169:                                    originalElement
1170:                                            .setAttributeNode((Attr) newAttribute
1171:                                                    .cloneNode(false));
1172:                                    if (!changedElements
1173:                                            .contains(originalElement)) {
1174:                                        changedElements.add(originalElement);
1175:                                    }
1176:                                }
1177:                            }
1178:                        }
1179:                    }
1180:                }
1181:
1182:                return true;
1183:            }
1184:
1185:            /** Determines whether the element represents external form (fragment, frame etc.). */
1186:            private static boolean isExternalElement(Element element) {
1187:                String tagName = element.getTagName();
1188:                if (HtmlTag.FSUBVIEW.name.equals(tagName)
1189:                        || HtmlTag.JSPINCLUDE.name.equals(tagName)
1190:                        || HtmlTag.JSPINCLUDEX.name.equals(tagName)
1191:                        || HtmlTag.IFRAME.name.equals(tagName)) {
1192:                    return true;
1193:                }
1194:                return false;
1195:            }
1196:
1197:            private static Map<String, Attr> getAttributesMap(Element element) {
1198:                NamedNodeMap attributes = element.getAttributes();
1199:                Map<String, Attr> attributesMap = new HashMap<String, Attr>();
1200:                int size = attributes == null ? 0 : attributes.getLength();
1201:                for (int i = 0; i < size; i++) {
1202:                    Node attr = attributes.item(i);
1203:                    if (attr instanceof  Attr) {
1204:                        Attr attribute = (Attr) attr;
1205:                        attributesMap.put(attribute.getName(), attribute);
1206:                    }
1207:                }
1208:                return attributesMap;
1209:            }
1210:
1211:            /**
1212:             * Check whether the given child is below the given parent.
1213:             *
1214:             * @param child The assumed child
1215:             * @param parent The assumed parent
1216:             *
1217:             * @return true iff child is the same as parent or a descendant of the parent
1218:             */
1219:            public boolean isBelow(DesignBean child, DesignBean parent) {
1220:                while (child != null) {
1221:                    if (child == parent) {
1222:                        return true;
1223:                    }
1224:
1225:                    child = child.getBeanParent();
1226:                }
1227:
1228:                return false;
1229:            }
1230:
1231:            /**
1232:             * Given two DesignBeans known to not be different and known to not be ancestors or descendants
1233:             * of each other, return a common ancestor DesignBean parent to both beans.
1234:             *
1235:             * @param a A DesignBean to locate a common ancestor with  <code>b</code> for
1236:             * @param b A DesignBean to locate a common ancestor with  <code>a</code> for
1237:             *
1238:             * @return A common ancestor of <code>a</code> and <code>b</code>
1239:             */
1240:            public DesignBean getCommonAncestor(DesignBean a, DesignBean b) {
1241:                a = a.getBeanParent();
1242:
1243:                while (a != null) {
1244:                    if (isBelow(b, a)) {
1245:                        return a;
1246:                    }
1247:
1248:                    a = a.getBeanParent();
1249:                }
1250:
1251:                return null;
1252:            }
1253:
1254:            // ------------ Methods to actually modify the HTML DOM ---------------------------
1255:
1256:            /**
1257:             * Render the given source JSP element and insert its HTML content in the right place in the
1258:             * HTML rendered DOM
1259:             *
1260:             * @param bean The bean to be rendered and inserted into the DOM
1261:             *
1262:             * @return true if everything works, otherwise false
1263:             */
1264:            private boolean processInsert(MarkupDesignBean bean) {
1265:                //        RaveElement element = (RaveElement)bean.getElement();
1266:                Element element = bean.getElement();
1267:
1268:                // Compute JSP DOM positions in the rendered markup
1269:                Node parent = element.getParentNode();
1270:                Node before = element.getNextSibling();
1271:
1272:                // Compute HTML DOM corresponding positions
1273:                if (before != null) {
1274:                    //            if (before instanceof RaveRenderNode) {
1275:                    //                before = ((RaveRenderNode)before).getRenderedNode();
1276:                    //            } else {
1277:                    //                before = null;
1278:                    //
1279:                    //                // TODO - how do we handle this? There could be whitespace text nodes
1280:                    //                // in the JSP source dom which do not get rendered in the HTML...
1281:                    //                // XXX Should I modify XhtmlText to do source<->model mapping too?
1282:                    //                // ... fall through ...
1283:                    //            }
1284:                    before = MarkupService.getRenderedNodeForNode(before);
1285:                }
1286:
1287:                if (before != null) {
1288:                    parent = before.getParentNode();
1289:                } else if (deleteParent != null) {
1290:                    parent = deleteParent;
1291:                } else {
1292:                    // Finding the parent could be tricky - but this will work because we walk
1293:                    // up past encodes-children components. For example, let's say you added a
1294:                    // <h:commandButton> component to a <h:gridPanel>.
1295:                    // You probably expect this to be inserted inside a <td> in the grid panel -
1296:                    // but the HTML element for the <h:gridPanel> is going to be the topmost
1297:                    // <table> tag!  But this is precisely why we skip up and re-render all
1298:                    // encodes-children components like gridpanel and data table. So you only
1299:                    // end up with cases where a parent is for example "<h:form>" and the
1300:                    // rendered dom parent insert position will be "<form>".
1301:                    //            while (parent instanceof RaveRenderNode) {
1302:                    //                Node renderedParent = ((RaveRenderNode)parent).getRenderedNode();
1303:                    while (parent != null) {
1304:                        Node renderedParent = MarkupService
1305:                                .getRenderedNodeForNode(parent);
1306:
1307:                        if (renderedParent != null) {
1308:                            parent = renderedParent;
1309:
1310:                            break;
1311:                        }
1312:
1313:                        parent = parent.getParentNode();
1314:                    }
1315:
1316:                    if (parent == null) {
1317:                        // You've inserted at the root of the document -- we should not allow that.
1318:                        //                assert false : "Can't insert at document root";
1319:                        ErrorManager.getDefault().notify(
1320:                                ErrorManager.INFORMATIONAL,
1321:                                new NullPointerException(
1322:                                        "There is no rendered parent for the bean, insert not possible, bean="
1323:                                                + bean)); // NOI18N
1324:                        return false;
1325:                    }
1326:                }
1327:
1328:                // This should not be the case, but we REALLY have to make sure this does not happen
1329:                // I can remove this check once I've fully debugged my new code
1330:                if ((parent != null) && isJspNode(parent)) {
1331:                    // XXX What this check supposed to mean?
1332:                    ErrorManager.getDefault().notify(
1333:                            ErrorManager.INFORMATIONAL,
1334:                            new IllegalStateException(
1335:                                    "Parent may not be a jsp node, parent="
1336:                                            + parent)); // NOI18N
1337:                    return false;
1338:                }
1339:
1340:                // XXX TODO There is not needed webform here.
1341:                //        FileObject markupFile = jsfForm.getFacesModel().getMarkupFile();
1342:                // Render the new element
1343:                // TODO - this should not necessarily have to involve FacesBeans!
1344:                //        DocumentFragment df = FacesSupport.renderHtml(markupFile, bean, !CssBox.noBoxPersistence);
1345:                //        DocumentFragment df = InSyncServiceProvider.get().renderHtml(markupFile, bean);
1346:                //        FacesModel facesModel = jsfForm.getFacesModel();
1347:                //        DocumentFragment df = FacesPageUnit.renderHtml(facesModel, bean);
1348:                DocumentFragment df = jsfForm.renderMarkupDesignBean(bean);
1349:
1350:                // XXX FIXME Is this correct here?
1351:                //        webform.updateErrorsInComponent();
1352:                jsfForm.updateErrorsInComponent();
1353:
1354:                if (DEBUG) {
1355:                    System.out.println("Got fragment from insert render html: "
1356:                            + InSyncServiceProvider.get().getHtmlStream(df));
1357:                }
1358:
1359:                if (df != null) {
1360:                    // Insert nodes into the rendered dom
1361:                    NodeList nl = df.getChildNodes();
1362:                    int num = nl.getLength();
1363:
1364:                    if (num == 0) {
1365:                        // Rendered to nothing. This happens if you switch the Rendered attribute
1366:                        // to off for example - this comes in as a change request rather than
1367:                        // as a delete request - but we need to go and update the source element's
1368:                        // rendered reference since it would now be obsolete in the future
1369:                        // and we shouldn't attempt to use it
1370:                        //                ((RaveElement)bean.getElement()).setRendered(null);
1371:                        // XXX FIXME Modifying the data structure!
1372:                        MarkupService.setRenderedElementForElement(bean
1373:                                .getElement(), null);
1374:
1375:                        return true;
1376:                    }
1377:
1378:                    // Can't remove from a NodeList while iterating over it - causes
1379:                    // surprises like null siblings. So we need to copy first.
1380:                    int n = nl.getLength();
1381:                    Node[] nodes = new Node[n];
1382:
1383:                    for (int i = 0; i < n; i++) {
1384:                        nodes[i] = nl.item(i);
1385:                    }
1386:
1387:                    for (int i = 0; i < n; i++) {
1388:                        Node nn = nodes[i];
1389:
1390:                        if (DEBUG) {
1391:                            if (nn != null) {
1392:                                System.out.println("next node is: "
1393:                                        + InSyncServiceProvider.get()
1394:                                                .getHtmlStream(nn));
1395:                                System.out.println("Fragment is now: "
1396:                                        + InSyncServiceProvider.get()
1397:                                                .getHtmlStream(df));
1398:                            }
1399:                        }
1400:
1401:                        if (nn != null) {
1402:                            assert !isJspNode(nn);
1403:
1404:                            parent.insertBefore(nn, before);
1405:                        }
1406:                    }
1407:                } else {
1408:                    //            assert false; // can this happen?
1409:                    ErrorManager.getDefault().notify(
1410:                            ErrorManager.INFORMATIONAL,
1411:                            //                    new NullPointerException("Null DocumentFragment for FacesModel, facesModel=" + facesModel)); // NOI18N
1412:                            new NullPointerException(
1413:                                    "Null DocumentFragment for JsfForm, jsfForm="
1414:                                            + jsfForm)); // NOI18N
1415:                    return false;
1416:                }
1417:
1418:                return true;
1419:            }
1420:
1421:            /**
1422:             * Return true iff the given node is in the source JSP node
1423:             */
1424:            private boolean isJspNode(Node n) {
1425:                // Determine if this node is in a DocumentFragment which means
1426:                // it's read only
1427:                while (n.getParentNode() != null) {
1428:                    n = n.getParentNode();
1429:                }
1430:
1431:                // We already have the isRendered property on XhtmlElements -- this should
1432:                // agree with this actual parent-walking check. Assuming they are, I can
1433:                // leave the faster isRendered check in the product once I feel confident
1434:                // about this.
1435:                //        assert (!(n instanceof RaveElement)) ||
1436:                //        (((RaveElement)n).isRendered() == (n != webform.getDom()));
1437:                // XXX One can't get out of this mess, what is actually supposed to be checked?
1438:                //        if (n instanceof Element 
1439:                //        && (!MarkupService.isRenderedNode(n) != (n == webform.getDom()))) {
1440:                //            ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL,
1441:                //                    new IllegalStateException(); // XXX What to log?
1442:                //        }
1443:
1444:                return n == jsfForm.getJspDom();
1445:            }
1446:
1447:            /**
1448:             * Remove the rendered HTML for the given bean from the HTML rendered DOM
1449:             *
1450:             * @param bean The bean whose markup should be deleted from the HTML DOM
1451:             *
1452:             * @return true if everything works, otherwise false
1453:             */
1454:            private boolean processDelete(MarkupDesignBean bean) {
1455:                //        RaveElement element = (RaveElement)bean.getElement();
1456:                //        Node rendered = element.getRendered();
1457:                Element element = bean.getElement();
1458:                Node rendered = MarkupService
1459:                        .getRenderedElementForElement(element);
1460:
1461:                if (rendered == null) {
1462:                    return false;
1463:                }
1464:
1465:                Node parent = rendered.getParentNode();
1466:
1467:                // Find leftmost node
1468:                Node curr = rendered.getPreviousSibling();
1469:
1470:                while (curr != null) {
1471:                    if (curr.getNodeType() == Node.ELEMENT_NODE) {
1472:                        //                RaveElement xel = (RaveElement)curr;
1473:                        Element xel = (Element) curr;
1474:
1475:                        //                if (xel.getDesignBean() == bean) {
1476:                        //                if (InSyncServiceProvider.get().getMarkupDesignBeanForElement(xel) == bean) {
1477:                        if (MarkupUnit.getMarkupDesignBeanForElement(xel) == bean) {
1478:                            rendered = curr;
1479:                        } else {
1480:                            break;
1481:                        }
1482:                    }
1483:
1484:                    curr = curr.getPreviousSibling();
1485:                }
1486:
1487:                deleteParent = parent;
1488:
1489:                // TODO - I ought to assert here that the parent is in the
1490:                // HTML DOM I'm attached to
1491:                // Remove all the rendered nodes
1492:                while (rendered != null) {
1493:                    Node before = rendered.getNextSibling();
1494:                    parent.removeChild(rendered);
1495:
1496:                    //assert deleted != null;
1497:                    if (before == null) {
1498:                        break;
1499:                    }
1500:
1501:                    // See if I need to delete additional siblings. This is slightly
1502:                    // tricky because I can't just look at the next sibling. Components
1503:                    // may separate tags with text nodes that are associated with the
1504:                    // parent rather than the component being rendered; for example,
1505:                    // a component renderer may do this:
1506:                    //    writer.startElement(foo, component);
1507:                    //    writer.endElement(foo);
1508:                    //    writer.write("\n");
1509:                    //    writer.startElement(bar, component);
1510:                    //    ....
1511:                    // Note that we want to delete both foo and bar even though there's
1512:                    // a text node in between which is not associated with the component
1513:                    // since it's not within a startElement for "component", it's within
1514:                    // the element for the parent these children (foo and bar) are being
1515:                    // added to.
1516:                    // So, my strategy is to peek ahead and see if the next element
1517:                    // found is associated with the same DesignBean, and if so, delete
1518:                    // everything up to it.
1519:                    if (!moreElements(before, bean)) {
1520:                        break;
1521:                    }
1522:
1523:                    rendered = before;
1524:                }
1525:
1526:                return true;
1527:            }
1528:
1529:            private boolean moreElements(Node node, MarkupDesignBean bean) {
1530:                while (node != null) {
1531:                    //            if (node instanceof RaveElement && (((RaveElement)node).getDesignBean() == bean)) {
1532:                    if ((node instanceof  Element)
1533:                    //            && (InSyncServiceProvider.get().getMarkupDesignBeanForElement((Element)node) == bean)) {
1534:                            && (MarkupUnit
1535:                                    .getMarkupDesignBeanForElement((Element) node) == bean)) {
1536:                        return true;
1537:                    }
1538:
1539:                    node = node.getNextSibling();
1540:                }
1541:
1542:                return false;
1543:            }
1544:
1545:            private void processRefresh() {
1546:                // XXX Revise this method, it might be meaningless here now.
1547:                //        webform.clearHtml();
1548:                //        jsfForm.getDomProvider().clearHtml();
1549:                jsfForm.clearHtml();
1550:
1551:                // Intentional side-effect -- cause re-generation of html dom
1552:                //        DocumentFragment html = jsfForm.getDomProvider().getHtmlDocumentFragment();
1553:                //        DocumentFragment html = jsfForm.getHtmlDomFragment();
1554:                // XXX This is a very ugly hack.
1555:                jsfForm.getHtmlBody();
1556:
1557:                if (DEBUG) {
1558:                    System.err.println("Refresh: Got new HTML: " + // NOI18N
1559:                            InSyncServiceProvider.get().getHtmlStream(
1560:                                    jsfForm.getHtmlDom()));
1561:                }
1562:
1563:                //        webform.getPane().hideCaret();
1564:                //        webform.getSelection().syncCaret();
1565:                //        webform.setGridMode(webform.getDocument().isGridMode()); // XXX
1566:                jsfForm.updateGridMode();
1567:            }
1568:
1569:            /**
1570:             * Given a bean in the DOM, duplicate its associated node tree and return this in a new
1571:             * DocumentFragment. Additionally, the returned fragment will be marked as the source nodes
1572:             * for the render fragment.
1573:             */
1574:            public DocumentFragment createSourceFragment(MarkupDesignBean bean) {
1575:                //return FacesSupport.renderHtml(webform, bean);
1576:                //        RaveElement element = (RaveElement)bean.getElement();
1577:                //        Node rendered = element.getRendered();
1578:                Element element = bean.getElement();
1579:                Node rendered = MarkupService
1580:                        .getRenderedElementForElement(element);
1581:
1582:                if (rendered == null) {
1583:                    return null;
1584:                }
1585:
1586:                org.w3c.dom.Document doc = jsfForm.getJspDom();
1587:                DocumentFragment fragment = doc.createDocumentFragment();
1588:
1589:                // Duplicate and move all the nodes in the HTML DOM into the new fragment
1590:                while (rendered != null) {
1591:                    Node before = rendered.getNextSibling();
1592:                    Node n = doc.importNode(rendered, true);
1593:                    fragment.appendChild(n);
1594:                    //            ((RaveRenderNode)n).setJspx(false); // XXX Gotta do this recursively. How?
1595:                    MarkupService.setJspxNode(n, false);
1596:
1597:                    MarkupService.markRendered(n, rendered);
1598:
1599:                    if (before == null) {
1600:                        break;
1601:                    }
1602:
1603:                    //            if (before instanceof RaveRenderNode) {
1604:                    //                RaveRenderNode rn = (RaveRenderNode)before;
1605:                    //
1606:                    //                if (rn.getSourceNode() != element) {
1607:                    //                    break;
1608:                    //                }
1609:                    //            } else {
1610:                    //                break;
1611:                    //            }
1612:                    Node sourceNode = MarkupService
1613:                            .getSourceNodeForNode(before);
1614:                    if (sourceNode != null && sourceNode != element) {
1615:                        break;
1616:                    }
1617:
1618:                    rendered = before;
1619:                }
1620:
1621:                return fragment;
1622:            }
1623:
1624:            /**
1625:             * Locate the most distant renders-children ancestor of this bean (if any) and return it -
1626:             * otherwise return the bean itself. This is used to compute the leaf-most node in the tree we
1627:             * need to re-render the HTML for. For example, think of a datagrid containin a single command
1628:             * button. When this button is updated, the parent will re-generate this button in multiple
1629:             * table cells. Thus we need to find any containing datagrid (or more generally, any component
1630:             * which has rendersChildren set, since these can potentially replicate the children.)
1631:             */
1632:            private MarkupDesignBean getRenderTarget(MarkupDesignBean bean) {
1633:                DesignBean curr = bean;
1634:
1635:                for (; curr != null; curr = curr.getBeanParent()) {
1636:                    if (!(curr instanceof  MarkupDesignBean)) {
1637:                        break;
1638:                    }
1639:
1640:                    MarkupDesignBean b = (MarkupDesignBean) curr;
1641:                    Object instance = curr.getInstance();
1642:
1643:                    if (instance instanceof  UIComponent) {
1644:                        //                ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
1645:                        //            	try {
1646:                        //                    Thread.currentThread().setContextClassLoader(InSyncServiceProvider.get().getContextClassLoader(b));
1647:                        // See #6475512. Not needed anymore.
1648:                        //                    try {
1649:                        if (InSyncServiceProvider
1650:                                .isComponentRendersChildren(curr)) {
1651:                            bean = b;
1652:
1653:                            // Can't break here - there could be an outer
1654:                            // renders-children parent
1655:                        }
1656:                        //                    } catch (NullPointerException ex) { // XXX Catching runtime exception is a wrong solution.
1657:                        //                        // XXX #6465131 Temporary workaround to find out what is going on, see the bug.
1658:                        //                        IllegalStateException ise = new IllegalStateException(
1659:                        //                                "NPE occured when called UIComponent.getRendersChildren() on instance=" + instance, ex); // NOI18N
1660:                        //                        ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ise);
1661:                        //                         // XXX #6475481 Force refresh in that case.
1662:                        //                        return null;
1663:                        //                    }
1664:                        //                } finally {    		
1665:                        //                    Thread.currentThread().setContextClassLoader(oldContextClassLoader);
1666:                        //            	}
1667:                    }
1668:                    // Events in the <head> section typically implies a re-render
1669:                    // on the whole document - e.g. background color changes, or
1670:                    // style sheet changes, or character encoding changes...
1671:                    //                RaveElement e = (RaveElement)b.getElement();
1672:                    //                if (e.getRendered() != null) {
1673:                    //                    e = (RaveElement)e.getRendered();
1674:                    //                }
1675:                    Element e = b.getElement();
1676:                    Element rendered = MarkupService
1677:                            .getRenderedElementForElement(e);
1678:                    if (rendered != null) {
1679:                        e = rendered;
1680:                    }
1681:
1682:                    String tag = e.getTagName();
1683:
1684:                    if (tag.equals(HtmlTag.HEAD.name)) {
1685:                        return null; // Force refresh
1686:                    } else if ((curr == bean) && tag.equals(HtmlTag.BODY.name)) {
1687:                        // Refresh document if we're trying to change the body itself, not some
1688:                        // child of it
1689:                        return null;
1690:                    } else if ((curr == bean)
1691:                            && ((e.getParentNode() == null) || (e
1692:                                    .getParentNode().getNodeType() != Node.ELEMENT_NODE))) {
1693:                        return null;
1694:                    }
1695:                }
1696:
1697:                return bean;
1698:            }
1699:
1700:            // -------- Source DOM listening ---------------------------------------------------
1701:            // We need to listen to the DOM for changes because updates like text modifications
1702:            // aren't communicated to us!
1703:            public void handleEvent(final org.w3c.dom.events.Event e) {
1704:                // I seem to get lots of useless mutation events - old value = new value
1705:                //            if (e instanceof org.w3c.dom.events.MutationEvent) {
1706:                //                org.w3c.dom.events.MutationEvent me =
1707:                //                    (org.w3c.dom.events.MutationEvent)e;
1708:                //                String old = me.getPrevValue();
1709:                //                String nw = me.getNewValue();
1710:                //                if (((old != null) && (nw != null) && (old.equals(nw)))) {
1711:                //                    if (debugevents) {
1712:                //                        Log.err.log("Event " + e + " on " + e.getTarget() + ": ignoring since prev-value==new-value");
1713:                //                    }
1714:                //                    return;
1715:                //                }
1716:                //            }
1717:                //            if (debugevents) {
1718:                //                Log.err.log("View.handleEvent(" + e + ")");
1719:                //                Log.err.log("type = " + e.getType());
1720:                //                Log.err.log("phase = " + e.getEventPhase());
1721:                //                Log.err.log("bubbles = " + e.getBubbles());
1722:                //                Log.err.log("target = " + e.getTarget());
1723:                //                if (e.getTarget() != null) {
1724:                //                    Log.err.log("target.parent = " + ((org.w3c.dom.Node)e.getTarget()).getParentNode());
1725:                //                    if (((org.w3c.dom.Node)e.getTarget()).getParentNode() != null) {
1726:                //                        Log.err.log("target.parent.parent = " + ((org.w3c.dom.Node)e.getTarget()).getParentNode().getParentNode());
1727:                //                    }
1728:                //                }
1729:                //                Log.err.log("target = " + e.getTarget().getClass().getName());
1730:                //                Log.err.log("currtarget = " + e.getCurrentTarget());
1731:                //                Log.err.log("currtarget = " + e.getCurrentTarget().getClass().getName());
1732:                //                if (e instanceof org.w3c.dom.events.MutationEvent) {
1733:                //                    org.w3c.dom.events.MutationEvent me =
1734:                //                        (org.w3c.dom.events.MutationEvent)e;
1735:                //                    Log.err.log("getAttrName = " + me.getAttrName());
1736:                //                    Log.err.log("attrchange = " + me.getAttrChange());
1737:                //                    Log.err.log("newvalue = " + me.getNewValue());
1738:                //                    Log.err.log("prevvalue = " + me.getPrevValue());
1739:                //                    Log.err.log("relatednode = " + me.getRelatedNode());
1740:                //                    if (me.getRelatedNode() != null) {
1741:                //                        Log.err.log("relatednode.parent = " + me.getRelatedNode().getParentNode());
1742:                //                    }
1743:                //                }
1744:                //            }
1745:                if (e.getType().equals(MarkupUnit.DOM_DOCUMENT_REPLACED)) {
1746:                    updateDomListeners();
1747:
1748:                    // Ensure that the caret is in the new DOM
1749:                    //            DesignerPane pane = webform.getPane();
1750:                    //
1751:                    //            if (pane != null) {
1752:                    //                if (pane.getCaret() != null) {
1753:                    //                    pane.getCaret().detachDom();
1754:                    //
1755:                    //                    //pane.setCaret(null);
1756:                    //                }
1757:                    //
1758:                    //                //                pane.showCaretAtBeginning();
1759:                    //            }
1760:                    jsfForm.documentReplaced();
1761:
1762:                    return;
1763:                }
1764:
1765:                Node node = (org.w3c.dom.Node) e.getTarget();
1766:
1767:                // Text node or entity node changes should get translated
1768:                // into a change event on their surrounding element...
1769:                // XXX I could possibly handle to rebreak only
1770:                // the LineBreakGroup.... That would save work -ESPECIALLY-
1771:                // for text right within the <body> tag... but optimize that
1772:                // later
1773:                if (!(node instanceof  Element)
1774:                        || ((Element) node).getTagName()
1775:                                .equals(HtmlTag.BR.name)) { // text, cdata, entity, ...
1776:                    node = node.getParentNode();
1777:
1778:                    if (node instanceof  Element) {
1779:                        //                MarkupDesignBean bean = ((RaveElement)node).getDesignBean();
1780:                        MarkupDesignBean bean = InSyncServiceProvider.get()
1781:                                .getMarkupDesignBeanForElement((Element) node);
1782:
1783:                        if (bean != null) {
1784:                            requestTextUpdate((MarkupDesignBean) bean);
1785:
1786:                            return;
1787:                        }
1788:                    }
1789:                }
1790:            }
1791:
1792:            // -------- Listener Registration ---------------------------------------------------
1793:            private void registerDomListeners() {
1794:                if (currentDOM instanceof  EventTarget) {
1795:                    EventTarget target = (org.w3c.dom.events.EventTarget) currentDOM;
1796:                    target.addEventListener(
1797:                            MutationEventImpl.DOM_ATTR_MODIFIED, this , false);
1798:
1799:                    /* This event seems to be redundant.
1800:                       target.addEventListener(MutationEventImpl.DOM_SUBTREE_MODIFIED, this, false);
1801:                     */
1802:                    target.addEventListener(
1803:                            MutationEventImpl.DOM_NODE_INSERTED, this , false);
1804:                    target.addEventListener(
1805:                            MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT,
1806:                            this , false);
1807:                    target.addEventListener(MutationEventImpl.DOM_NODE_REMOVED,
1808:                            this , false);
1809:                    target.addEventListener(
1810:                            MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
1811:                            this , false);
1812:                    target.addEventListener(
1813:                            MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
1814:                            this , false);
1815:
1816:                    target.addEventListener(MarkupUnit.DOM_DOCUMENT_REPLACED,
1817:                            this , false);
1818:                }
1819:            }
1820:
1821:            public void unregisterDomListeners() {
1822:                if (currentDOM instanceof  EventTarget) {
1823:                    EventTarget target = (org.w3c.dom.events.EventTarget) currentDOM;
1824:                    target.removeEventListener(
1825:                            MutationEventImpl.DOM_ATTR_MODIFIED, this , false);
1826:
1827:                    /* This event seems to be redundant.
1828:                       target.removeEventListener(MutationEventImpl.DOM_SUBTREE_MODIFIED, this, false);
1829:                     */
1830:                    target.removeEventListener(
1831:                            MutationEventImpl.DOM_NODE_INSERTED, this , false);
1832:                    target.removeEventListener(
1833:                            MutationEventImpl.DOM_NODE_INSERTED_INTO_DOCUMENT,
1834:                            this , false);
1835:                    target.removeEventListener(
1836:                            MutationEventImpl.DOM_NODE_REMOVED, this , false);
1837:                    target.removeEventListener(
1838:                            MutationEventImpl.DOM_NODE_REMOVED_FROM_DOCUMENT,
1839:                            this , false);
1840:                    target.removeEventListener(
1841:                            MutationEventImpl.DOM_CHARACTER_DATA_MODIFIED,
1842:                            this , false);
1843:
1844:                    target.removeEventListener(
1845:                            MarkupUnit.DOM_DOCUMENT_REPLACED, this , false);
1846:                }
1847:            }
1848:
1849:            /**
1850:             * The underlying DOM which manages the document content has changed. This might for example
1851:             * happen if the document source is edited and reparsed.
1852:             */
1853:            public void updateDomListeners() {
1854:                boolean wasDifferent = false;
1855:
1856:                if (currentDOM != jsfForm.getJspDom()) {
1857:                    wasDifferent = true;
1858:
1859:                    if (currentDOM != null) {
1860:                        unregisterDomListeners();
1861:                    }
1862:
1863:                    currentDOM = jsfForm.getJspDom();
1864:
1865:                    if (currentDOM == null) {
1866:                        return;
1867:                    }
1868:                }
1869:
1870:                //        fireDomChangedUpdate();
1871:                if (wasDifferent) {
1872:                    registerDomListeners();
1873:                }
1874:
1875:                //        webform.setGridMode(isGridMode());
1876:            }
1877:
1878:            /** Impl <code>UpdateSuspender</code>. */
1879:            public void setSuspended(MarkupDesignBean markupDesignBean,
1880:                    boolean suspend) {
1881:                setUpdatesSuspended(markupDesignBean, suspend);
1882:            }
1883:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.