Source Code Cross Referenced for AbstractTree.java in  » J2EE » wicket » wicket » extensions » markup » html » tree » 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 » J2EE » wicket » wicket.extensions.markup.html.tree 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * $Id$ $Revision$ $Date$
0003:         * 
0004:         * ==============================================================================
0005:         * Licensed under the Apache License, Version 2.0 (the "License"); you may not
0006:         * use this file except in compliance with the License. You may obtain a copy of
0007:         * the License at
0008:         * 
0009:         * http://www.apache.org/licenses/LICENSE-2.0
0010:         * 
0011:         * Unless required by applicable law or agreed to in writing, software
0012:         * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
0013:         * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
0014:         * License for the specific language governing permissions and limitations under
0015:         * the License.
0016:         */
0017:        package wicket.extensions.markup.html.tree;
0018:
0019:        import java.io.Serializable;
0020:        import java.util.ArrayList;
0021:        import java.util.Collections;
0022:        import java.util.Enumeration;
0023:        import java.util.HashMap;
0024:        import java.util.Iterator;
0025:        import java.util.List;
0026:        import java.util.Map;
0027:
0028:        import javax.swing.event.TreeModelEvent;
0029:        import javax.swing.event.TreeModelListener;
0030:        import javax.swing.tree.TreeModel;
0031:        import javax.swing.tree.TreeNode;
0032:
0033:        import wicket.Component;
0034:        import wicket.ajax.AjaxRequestTarget;
0035:        import wicket.behavior.HeaderContributor;
0036:        import wicket.markup.MarkupStream;
0037:        import wicket.markup.html.WebMarkupContainer;
0038:        import wicket.markup.html.panel.Panel;
0039:        import wicket.markup.html.tree.Tree;
0040:        import wicket.model.IDetachable;
0041:        import wicket.model.IModel;
0042:        import wicket.model.Model;
0043:        import wicket.util.string.AppendingStringBuffer;
0044:
0045:        /**
0046:         * This class encapsulates the logic for displaying and (partial) updating the
0047:         * tree. Actual presentation is out of scope of this class. User should derive
0048:         * they own tree (if needed) from {@link DefaultAbstractTree} or {@link Tree}
0049:         * (recommended).
0050:         * 
0051:         * @author Matej Knopp
0052:         */
0053:        public abstract class AbstractTree extends Panel implements 
0054:                ITreeStateListener, TreeModelListener {
0055:
0056:            /**
0057:             * Interface for visiting individual tree items.
0058:             */
0059:            private static interface IItemCallback {
0060:                /**
0061:                 * Visits the tree item.
0062:                 * 
0063:                 * @param item
0064:                 *            the item to visit
0065:                 */
0066:                void visitItem(TreeItem item);
0067:            }
0068:
0069:            /**
0070:             * This class represents one row in rendered tree (TreeNode). Only TreeNodes
0071:             * that are visible (all their parent are expanded) have TreeItem created
0072:             * for them.
0073:             */
0074:            private final class TreeItem extends WebMarkupContainer {
0075:                /**
0076:                 * whether this tree item should also render it's children to response.
0077:                 * this is set if we need the whole subtree rendered as one component in
0078:                 * ajax response, so that we can replace it in one step (replacing
0079:                 * individual rows is very slow in javascript, therefore we replace the
0080:                 * whole subtree)
0081:                 */
0082:                private final static int FLAG_RENDER_CHILDREN = FLAG_RESERVED8;
0083:
0084:                private static final long serialVersionUID = 1L;
0085:
0086:                /**
0087:                 * tree item children - we need this to traverse items in correct order
0088:                 * when rendering
0089:                 */
0090:                private List children = null;
0091:
0092:                /** tree item level - how deep is this item in tree */
0093:                private int level;
0094:
0095:                /**
0096:                 * Construct.
0097:                 * 
0098:                 * @param id
0099:                 *            The component id
0100:                 * @param node
0101:                 *            tree node
0102:                 * @param level
0103:                 *            current level
0104:                 */
0105:                public TreeItem(String id, final TreeNode node, int level) {
0106:                    super (id, new Model((Serializable) node));
0107:
0108:                    nodeToItemMap.put(node, this );
0109:                    this .level = level;
0110:                    setOutputMarkupId(true);
0111:
0112:                    // if this isn't a root item in rootless mode
0113:                    if (level != -1) {
0114:                        populateTreeItem(this , level);
0115:                    }
0116:                }
0117:
0118:                /**
0119:                 * @return The children
0120:                 */
0121:                public List getChildren() {
0122:                    return children;
0123:                }
0124:
0125:                /**
0126:                 * @return The current level
0127:                 */
0128:                public int getLevel() {
0129:                    return level;
0130:                }
0131:
0132:                /**
0133:                 * @see wicket.Component#getMarkupId()
0134:                 */
0135:                public String getMarkupId() {
0136:                    // this is overriden to produce id that begins with id of tree
0137:                    // if the tree has set (shorter) id in markup, we can use it to
0138:                    // shorten the id of individual TreeItems
0139:                    return AbstractTree.this .getMarkupId() + "_" + getId();
0140:                }
0141:
0142:                /**
0143:                 * @return parent item
0144:                 */
0145:                public TreeItem getParentItem() {
0146:                    return (TreeItem) nodeToItemMap
0147:                            .get(((TreeNode) getModelObject()).getParent());
0148:                }
0149:
0150:                /**
0151:                 * Sets the children.
0152:                 * 
0153:                 * @param children
0154:                 *            The children
0155:                 */
0156:                public void setChildren(List children) {
0157:                    this .children = children;
0158:                }
0159:
0160:                /**
0161:                 * Whether to render children.
0162:                 * 
0163:                 * @return whether to render children
0164:                 */
0165:                protected final boolean isRenderChildren() {
0166:                    return getFlag(FLAG_RENDER_CHILDREN);
0167:                }
0168:
0169:                /**
0170:                 * @see wicket.MarkupContainer#onRender(wicket.markup.MarkupStream)
0171:                 */
0172:                protected void onRender(final MarkupStream markupStream) {
0173:                    // is this root and tree is in rootless mode?
0174:                    if (this  == rootItem && isRootLess() == true) {
0175:                        // yes, write empty div with id
0176:                        // this is necesary for createElement js to work correctly
0177:                        getResponse().write(
0178:                                "<div style=\"display:none\" id=\""
0179:                                        + getMarkupId() + "\"></div>");
0180:                        markupStream.skipComponent();
0181:                    } else {
0182:                        // remember current index
0183:                        final int index = markupStream.getCurrentIndex();
0184:
0185:                        // render the item
0186:                        super .onRender(markupStream);
0187:
0188:                        // should we also render children (ajax response)
0189:                        if (isRenderChildren()) {
0190:                            // visit every child
0191:                            visitItemChildren(this , new IItemCallback() {
0192:                                public void visitItem(TreeItem item) {
0193:                                    // rewind markupStream
0194:                                    markupStream.setCurrentIndex(index);
0195:                                    // render child
0196:                                    item.onRender(markupStream);
0197:                                }
0198:                            });
0199:                            // children are rendered, clear the flag
0200:                            setRenderChildren(false);
0201:                        }
0202:                    }
0203:                }
0204:
0205:                protected final void setRenderChildren(boolean value) {
0206:                    setFlag(FLAG_RENDER_CHILDREN, value);
0207:                }
0208:
0209:                protected void onDetach() {
0210:                    super .onDetach();
0211:                    Object object = getModelObject();
0212:                    if (object instanceof  IDetachable) {
0213:                        ((IDetachable) object).detach();
0214:                    }
0215:                }
0216:            }
0217:
0218:            /**
0219:             * Components that holds tree items. This is similiar to ListView, but it
0220:             * renders tree items in the right order.
0221:             */
0222:            private class TreeItemContainer extends WebMarkupContainer {
0223:                private static final long serialVersionUID = 1L;
0224:
0225:                /**
0226:                 * Construct.
0227:                 * 
0228:                 * @param id
0229:                 *            The component id
0230:                 */
0231:                public TreeItemContainer(String id) {
0232:                    super (id);
0233:                }
0234:
0235:                /**
0236:                 * @see wicket.MarkupContainer#remove(wicket.Component)
0237:                 */
0238:                public void remove(Component component) {
0239:                    // when a treeItem is removed, remove reference to it from
0240:                    // nodeToItemMAp
0241:                    if (component instanceof  TreeItem) {
0242:                        nodeToItemMap.remove(((TreeItem) component)
0243:                                .getModelObject());
0244:                    }
0245:                    super .remove(component);
0246:                }
0247:
0248:                /**
0249:                 * renders the tree items, making sure that items are rendered in the
0250:                 * order they should be
0251:                 * 
0252:                 * @param markupStream
0253:                 */
0254:                protected void onRender(final MarkupStream markupStream) {
0255:                    // Save position in markup stream
0256:                    final int markupStart = markupStream.getCurrentIndex();
0257:
0258:                    // have we rendered at least one item?
0259:                    final class Rendered {
0260:                        boolean rendered = false;
0261:                    }
0262:                    ;
0263:                    final Rendered rendered = new Rendered();
0264:
0265:                    // is there a root item? (non-empty tree)
0266:                    if (rootItem != null) {
0267:                        IItemCallback callback = new IItemCallback() {
0268:                            public void visitItem(TreeItem item) {
0269:                                // rewind markup stream
0270:                                markupStream.setCurrentIndex(markupStart);
0271:
0272:                                // render component
0273:                                item.render(markupStream);
0274:
0275:                                rendered.rendered = true;
0276:                            }
0277:                        };
0278:
0279:                        // visit item and it's children
0280:                        visitItemAndChildren(rootItem, callback);
0281:                    }
0282:
0283:                    if (rendered.rendered == false) {
0284:                        // tree is empty, just move the markupStream
0285:                        markupStream.skipComponent();
0286:                    }
0287:                }
0288:            }
0289:
0290:            /**
0291:             * Returns an iterator that iterates trough the enumeration.
0292:             * 
0293:             * @param enumeration
0294:             *            The enumeration to iterate through
0295:             * @return The iterator
0296:             */
0297:            private static final Iterator toIterator(
0298:                    final Enumeration enumeration) {
0299:                return new Iterator() {
0300:                    private Enumeration e = enumeration;
0301:
0302:                    public boolean hasNext() {
0303:                        return e.hasMoreElements();
0304:                    }
0305:
0306:                    public Object next() {
0307:                        return e.nextElement();
0308:                    }
0309:
0310:                    public void remove() {
0311:                        throw new UnsupportedOperationException(
0312:                                "Remove is not supported on enumeration.");
0313:                    }
0314:                };
0315:            }
0316:
0317:            private boolean attached = false;
0318:
0319:            /** comma separated list of ids of elements to be deleted. */
0320:            private final AppendingStringBuffer deleteIds = new AppendingStringBuffer();
0321:
0322:            /**
0323:             * whether the whole tree is dirty (so the whole tree needs to be
0324:             * refreshed).
0325:             */
0326:            private boolean dirtyAll = false;
0327:
0328:            /**
0329:             * list of dirty items. if children property of these items is null, the
0330:             * chilren will be rebuild.
0331:             */
0332:            private final List dirtyItems = new ArrayList();
0333:
0334:            /**
0335:             * list of dirty items which need the DOM structure to be created for them
0336:             * (added items)
0337:             */
0338:            private final List dirtyItemsCreateDOM = new ArrayList();
0339:
0340:            /** counter for generating unique ids of every tree item. */
0341:            private int idCounter = 0;
0342:
0343:            /** Component whose children are tree items. */
0344:            private TreeItemContainer itemContainer;
0345:
0346:            /**
0347:             * map that maps TreeNode to TreeItem. TreeItems only exists for TreeNodes,
0348:             * that are visibled (their parents are not collapsed).
0349:             */
0350:            private final Map nodeToItemMap = new HashMap();
0351:
0352:            /**
0353:             * we need to track previous model. if the model changes, we unregister the
0354:             * tree from listeners of old model and register the tree as litener of new
0355:             * model.
0356:             */
0357:            private TreeModel previousModel = null;
0358:
0359:            /** root item of the tree. */
0360:            private TreeItem rootItem = null;
0361:
0362:            /** whether the tree root is shown. */
0363:            private boolean rootLess = false;
0364:
0365:            /** stores reference to tree state. */
0366:            private ITreeState state;
0367:
0368:            /**
0369:             * Tree constructor
0370:             * 
0371:             * @param id
0372:             *            The component id
0373:             */
0374:            public AbstractTree(String id) {
0375:                super (id);
0376:                init();
0377:            }
0378:
0379:            /**
0380:             * Tree constructor
0381:             * 
0382:             * @param id
0383:             *            The component id
0384:             * @param model
0385:             *            The tree model
0386:             */
0387:            public AbstractTree(String id, IModel model) {
0388:                super (id, model);
0389:                init();
0390:            }
0391:
0392:            /** called when all nodes are collapsed. */
0393:            public final void allNodesCollapsed() {
0394:                invalidateAll();
0395:            }
0396:
0397:            /** called when all nodes are expaned. */
0398:            public final void allNodesExpanded() {
0399:                invalidateAll();
0400:            }
0401:
0402:            /**
0403:             * Returns the TreeState of this tree.
0404:             * 
0405:             * @return Tree state instance
0406:             */
0407:            public ITreeState getTreeState() {
0408:                if (state == null) {
0409:                    state = newTreeState();
0410:
0411:                    // add this object as listener of the state
0412:                    state.addTreeStateListener(this );
0413:                    // FIXME: Where should we remove the listener?
0414:                }
0415:                return state;
0416:            }
0417:
0418:            /**
0419:             * This method is called before the onAttach is called. Code here gets
0420:             * executed before the items have been populated. 
0421:             */
0422:            protected void onBeforeAttach() {
0423:            }
0424:
0425:            /**
0426:             * Called at the beginning of the request (not ajax request, unless we are
0427:             * rendering the entire component)
0428:             */
0429:            public void internalAttach() {
0430:                if (attached == false) {
0431:                    onBeforeAttach();
0432:
0433:                    checkModel();
0434:
0435:                    // Do we have to rebuld the whole tree?
0436:                    if (dirtyAll && rootItem != null) {
0437:                        clearAllItem();
0438:                    } else {
0439:                        // rebuild chilren of dirty nodes that need it
0440:                        rebuildDirty();
0441:                    }
0442:
0443:                    // is root item created? (root item is null if the items have not
0444:                    // been created yet, or the whole tree was dirty and clearAllITem
0445:                    // has been called
0446:                    if (rootItem == null) {
0447:                        TreeNode rootNode = (TreeNode) ((TreeModel) getModelObject())
0448:                                .getRoot();
0449:                        if (rootNode != null) {
0450:                            if (isRootLess()) {
0451:                                rootItem = newTreeItem(rootNode, -1);
0452:                            } else {
0453:                                rootItem = newTreeItem(rootNode, 0);
0454:                            }
0455:                            itemContainer.add(rootItem);
0456:                            buildItemChildren(rootItem);
0457:                        }
0458:                    }
0459:
0460:                    attached = true;
0461:                }
0462:
0463:                super .internalAttach();
0464:            }
0465:
0466:            /**
0467:             * @see wicket.MarkupContainer#internalDetach()
0468:             */
0469:            public void internalDetach() {
0470:                super .internalDetach();
0471:                attached = false;
0472:            }
0473:
0474:            /**
0475:             * Call to refresh the whole tree. This should only be called when the
0476:             * roodNode has been replaced or the entiry tree model changed.
0477:             */
0478:            public final void invalidateAll() {
0479:                updated();
0480:                this .dirtyAll = true;
0481:            }
0482:
0483:            /**
0484:             * @return whether the tree root is shown
0485:             */
0486:            public final boolean isRootLess() {
0487:                return rootLess;
0488:            };
0489:
0490:            /**
0491:             * @see wicket.extensions.markup.html.tree.ITreeStateListener#nodeCollapsed(javax.swing.tree.TreeNode)
0492:             */
0493:            public final void nodeCollapsed(TreeNode node) {
0494:                if (isNodeVisible(node) == true) {
0495:                    invalidateNodeWithChildren(node);
0496:                }
0497:            }
0498:
0499:            /**
0500:             * @see wicket.extensions.markup.html.tree.ITreeStateListener#nodeExpanded(javax.swing.tree.TreeNode)
0501:             */
0502:            public final void nodeExpanded(TreeNode node) {
0503:                if (isNodeVisible(node) == true) {
0504:                    invalidateNodeWithChildren(node);
0505:                }
0506:            }
0507:
0508:            /**
0509:             * @see wicket.extensions.markup.html.tree.ITreeStateListener#nodeSelected(javax.swing.tree.TreeNode)
0510:             */
0511:            public final void nodeSelected(TreeNode node) {
0512:                if (isNodeVisible(node)) {
0513:                    invalidateNode(node, true);
0514:                }
0515:            }
0516:
0517:            /**
0518:             * @see wicket.extensions.markup.html.tree.ITreeStateListener#nodeUnselected(javax.swing.tree.TreeNode)
0519:             */
0520:            public final void nodeUnselected(TreeNode node) {
0521:                if (isNodeVisible(node)) {
0522:                    invalidateNode(node, true);
0523:                }
0524:            }
0525:
0526:            /**
0527:             * Sets whether the root of the tree should be visible.
0528:             * 
0529:             * @param rootLess
0530:             *            whether the root should be visible
0531:             */
0532:            public void setRootLess(boolean rootLess) {
0533:                if (this .rootLess != rootLess) {
0534:                    this .rootLess = rootLess;
0535:                    invalidateAll();
0536:
0537:                    // if the tree is in rootless mode, make sure the root node is
0538:                    // expanded
0539:                    if (rootLess == true && getModelObject() != null) {
0540:                        getTreeState().expandNode(
0541:                                (TreeNode) ((TreeModel) getModelObject())
0542:                                        .getRoot());
0543:                    }
0544:                }
0545:            }
0546:
0547:            /**
0548:             * @see javax.swing.event.TreeModelListener#treeNodesChanged(javax.swing.event.TreeModelEvent)
0549:             */
0550:            public final void treeNodesChanged(TreeModelEvent e) {
0551:                // has root node changed?
0552:                if (e.getChildren() == null) {
0553:                    if (rootItem != null) {
0554:                        invalidateNode((TreeNode) rootItem.getModelObject(),
0555:                                true);
0556:                    }
0557:                } else {
0558:                    // go through all changed nodes
0559:                    Object[] children = e.getChildren();
0560:                    if (children != null)
0561:                        for (int i = 0; i < children.length; i++) {
0562:                            TreeNode node = (TreeNode) children[i];
0563:                            if (isNodeVisible(node)) {
0564:                                // if the nodes is visible invalidate it
0565:                                invalidateNode(node, true);
0566:                            }
0567:                        }
0568:                }
0569:            };
0570:
0571:            /**
0572:             * Marks the last but one visible child node of the given item as dirty,
0573:             * if give child is the last item of parent.
0574:             * 
0575:             * We need this to refresh the previous visible item in case the 
0576:             * inserted / deleteditem was last. The reason is that  the line 
0577:             * shape of previous item chages from L to |- .
0578:             * 
0579:             * @param parent
0580:             * @param child
0581:             */
0582:            private void markTheLastButOneChildDirty(TreeItem parent,
0583:                    TreeItem child) {
0584:                if (parent.getChildren().indexOf(child) == parent.getChildren()
0585:                        .size() - 1) {
0586:                    // go through the childrend backwards, start at the last but one
0587:                    // item
0588:                    for (int i = parent.getChildren().size() - 2; i >= 0; --i) {
0589:                        TreeItem item = (TreeItem) parent.getChildren().get(i);
0590:
0591:                        // invalidate the node and it's children, so that they are redrawn
0592:                        invalidateNodeWithChildren((TreeNode) item
0593:                                .getModelObject());
0594:
0595:                    }
0596:                }
0597:            }
0598:
0599:            /**
0600:             * @see javax.swing.event.TreeModelListener#treeNodesInserted(javax.swing.event.TreeModelEvent)
0601:             */
0602:            public final void treeNodesInserted(TreeModelEvent e) {
0603:                // get the parent node of inserted nodes
0604:                TreeNode parent = (TreeNode) e.getTreePath()
0605:                        .getLastPathComponent();
0606:
0607:                if (isNodeVisible(parent) && isNodeExpanded(parent)) {
0608:                    TreeItem parentItem = (TreeItem) nodeToItemMap.get(parent);
0609:                    for (int i = 0; i < e.getChildren().length; ++i) {
0610:                        TreeNode node = (TreeNode) e.getChildren()[i];
0611:                        int index = e.getChildIndices()[i];
0612:                        TreeItem item = newTreeItem(node,
0613:                                parentItem.getLevel() + 1);
0614:                        itemContainer.add(item);
0615:                        parentItem.getChildren().add(index, item);
0616:
0617:                        markTheLastButOneChildDirty(parentItem, item);
0618:
0619:                        dirtyItems.add(item);
0620:                        dirtyItemsCreateDOM.add(item);
0621:                    }
0622:                }
0623:            }
0624:
0625:            /**
0626:             * @see javax.swing.event.TreeModelListener#treeNodesRemoved(javax.swing.event.TreeModelEvent)
0627:             */
0628:            public final void treeNodesRemoved(TreeModelEvent e) {
0629:                // get the parent node of inserted nodes
0630:                TreeNode parent = (TreeNode) e.getTreePath()
0631:                        .getLastPathComponent();
0632:                TreeItem parentItem = (TreeItem) nodeToItemMap.get(parent);
0633:
0634:                if (isNodeVisible(parent) && isNodeExpanded(parent)) {
0635:
0636:                    for (int i = 0; i < e.getChildren().length; ++i) {
0637:                        TreeNode node = (TreeNode) e.getChildren()[i];
0638:
0639:                        TreeItem item = (TreeItem) nodeToItemMap.get(node);
0640:                        if (item != null) {
0641:                            markTheLastButOneChildDirty(parentItem, item);
0642:
0643:                            parentItem.getChildren().remove(item);
0644:
0645:                            // go though item children and remove every one of them
0646:                            visitItemChildren(item, new IItemCallback() {
0647:                                public void visitItem(TreeItem item) {
0648:                                    removeItem(item);
0649:
0650:                                    // unselect the node
0651:                                    getTreeState().selectNode(
0652:                                            (TreeNode) item.getModelObject(),
0653:                                            false);
0654:                                }
0655:                            });
0656:
0657:                            removeItem(item);
0658:                        }
0659:                    }
0660:                }
0661:            }
0662:
0663:            /**
0664:             * @see javax.swing.event.TreeModelListener#treeStructureChanged(javax.swing.event.TreeModelEvent)
0665:             */
0666:            public final void treeStructureChanged(TreeModelEvent e) {
0667:                // get the parent node of changed nodes
0668:                TreeNode node = (TreeNode) e.getTreePath()
0669:                        .getLastPathComponent();
0670:
0671:                // has the tree root changed?
0672:                if (e.getTreePath().getPathCount() == 1
0673:                        && node.equals(rootItem.getModelObject())) {
0674:                    invalidateAll();
0675:                } else {
0676:                    invalidateNodeWithChildren(node);
0677:                }
0678:            }
0679:
0680:            /**
0681:             * Updates the changed portions of the tree using given AjaxRequestTarget.
0682:             * Call this method if you modified the tree model during an ajax request
0683:             * target and you want to partially update the component on page. Make sure
0684:             * that the tree model has fired the proper listener functions.
0685:             * 
0686:             * @param target
0687:             *            Ajax request target used to send the update to the page
0688:             */
0689:            public final void updateTree(final AjaxRequestTarget target) {
0690:                if (target == null) {
0691:                    return;
0692:                }
0693:
0694:                // check whether the model hasn't changed
0695:                checkModel();
0696:
0697:                // is the whole tree dirty
0698:                if (dirtyAll) {
0699:                    // render entire tree component
0700:                    target.addComponent(this );
0701:                } else {
0702:                    // remove DOM elements that need to be removed
0703:                    if (deleteIds.length() != 0) {
0704:                        String js = getElementsDeleteJavascript();
0705:
0706:                        // add the javascript to target
0707:                        target.prependJavascript(js);
0708:                    }
0709:
0710:                    // We have to repeat this as long as there are any dirty items to be created.
0711:                    // The reason why we can't do this in one pass is that some of the items
0712:                    // may need to be inserted after items that has not been inserted yet, so we have
0713:                    // to detect those and wait until the items they depend on are inserted.
0714:                    while (dirtyItemsCreateDOM.isEmpty() == false) {
0715:                        for (Iterator i = dirtyItemsCreateDOM.iterator(); i
0716:                                .hasNext();) {
0717:                            TreeItem item = (TreeItem) i.next();
0718:                            TreeItem parent = item.getParentItem();
0719:                            int index = parent.getChildren().indexOf(item);
0720:                            TreeItem previous;
0721:                            // we need item before this (in dom structure)
0722:
0723:                            if (index == 0) {
0724:                                previous = parent;
0725:                            } else {
0726:                                previous = (TreeItem) parent.getChildren().get(
0727:                                        index - 1);
0728:                                // get the last item of previous item subtree
0729:                                while (previous.getChildren() != null
0730:                                        && previous.getChildren().size() > 0) {
0731:                                    previous = (TreeItem) previous
0732:                                            .getChildren().get(
0733:                                                    previous.getChildren()
0734:                                                            .size() - 1);
0735:                                }
0736:                            }
0737:                            // check if the previous item isn't waiting to be inserted
0738:                            if (dirtyItemsCreateDOM.contains(previous) == false) {
0739:                                // it's already in dom, so we can use it as point of insertion
0740:                                target
0741:                                        .prependJavascript("Wicket.Tree.createElement(\""
0742:                                                + item.getMarkupId()
0743:                                                + "\","
0744:                                                + "\""
0745:                                                + previous.getMarkupId()
0746:                                                + "\")");
0747:
0748:                                // remove the item so we don't process it again
0749:                                i.remove();
0750:                            } else {
0751:                                // we don't do anything here, inserting this item will have to wait 
0752:                                // until the previous item gets inserted 
0753:                            }
0754:                        }
0755:                    }
0756:
0757:                    // iterate through dirty items
0758:                    for (Iterator i = dirtyItems.iterator(); i.hasNext();) {
0759:                        TreeItem item = (TreeItem) i.next();
0760:                        // does the item need to rebuild children?
0761:                        if (item.getChildren() == null) {
0762:                            // rebuld the children
0763:                            buildItemChildren(item);
0764:
0765:                            // set flag on item so that it renders itself together with
0766:                            // it's children
0767:                            item.setRenderChildren(true);
0768:                        }
0769:
0770:                        // add the component to target
0771:                        target.addComponent(item);
0772:                    }
0773:
0774:                    // clear dirty flags
0775:                    updated();
0776:                }
0777:            }
0778:
0779:            /**
0780:             * Returns whether the given node is expanded.
0781:             * 
0782:             * @param node
0783:             *            The node to inspect
0784:             * @return true if the node is expanded, false otherwise
0785:             */
0786:            protected final boolean isNodeExpanded(TreeNode node) {
0787:                // In root less mode the root node is always expanded
0788:                if (isRootLess() && rootItem != null
0789:                        && rootItem.getModelObject().equals(node)) {
0790:                    return true;
0791:                }
0792:
0793:                return getTreeState().isNodeExpanded(node);
0794:            }
0795:
0796:            /**
0797:             * Creates the TreeState, which is an object where the current state of tree
0798:             * (which nodes are expanded / collapsed, selected, ...) is stored.
0799:             * 
0800:             * @return Tree state instance
0801:             */
0802:            protected ITreeState newTreeState() {
0803:                return new DefaultTreeState();
0804:            }
0805:
0806:            /**
0807:             * Called after the rendering of tree is complete. Here we clear the dirty
0808:             * flags.
0809:             */
0810:            protected void onAfterRender() {
0811:                // rendering is complete, clear all dirty flags and items
0812:                updated();
0813:            }
0814:
0815:            /**
0816:             * This method is called after creating every TreeItem. This is the place
0817:             * for adding components on item (junction links, labels, icons...)
0818:             * 
0819:             * @param item
0820:             *            newly created tree item. The node can be obtained as
0821:             *            item.getModelObject()
0822:             * 
0823:             * @param level
0824:             *            how deep the component is in tree hierarchy (0 for root item)
0825:             */
0826:            protected abstract void populateTreeItem(WebMarkupContainer item,
0827:                    int level);
0828:
0829:            /**
0830:             * Builds the children for given TreeItem. It recursively traverses children
0831:             * of it's TreeNode and creates TreeItem for every visible TreeNode.
0832:             * 
0833:             * @param item
0834:             *            The parent tree item
0835:             */
0836:            private final void buildItemChildren(TreeItem item) {
0837:                List items;
0838:
0839:                // if the node is expanded
0840:                if (isNodeExpanded((TreeNode) item.getModelObject())) {
0841:                    // build the items for children of the items' treenode.
0842:                    items = buildTreeItems(nodeChildren((TreeNode) item
0843:                            .getModelObject()), item.getLevel() + 1);
0844:                } else {
0845:                    // it's not expanded, just set children to an empty list
0846:                    items = Collections.EMPTY_LIST;
0847:                }
0848:
0849:                item.setChildren(items);
0850:            }
0851:
0852:            /**
0853:             * Builds (recursively) TreeItems for the given Iterator of TreeNodes.
0854:             * 
0855:             * @param nodes
0856:             *            The nodes to build tree items for
0857:             * @param level
0858:             *            The current level
0859:             * @return List with new tree items
0860:             */
0861:            private final List buildTreeItems(Iterator nodes, int level) {
0862:                List result = new ArrayList();
0863:
0864:                // for each node
0865:                while (nodes.hasNext()) {
0866:                    TreeNode node = (TreeNode) nodes.next();
0867:                    // create tree item
0868:                    TreeItem item = newTreeItem(node, level);
0869:                    itemContainer.add(item);
0870:
0871:                    // builds it children (recursively)
0872:                    buildItemChildren(item);
0873:
0874:                    // add item to result
0875:                    result.add(item);
0876:                }
0877:
0878:                return result;
0879:            }
0880:
0881:            /**
0882:             * Checks whether the model has been chaned, and if so unregister and
0883:             * register listeners.
0884:             */
0885:            private final void checkModel() {
0886:                // find out whether the model object (the TreeModel) has been changed
0887:                TreeModel model = (TreeModel) getModelObject();
0888:                if (model != previousModel) {
0889:                    if (previousModel != null) {
0890:                        previousModel.removeTreeModelListener(this );
0891:                    }
0892:
0893:                    previousModel = model;
0894:
0895:                    if (model != null) {
0896:                        model.addTreeModelListener(this );
0897:                    }
0898:                    // model has been changed, redraw whole tree
0899:                    invalidateAll();
0900:                }
0901:            }
0902:
0903:            /**
0904:             * Removes all TreeItem components.
0905:             */
0906:            private final void clearAllItem() {
0907:                visitItemAndChildren(rootItem, new IItemCallback() {
0908:                    public void visitItem(TreeItem item) {
0909:                        item.remove();
0910:                    }
0911:                });
0912:                rootItem = null;
0913:            }
0914:
0915:            /**
0916:             * Returns the javascript used to delete removed elements.
0917:             * 
0918:             * @return The javascript
0919:             */
0920:            private String getElementsDeleteJavascript() {
0921:                // build the javascript call
0922:                final AppendingStringBuffer buffer = new AppendingStringBuffer(
0923:                        100);
0924:
0925:                buffer.append("Wicket.Tree.removeNodes(\"");
0926:
0927:                // first parameter is the markup id of tree (will be used as prefix to
0928:                // build ids of child items
0929:                buffer.append(getMarkupId() + "_\",[");
0930:
0931:                // append the ids of elements to be deleted
0932:                buffer.append(deleteIds);
0933:
0934:                // does the buffer end if ','?
0935:                if (buffer.endsWith(",")) {
0936:                    // it does, trim it
0937:                    buffer.setLength(buffer.length() - 1);
0938:                }
0939:
0940:                buffer.append("]);");
0941:
0942:                return buffer.toString();
0943:            }
0944:
0945:            //
0946:            // State and Model's callbacks
0947:            //
0948:
0949:            /**
0950:             * returns the short version of item id (just the number part).
0951:             * 
0952:             * @param item
0953:             *            The tree item
0954:             * @return The id
0955:             */
0956:            private String getShortItemId(TreeItem item) {
0957:                // show much of component id can we skip? (to minimize the length of
0958:                // javascript being sent)
0959:                final int skip = getMarkupId().length() + 1; // the length of id of
0960:                // tree and '_'.
0961:                return item.getMarkupId().substring(skip);
0962:            }
0963:
0964:            /**
0965:             * Initialize the component.
0966:             */
0967:            private final void init() {
0968:                setVersioned(false);
0969:
0970:                // we need id when we are replacing the whole tree
0971:                setOutputMarkupId(true);
0972:
0973:                // create container for tree items
0974:                itemContainer = new TreeItemContainer("i");
0975:                add(itemContainer);
0976:
0977:                add(HeaderContributor.forJavaScript(AbstractTree.class,
0978:                        "res/tree.js"));
0979:            }
0980:
0981:            /**
0982:             * Invalidates single node (without children). On the next render, this node
0983:             * will be updated. Node will not be rebuilt, unless forceRebuild is true.
0984:             * 
0985:             * @param node
0986:             *            The node to invalidate
0987:             * @param forceRebuild
0988:             */
0989:            private final void invalidateNode(TreeNode node,
0990:                    boolean forceRebuild) {
0991:                if (dirtyAll == false) {
0992:                    // get item for this node
0993:                    TreeItem item = (TreeItem) nodeToItemMap.get(node);
0994:
0995:                    if (item != null) {
0996:                        boolean createDOM = false;
0997:
0998:                        if (forceRebuild) {
0999:                            // recreate the item
1000:                            int level = item.getLevel();
1001:                            List children = item.getChildren();
1002:                            String id = item.getId();
1003:
1004:                            // store the parent of old item
1005:                            TreeItem parent = item.getParentItem();
1006:
1007:                            // if the old item has a parent, store it's index
1008:                            int index = parent != null ? parent.getChildren()
1009:                                    .indexOf(item) : -1;
1010:
1011:                            createDOM = dirtyItemsCreateDOM.contains(item);
1012:
1013:                            dirtyItems.remove(item);
1014:                            dirtyItemsCreateDOM.remove(item);
1015:
1016:                            item.remove();
1017:
1018:                            item = newTreeItem(node, level, id);
1019:                            itemContainer.add(item);
1020:
1021:                            item.setChildren(children);
1022:
1023:                            // was the item an root item?
1024:                            if (parent == null) {
1025:                                rootItem = item;
1026:                            } else {
1027:                                parent.getChildren().set(index, item);
1028:                            }
1029:                        }
1030:
1031:                        dirtyItems.add(item);
1032:                        if (createDOM)
1033:                            dirtyItemsCreateDOM.add(item);
1034:                    }
1035:                }
1036:            }
1037:
1038:            /**
1039:             * Invalidates node and it's children. On the next render, the node and
1040:             * children will be updated. Node children will be rebuilt.
1041:             * 
1042:             * @param node
1043:             *            The node to invalidate
1044:             */
1045:            private final void invalidateNodeWithChildren(TreeNode node) {
1046:                if (dirtyAll == false) {
1047:                    // get item for this node
1048:                    TreeItem item = (TreeItem) nodeToItemMap.get(node);
1049:
1050:                    // is the item visible?
1051:                    if (item != null) {
1052:                        // go though item children and remove every one of them
1053:                        visitItemChildren(item, new IItemCallback() {
1054:                            public void visitItem(TreeItem item) {
1055:                                removeItem(item);
1056:                            }
1057:                        });
1058:
1059:                        // set children to null so that they get rebuild
1060:                        item.setChildren(null);
1061:
1062:                        // add item to dirty items
1063:                        dirtyItems.add(item);
1064:                    }
1065:                }
1066:            }
1067:
1068:            /**
1069:             * Returns whether the given node is visibled, e.g. all it's parents are
1070:             * expanded.
1071:             * 
1072:             * @param node
1073:             *            The node to inspect
1074:             * @return true if the node is visible, false otherwise
1075:             */
1076:            private final boolean isNodeVisible(TreeNode node) {
1077:                while (node.getParent() != null) {
1078:                    if (isNodeExpanded(node.getParent()) == false) {
1079:                        return false;
1080:                    }
1081:                    node = node.getParent();
1082:                }
1083:                return true;
1084:            }
1085:
1086:            /**
1087:             * Creates a tree item for given node.
1088:             * 
1089:             * @param node
1090:             *            The tree node
1091:             * @param level
1092:             *            The level
1093:             * @return The new tree item
1094:             */
1095:            private final TreeItem newTreeItem(TreeNode node, int level) {
1096:                return new TreeItem("" + idCounter++, node, level);
1097:            }
1098:
1099:            /**
1100:             * Creates a tree item for given node with specified id.
1101:             * 
1102:             * @param node
1103:             *            The tree node
1104:             * @param level
1105:             *            The level
1106:             * @param id
1107:             *            the component id
1108:             * @return The new tree item
1109:             */
1110:            private final TreeItem newTreeItem(TreeNode node, int level,
1111:                    String id) {
1112:                return new TreeItem(id, node, level);
1113:            }
1114:
1115:            /**
1116:             * Return the representation of node children as Iterator interface.
1117:             * 
1118:             * @param node
1119:             *            The tree node
1120:             * @return iterable presentation of node children
1121:             */
1122:            private final Iterator nodeChildren(TreeNode node) {
1123:                return toIterator(node.children());
1124:            }
1125:
1126:            /**
1127:             * Rebuilds children of every item in dirtyItems that needs it. This method
1128:             * is called for non-partial update.
1129:             */
1130:            private final void rebuildDirty() {
1131:                // go through dirty items
1132:                for (Iterator i = dirtyItems.iterator(); i.hasNext();) {
1133:                    TreeItem item = (TreeItem) i.next();
1134:                    // item chilren need to be rebuilt
1135:                    if (item.getChildren() == null) {
1136:                        buildItemChildren(item);
1137:                    }
1138:                }
1139:            }
1140:
1141:            /**
1142:             * Removes the item, appends it's id to deleteIds. This is called when a
1143:             * items parent is being deleted or rebuilt.
1144:             * 
1145:             * @param item
1146:             *            The item to remove
1147:             */
1148:            private void removeItem(TreeItem item) {
1149:                // even if the item is dirty it's no longer necessary to update id
1150:                dirtyItems.remove(item);
1151:
1152:                // if the item was about to be created
1153:                if (dirtyItemsCreateDOM.contains(item)) {
1154:                    // we needed to create DOM element, we no longer do
1155:                    dirtyItemsCreateDOM.remove(item);
1156:                } else {
1157:                    // add items id (it's short version) to ids of DOM elements that will be
1158:                    // removed
1159:                    deleteIds.append(getShortItemId(item));
1160:                    deleteIds.append(",");
1161:                }
1162:
1163:                // remove the id
1164:                // note that this doesn't update item's parent's children list
1165:                item.remove();
1166:            }
1167:
1168:            /**
1169:             * Calls after the tree has been rendered. Clears all dirty flags.
1170:             */
1171:            private final void updated() {
1172:                this .dirtyAll = false;
1173:                this .dirtyItems.clear();
1174:                this .dirtyItemsCreateDOM.clear();
1175:                deleteIds.clear(); // FIXME: Recreate it to save some space?
1176:            }
1177:
1178:            /**
1179:             * Call the callback#visitItem method for the given item and all it's
1180:             * chilren.
1181:             * 
1182:             * @param item
1183:             *            The tree item
1184:             * @param callback
1185:             *            item call back
1186:             */
1187:            private final void visitItemAndChildren(TreeItem item,
1188:                    IItemCallback callback) {
1189:                callback.visitItem(item);
1190:                visitItemChildren(item, callback);
1191:            }
1192:
1193:            /**
1194:             * Call the callback#visitItem method for every child of given item.
1195:             * 
1196:             * @param item
1197:             *            The tree item
1198:             * @param callback
1199:             *            The callback
1200:             */
1201:            private final void visitItemChildren(TreeItem item,
1202:                    IItemCallback callback) {
1203:                if (item.getChildren() != null) {
1204:                    for (Iterator i = item.getChildren().iterator(); i
1205:                            .hasNext();) {
1206:                        TreeItem child = (TreeItem) i.next();
1207:                        visitItemAndChildren(child, callback);
1208:                    }
1209:                }
1210:            }
1211:
1212:            /**
1213:             * Returns the component associated with given node, or null, if node is not
1214:             * visible. This is useful in situations when you want to touch the node
1215:             * element in html.
1216:             * 
1217:             * @param node
1218:             *            Tree node
1219:             * @return Component associated with given node, or null if node is not
1220:             *         visible.
1221:             */
1222:            public Component getNodeComponent(TreeNode node) {
1223:                return (Component) nodeToItemMap.get(node);
1224:            }
1225:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.