📄 abstracttree.java
字号:
/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.apache.wicket.markup.html.tree;import java.io.Serializable;import java.util.ArrayList;import java.util.Enumeration;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;import javax.swing.event.TreeModelEvent;import javax.swing.event.TreeModelListener;import javax.swing.tree.TreeModel;import javax.swing.tree.TreeNode;import org.apache.wicket.Component;import org.apache.wicket.IRequestTarget;import org.apache.wicket.ResourceReference;import org.apache.wicket.ajax.AjaxRequestTarget;import org.apache.wicket.behavior.HeaderContributor;import org.apache.wicket.behavior.IBehavior;import org.apache.wicket.markup.MarkupStream;import org.apache.wicket.markup.html.WebMarkupContainer;import org.apache.wicket.markup.html.internal.HtmlHeaderContainer;import org.apache.wicket.markup.html.panel.Panel;import org.apache.wicket.markup.html.resources.JavascriptResourceReference;import org.apache.wicket.model.IDetachable;import org.apache.wicket.model.IModel;import org.apache.wicket.model.Model;import org.apache.wicket.util.string.AppendingStringBuffer;/** * This class encapsulates the logic for displaying and (partial) updating the tree. Actual * presentation is out of scope of this class. User should derive they own tree (if needed) from * {@link BaseTree} (recommended). * * @author Matej Knopp */public abstract class AbstractTree extends Panel implements ITreeStateListener, TreeModelListener{ /** * */ private static final long serialVersionUID = 1L; /** * Interface for visiting individual tree items. */ private static interface IItemCallback { /** * Visits the tree item. * * @param item * the item to visit */ void visitItem(TreeItem item); } /** * This class represents one row in rendered tree (TreeNode). Only TreeNodes that are visible * (all their parent are expanded) have TreeItem created for them. */ private final class TreeItem extends WebMarkupContainer { /** * whether this tree item should also render it's children to response. this is set if we * need the whole subtree rendered as one component in ajax response, so that we can replace * it in one step (replacing individual rows is very slow in javascript, therefore we * replace the whole subtree) */ private final static int FLAG_RENDER_CHILDREN = FLAG_RESERVED8; private static final long serialVersionUID = 1L; /** * tree item children - we need this to traverse items in correct order when rendering */ private List children = null; /** tree item level - how deep is this item in tree */ private final int level; /** * Construct. * * @param id * The component id * @param node * tree node * @param level * current level */ public TreeItem(String id, final TreeNode node, int level) { super(id, new Model((Serializable)node)); nodeToItemMap.put(node, this); this.level = level; setOutputMarkupId(true); // if this isn't a root item in rootless mode if (level != -1) { populateTreeItem(this, level); } } /** * @return The children */ public List getChildren() { return children; } /** * @return The current level */ public int getLevel() { return level; } /** * @see org.apache.wicket.Component#getMarkupId() */ public String getMarkupId() { // this is overridden to produce id that begins with id of tree // if the tree has set (shorter) id in markup, we can use it to // shorten the id of individual TreeItems return AbstractTree.this.getMarkupId() + "_" + getId(); } /** * @return parent item */ public TreeItem getParentItem() { return (TreeItem)nodeToItemMap.get(((TreeNode)getModelObject()).getParent()); } /** * Sets the children. * * @param children * The children */ public void setChildren(List children) { this.children = children; } /** * Whether to render children. * * @return whether to render children */ protected final boolean isRenderChildren() { return getFlag(FLAG_RENDER_CHILDREN); } /** * @see org.apache.wicket.MarkupContainer#onRender(org.apache.wicket.markup.MarkupStream) */ protected void onRender(final MarkupStream markupStream) { // is this root and tree is in rootless mode? if (this == rootItem && isRootLess() == true) { // yes, write empty div with id // this is necessary for createElement js to work correctly getResponse().write( "<div style=\"display:none\" id=\"" + getMarkupId() + "\"></div>"); markupStream.skipComponent(); } else { // remember current index final int index = markupStream.getCurrentIndex(); // render the item super.onRender(markupStream); // should we also render children (ajax response) if (isRenderChildren()) { // visit every child visitItemChildren(this, new IItemCallback() { public void visitItem(TreeItem item) { // rewind markupStream markupStream.setCurrentIndex(index); // render child item.onRender(markupStream); // go through the behaviors and invoke IBehavior.afterRender List behaviors = item.getBehaviors(); for (Iterator i = behaviors.iterator(); i.hasNext();) { IBehavior behavior = (IBehavior)i.next(); behavior.afterRender(item); } } }); // } } } public void renderHead(final HtmlHeaderContainer container) { super.renderHead(container); if (isRenderChildren()) { // visit every child visitItemChildren(this, new IItemCallback() { public void visitItem(TreeItem item) { // write header contributions from the children of item item.visitChildren(new Component.IVisitor() { public Object component(Component component) { if (component.isVisible()) { component.renderHead(container); return CONTINUE_TRAVERSAL; } else { return CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; } } }); } }); } } protected final void setRenderChildren(boolean value) { setFlag(FLAG_RENDER_CHILDREN, value); } protected void onDetach() { super.onDetach(); Object object = getModelObject(); if (object instanceof IDetachable) { ((IDetachable)object).detach(); } if (isRenderChildren()) { // visit every child visitItemChildren(this, new IItemCallback() { public void visitItem(TreeItem item) { item.detach(); } }); } // children are rendered, clear the flag setRenderChildren(false); } protected void onBeforeRender() { onBeforeRenderInternal(); super.onBeforeRender(); if (isRenderChildren()) { // visit every child visitItemChildren(this, new IItemCallback() { public void visitItem(TreeItem item) { item.prepareForRender(); } }); } } protected void onAfterRender() { super.onAfterRender(); if (isRenderChildren()) { // visit every child visitItemChildren(this, new IItemCallback() { public void visitItem(TreeItem item) { item.afterRender(); } }); } } } /** * Components that holds tree items. This is similar to ListView, but it renders tree items in * the right order. */ private class TreeItemContainer extends WebMarkupContainer { private static final long serialVersionUID = 1L; /** * Construct. * * @param id * The component id */ public TreeItemContainer(String id) { super(id); } /** * @see org.apache.wicket.MarkupContainer#remove(org.apache.wicket.Component) */ public void remove(Component component) { // when a treeItem is removed, remove reference to it from // nodeToItemMAp if (component instanceof TreeItem) { nodeToItemMap.remove(((TreeItem)component).getModelObject()); } super.remove(component); } /** * renders the tree items, making sure that items are rendered in the order they should be * * @param markupStream */ protected void onRender(final MarkupStream markupStream) { // Save position in markup stream final int markupStart = markupStream.getCurrentIndex(); // have we rendered at least one item? final class Rendered { boolean rendered = false; } ; final Rendered rendered = new Rendered(); // is there a root item? (non-empty tree) if (rootItem != null) { IItemCallback callback = new IItemCallback() { public void visitItem(TreeItem item) { // rewind markup stream markupStream.setCurrentIndex(markupStart); // render component item.render(markupStream); rendered.rendered = true; } }; // visit item and it's children visitItemAndChildren(rootItem, callback); } if (rendered.rendered == false) { // tree is empty, just move the markupStream markupStream.skipComponent(); } } } /** * Returns an iterator that iterates trough the enumeration. * * @param enumeration * The enumeration to iterate through * @return The iterator */ private static final Iterator toIterator(final Enumeration enumeration) { return new Iterator() { private final Enumeration e = enumeration; public boolean hasNext() { return e.hasMoreElements(); } public Object next() { return e.nextElement(); } public void remove() { throw new UnsupportedOperationException("Remove is not supported on enumeration."); } }; } private boolean attached = false; /** comma separated list of ids of elements to be deleted. */ private final AppendingStringBuffer deleteIds = new AppendingStringBuffer(); /** * whether the whole tree is dirty (so the whole tree needs to be refreshed). */ private boolean dirtyAll = false; /** * list of dirty items. if children property of these items is null, the children will be * rebuild. */ private final List dirtyItems = new ArrayList(); /** * list of dirty items which need the DOM structure to be created for them (added items) */ private final List dirtyItemsCreateDOM = new ArrayList(); /** counter for generating unique ids of every tree item. */ private int idCounter = 0; /** Component whose children are tree items. */ private TreeItemContainer itemContainer; /** * map that maps TreeNode to TreeItem. TreeItems only exists for TreeNodes, that are visible * (their parents are not collapsed). */ private final Map nodeToItemMap = new HashMap(); /** * we need to track previous model. if the model changes, we unregister the tree from listeners * of old model and register the tree as listener of new model. */ private TreeModel previousModel = null; /** root item of the tree. */ private TreeItem rootItem = null; /** whether the tree root is shown. */ private boolean rootLess = false; /** stores reference to tree state. */ private ITreeState state; /** * Tree constructor * * @param id * The component id */ public AbstractTree(String id) { super(id); init(); } /**
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -