com.eltiland.ui.course.components.tree.ELTDefaultAbstractTree.java Source code

Java tutorial

Introduction

Here is the source code for com.eltiland.ui.course.components.tree.ELTDefaultAbstractTree.java

Source

/*
 * 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 com.eltiland.ui.course.components.tree;

import org.apache.wicket.Component;
import org.apache.wicket.IClusterable;
import org.apache.wicket.MarkupContainer;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxFallbackLink;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.IAjaxLink;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.MarkupStream;
import org.apache.wicket.markup.html.IHeaderResponse;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.tree.AbstractTree;
import org.apache.wicket.markup.html.tree.LinkType;
import org.apache.wicket.markup.html.tree.WicketTreeModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.request.IRequestHandler;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.handler.resource.ResourceReferenceRequestHandler;
import org.apache.wicket.request.resource.PackageResourceReference;
import org.apache.wicket.request.resource.ResourceReference;

import javax.swing.tree.TreeModel;
import java.util.HashMap;
import java.util.Map;

/**
 * Tree class that contains convenient functions related to presentation of the tree, which includes
 * junction link, tree item selection link, spacers (with lines) and default tree item and folder
 * icons.
 * <p/>
 * The class itself adds no component to tree items. If you use this class directly, you have to
 * implement populateTreeItem() on your own. If you want to use an existing (complete) tree class,
 * use {@link org.apache.wicket.extensions.markup.html.tree.Tree}
 * <p/>
 * This class allows you to choose between 3 types of links.
 *
 * @author Matej Knopp
 */
public abstract class ELTDefaultAbstractTree extends AbstractTree {

    protected Map<ELTTreeNode, Long> nodeData = new HashMap<>();
    private Long currendId = (long) 0;

    private static final long serialVersionUID = 1L;

    /**
     * Helper class for calling an action from a link.
     *
     * @author Matej Knopp
     */
    protected interface ILinkCallback extends IAjaxLink, IClusterable {
    }

    /**
     * Reference to the css file.
     */
    private static final ResourceReference CSS = new PackageResourceReference(ELTDefaultAbstractTree.class,
            "res/tree.css");

    /**
     * Reference to the icon of closed tree folder
     */
    private static final ResourceReference FOLDER_CLOSED = new PackageResourceReference(
            ELTDefaultAbstractTree.class, "res/folder-closed.gif");

    /**
     * Reference to the icon of open tree folder
     */
    private static final ResourceReference FOLDER_OPEN = new PackageResourceReference(ELTDefaultAbstractTree.class,
            "res/folder-open.gif");

    /**
     * Reference to the icon of tree item (not a folder)
     */
    private static final ResourceReference ITEM = new PackageResourceReference(ELTDefaultAbstractTree.class,
            "res/item.gif");

    /**
     * The link type, default is {@link org.apache.wicket.markup.html.tree.LinkType#AJAX ajax}.
     */
    private LinkType linkType = LinkType.AJAX;

    /**
     * Tree constructor.
     *
     * @param id The component id
     */
    public ELTDefaultAbstractTree(final String id) {
        super(id);
    }

    /**
     * Tree constructor.
     *
     * @param id    The component id
     * @param model The tree model
     */
    public ELTDefaultAbstractTree(final String id, final IModel<? extends TreeModel> model) {
        super(id, model);
    }

    /**
     * Tree constructor.
     *
     * @param id    The component id
     * @param model The tree model
     */
    public ELTDefaultAbstractTree(final String id, final TreeModel model) {
        super(id, new WicketTreeModel());
        setModelObject(model);
    }

    /**
     * Returns the current type of links on tree items.
     *
     * @return The link type
     */
    public LinkType getLinkType() {
        return linkType;
    }

    /**
     * Sets the type of links on tree items. After the link type is changed, the whole tree is
     * rebuild and re-rendered.
     *
     * @param linkType type of links
     */
    public void setLinkType(final LinkType linkType) {
        if (this.linkType != linkType) {
            this.linkType = linkType;
            invalidateAll();
        }
    }

    /**
     * Returns the resource reference of default stylesheet.
     *
     * @return The package resource reference
     */
    protected ResourceReference getCSS() {
        return CSS;
    }

    /**
     * Returns the resource reference of default closed tree folder.
     *
     * @return The package resource reference
     */
    protected ResourceReference getFolderClosed() {
        return FOLDER_CLOSED;
    }

    /**
     * Returns the resource reference of default open tree folder.
     *
     * @return The package resource reference
     */
    protected ResourceReference getFolderOpen() {
        return FOLDER_OPEN;
    }

    /**
     * Returns the resource reference of default tree item (not folder).
     *
     * @return The package resource reference
     */
    protected ResourceReference getItem() {
        return ITEM;
    }

    /**
     * Returns the resource reference for icon of specified tree node.
     *
     * @param node The node
     * @return The package resource reference
     */
    protected ResourceReference getNodeIcon(final ELTTreeNode node) {
        if (node.isLeaf() == true) {
            return getItem();
        } else {
            if (isNodeExpanded(node)) {
                return getFolderOpen();
            } else {
                return getFolderClosed();
            }
        }
    }

    /**
     * Creates the indentation element. This element should be placed as first element in the tree
     * item markup to ensure proper indentation of the tree item. This implementation also takes
     * care of lines that connect nodes.
     *
     * @param parent The component parent
     * @param id     The component id
     * @param node   The tree node for which to create the indentation element
     * @param level  The current level
     * @return The indentation component
     */
    protected Component newIndentation(final MarkupContainer parent, final String id, final ELTTreeNode node,
            final int level) {
        WebMarkupContainer result = new WebMarkupContainer(id) {
            private static final long serialVersionUID = 1L;

            /**
             * {@inheritDoc}
             */
            @Override
            public void onComponentTagBody(final MarkupStream markupStream, final ComponentTag openTag) {
                Response response = RequestCycle.get().getResponse();
                ELTTreeNode parent = node.getParent();

                CharSequence urls[] = new CharSequence[level];
                for (int i = 0; i < level; ++i) {
                    if (isNodeLast(parent)) {
                        urls[i] = "indent-blank";
                    } else {
                        urls[i] = "indent-line";
                    }

                    if (parent != null) {
                        parent = parent.getParent();
                    }
                }

                for (int i = level - 1; i >= 0; --i) {
                    response.write("<span class=\"" + urls[i] + "\"></span>");
                }
            }
        };
        result.setRenderBodyOnly(true);
        return result;
    }

    /**
     * Creates an image placed on junction link. This image actually consists of two spans with
     * different css classes. These classes are specified according to the stylesheet to make the
     * junction image look well together with lines connecting nodes.
     *
     * @param parent The component parent
     * @param id     The component id
     * @param node   The tree node
     * @return The component that represents a junction
     */
    protected MarkupContainer newJunctionImage(final MarkupContainer parent, final String id,
            final ELTTreeNode node) {
        return (MarkupContainer) new WebMarkupContainer(id) {
            private static final long serialVersionUID = 1L;

            /**
             * {@inheritDoc}
             */
            @Override
            protected void onComponentTag(final ComponentTag tag) {
                super.onComponentTag(tag);

                final String cssClassInner;
                if (node.isLeaf() == false) {
                    cssClassInner = isNodeExpanded(node) ? "minus" : "plus";
                } else {
                    cssClassInner = "corner";
                }

                final String cssClassOuter = isNodeLast(node) ? "junction-last" : "junction";

                Response response = RequestCycle.get().getResponse();
                response.write("<span class=\"" + cssClassOuter + "\"><span class=\"" + cssClassInner
                        + "\"></span></span>");
            }
        }.setRenderBodyOnly(true);
    }

    /**
     * Creates the junction link for given node. Also (optionally) creates the junction image. If
     * the node is a leaf (it has no children), the created junction link is non-functional.
     *
     * @param parent  parent component of the link
     * @param id      wicket:id of the component
     * @param imageId wicket:id of the image. this can be null, in that case image is not created. image
     *                is supposed to be placed on the link (link is parent of image)
     * @param node    tree node for which the link should be created.
     * @return The link component
     */
    protected Component newJunctionLink(final MarkupContainer parent, final String id, final String imageId,
            final ELTTreeNode node) {
        final MarkupContainer junctionLink;

        if (node.isLeaf() == false) {
            junctionLink = newLink(parent, id, new ILinkCallback() {
                private static final long serialVersionUID = 1L;

                public void onClick(final AjaxRequestTarget target) {
                    if (isNodeExpanded(node)) {
                        getTreeState().collapseNode(node);
                    } else {
                        getTreeState().expandNode(node);
                    }
                    onJunctionLinkClicked(target, node);
                    if (target != null) {
                        updateTree(target);
                    }
                }
            });
        } else {
            junctionLink = new WebMarkupContainer(id) {
                private static final long serialVersionUID = 1L;

                /**
                 * {@inheritDoc}
                 */
                @Override
                protected void onComponentTag(final ComponentTag tag) {
                    super.onComponentTag(tag);
                    tag.put("onclick", "return false");
                }
            };
        }

        if (imageId != null) {
            junctionLink.add(newJunctionImage(junctionLink, imageId, node));
        }

        return junctionLink;
    }

    /**
     * Creates a link of type specified by current linkType. When the links is clicked it calls the
     * specified callback.
     *
     * @param parent   The parent component
     * @param id       The component id
     * @param callback The link call back
     * @return The link component
     */
    protected MarkupContainer newLink(final MarkupContainer parent, final String id, final ILinkCallback callback) {
        if (getLinkType() == LinkType.REGULAR) {
            return new Link<Void>(id) {
                private static final long serialVersionUID = 1L;

                /**
                 * {@inheritDoc}
                 */
                @Override
                public void onClick() {
                    callback.onClick(null);
                }
            };
        } else if (getLinkType() == LinkType.AJAX) {
            return new AjaxLink<Void>(id) {
                private static final long serialVersionUID = 1L;

                /**
                 * {@inheritDoc}
                 */
                @Override
                public void onClick(final AjaxRequestTarget target) {
                    callback.onClick(target);
                }
            };
        } else {
            return new AjaxFallbackLink<Void>(id) {
                private static final long serialVersionUID = 1L;

                /**
                 * {@inheritDoc}
                 */
                @Override
                public void onClick(final AjaxRequestTarget target) {
                    callback.onClick(target);
                }
            };
        }
    }

    /**
     * Creates the icon for current node. By default uses image reference specified by
     *
     * @param parent The parent component
     * @param id     The component id
     * @param node   The tree node
     * @return The web component that represents the icon of the current node
     */
    protected Component newNodeIcon(final MarkupContainer parent, final String id, final ELTTreeNode node) {
        return new WebMarkupContainer(id) {
            private static final long serialVersionUID = 1L;

            /**
             * {@inheritDoc}
             */
            @Override
            protected void onComponentTag(final ComponentTag tag) {
                super.onComponentTag(tag);
                IRequestHandler handler = new ResourceReferenceRequestHandler(getNodeIcon(node));
                tag.put("style", "background-image: url('" + RequestCycle.get().urlFor(handler) + "')");
            }
        };
    }

    /**
     * Creates a link that can be used to select / deselect the specified node.
     *
     * @param parent The parent component
     * @param id     The component id
     * @param node   The parent node
     * @return The component that represents the link
     */
    protected MarkupContainer newNodeLink(final MarkupContainer parent, final String id, final ELTTreeNode node) {
        return newLink(parent, id, new ILinkCallback() {
            private static final long serialVersionUID = 1L;

            public void onClick(final AjaxRequestTarget target) {
                getTreeState().selectNode(node, !getTreeState().isNodeSelected(node));
                onNodeLinkClicked(target, node);

                if (target != null) {
                    updateTree(target);
                }
            }
        });
    }

    /**
     * Callback function called after user clicked on an junction link. The node has already been
     * expanded/collapsed (depending on previous status).
     *
     * @param target Request target - may be null on non-ajax call
     * @param node   Node for which this callback is relevant
     */
    protected void onJunctionLinkClicked(final AjaxRequestTarget target, final ELTTreeNode node) {

        currendId = nodeData.get(node);
    }

    /**
     * This callback method is called after user has selected / deselected the given node.
     *
     * @param target Request target - may be null on non-ajax call
     * @param node   Node for which this this callback is fired.
     */
    protected void onNodeLinkClicked(final AjaxRequestTarget target, final ELTTreeNode node) {
        currendId = nodeData.get(node);
    }

    /**
     * Returns whether the provided node is last child of it's parent.
     *
     * @param node The node
     * @return whether the provided node is the last child
     */
    private boolean isNodeLast(final ELTTreeNode node) {
        if (node == null) {
            return true;
        }
        ELTTreeNode parent = node.getParent();
        if (parent == null) {
            return true;
        } else {
            return parent.getChildAt(parent.getChildCount() - 1).equals(node);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void renderHead(final IHeaderResponse response) {
        super.renderHead(response);
        ResourceReference css = getCSS();
        if (css != null) {
            response.renderCSSReference(css);
        }
    }

    public Long getCurrendId() {
        return currendId;
    }
}