All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.jaxx.runtime.swing.nav.tree.NavTreeNode Maven / Gradle / Ivy

There is a newer version: 3.0.3
Show newest version
/*
 * #%L
 * JAXX :: Runtime Swing Nav
 * %%
 * Copyright (C) 2008 - 2020 Code Lutin, Ultreia.io
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 *
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * .
 * #L%
 */
package org.nuiton.jaxx.runtime.swing.nav.tree;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.nuiton.jaxx.runtime.swing.nav.NavBridge;
import org.nuiton.jaxx.runtime.swing.nav.NavDataProvider;
import org.nuiton.jaxx.runtime.swing.nav.NavNode;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import java.util.Enumeration;

/**
 * Implementation of {@link NavNode} used to create in tree table
 * This node extends {@link DefaultMutableTreeNode}
 *
 * @author Tony Chemit - [email protected]
 * @since 2.2
 */
public class NavTreeNode> extends DefaultMutableTreeNode implements NavNode {

    /** Logger */
    static private final Logger log = LogManager.getLogger(NavNode.class);

    private static final long serialVersionUID = 1L;

    /** Type of data associated with the node */
    protected final Class internalClass;

    /**
     * Optinal context to distinguish different nodes with same
     * {@link #internalClass}.
     */
    protected final String context;

    /** Id of the data associated with the node. */
    protected final String id;

    /** Flag to know when renderer should (re-)compute render of the node. */
    protected boolean dirty = true;

    /** Flag to know when the none static node was loaded. */
    protected boolean loaded;

    /** Optional child loador to lazy create childs of the node. */
    protected final NavTreeNodeChildLoador childLoador;

    protected NavTreeNode(String id) {
        this(String.class, id, null, null);
    }

    public NavTreeNode(Class internalClass,
                       String id,
                       String context,
                       NavTreeNodeChildLoador childLoador) {
        this.internalClass = internalClass;
        this.id = id;
        this.context = context;
        this.childLoador = childLoador;
        if (isStaticNode()) {

            // A static node is always full loaded
            loaded = true;
        }
        if (log.isDebugEnabled()) {
            log.debug("new node : " + this);
        }
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public String getContext() {
        return context;
    }

    @Override
    public Class getInternalClass() {
        return internalClass;
    }

    @Override
    public boolean isLoaded() {
        return loaded;
    }

    @Override
    public boolean isDirty() {
        return dirty;
    }

    @Override
    public boolean isStringNode() {
        return String.class.equals(internalClass);
    }

    @Override
    public boolean isStaticNode() {
        return childLoador == null;
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public N getContainerNode() {
        if (isRoot()) {
            // si on arrive sur le root, quelque chose ne va pas,
            // on bloque par null, a defaut de declancher une exception
            return null;
        }

        if (isStringNode()) {
            // on est sur un noeud de type String, donc on regarde sur le parent
            return getParent().getContainerNode();
        }

        // cas final : sur un noeud de donnee + classe interne de donnee
        return (N) this;
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public N findNodeById(String id,
                          NavBridge model,
                          NavDataProvider provider) {
        if (id == null) {

            // id null ? donc rien a faire
            return null;
        }
        if (id.equals(getId())) {

            // on a trouve le bon noeud
            return (N) this;
        }

        if (!isLoaded()) {

            // il faut charger les fils du noeud pour effectuer la recherche
            populateChilds(model, provider);
        }

        if (isLeaf()) {

            // au final le noeud est une feuille, donc ne convient pas
            return null;
        }

        // on recherche dans les fils
        Enumeration enumeration = children();
        while (enumeration.hasMoreElements()) {
            N node = (N) enumeration.nextElement();
            N nodeById = node.findNodeById(id, model, provider);
            if (nodeById != null) {
                return nodeById;
            }
        }

        // aucun des noeud fils ne convient
        return null;
    }

    @Override
    public N getChild(String id,
                      NavBridge bridge,
                      NavDataProvider provider) {

        if (id == null) {

            // id null ? donc rien a faire
            return null;
        }

        if (!isLoaded()) {

            // il faut charger les fils du noeud pour effectuer la recherche
            populateChilds(bridge, provider);
        }

        if (isLeaf()) {

            // au final le noeud est une feuille, donc ne convient pas
            return null;
        }

        // on recherche dans les fils
        Enumeration enumeration = children();
        while (enumeration.hasMoreElements()) {
            @SuppressWarnings("unchecked") N child = (N) enumeration.nextElement();
            if (id.equals(child.getId())) {
                return child;
            }
        }

        // aucun des noeud fils ne convient
        return null;
    }

    @Override
    public void setDirty(boolean dirty) {
        this.dirty = dirty;
    }

    @Override
    public boolean isLeaf() {
        // there is two behaviours for the test :
        // 1 - when the node is static, then can directly use his number of child
        //     to determine if node is a leaf (no child)
        // 2 - when the node is dynamic, then ALWAYS says the node is NOT a leaf until
        //     it was loaded, otherwise the WillExpand listener will not load the childs...
        //     Once the node is loaded, use back the normal behaviour (count number of childs)
        return isStaticNode() ? super.isLeaf() : isLoaded() && getChildCount() == 0;
    }

    @Override
    public Object getUserObject() {
        return id;
    }

    @Override
    public String toString() {
        return System.identityHashCode(this) + "-" + id;
    }

    //--------------------------------------------------------------------------
    //-- Populate methods
    //--------------------------------------------------------------------------

    @Override
    public void populateNode(NavBridge model,
                             NavDataProvider provider,
                             boolean populateChilds) {

        // on indique que le noeud n'est plus propre
        setDirty(true);

        if (populateChilds) {

            // chargement des fils
            populateChilds(model, provider);
        }
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public void populateChilds(NavBridge bridge,
                               NavDataProvider provider) {
        if (isStaticNode()) {

            if (log.isDebugEnabled()) {
                log.debug("is static node " + this);
            }

            // noeud static, rien a faire
            return;
        }

        if (!bridge.canLoadChild((N) this)) {

            // can not load childs
            if (log.isDebugEnabled()) {
                log.debug("Will Skip populateChilds for node : " + this);
            }
            return;
        }

        // chargement des noeuds fils du noeud courant
        try {
            if (log.isDebugEnabled()) {
                log.debug("Will load childs for " + this);
            }
            childLoador.loadChilds((NavTreeBridge) bridge, (N) this, provider);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {

            // au final, on passe le noeud a l'état chargé
            loaded = true;
        }
    }

    //--------------------------------------------------------------------------
    //-- Overrides to use generic type as return
    //--------------------------------------------------------------------------

    @SuppressWarnings({"unchecked"})
    @Override
    public N getParent() {
        return (N) super.getParent();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getRoot() {
        return (N) super.getRoot();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getFirstChild() {
        return (N) super.getFirstChild();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getLastChild() {
        return (N) super.getLastChild();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getChildAfter(TreeNode aChild) {
        return (N) super.getChildAfter(aChild);
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getChildBefore(TreeNode aChild) {
        return (N) super.getChildBefore(aChild);
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getNextSibling() {
        return (N) super.getNextSibling();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getPreviousSibling() {
        return (N) super.getPreviousSibling();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getFirstLeaf() {
        return (N) super.getFirstLeaf();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getLastLeaf() {
        return (N) super.getLastLeaf();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getNextLeaf() {
        return (N) super.getNextLeaf();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getPreviousLeaf() {
        return (N) super.getPreviousLeaf();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getNextNode() {
        return (N) super.getNextNode();
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getPreviousNode() {
        return (N) super.getPreviousNode();
    }

    @SuppressWarnings({"unchecked"})
    public N getSharedAncestor(N aNode) {
        return (N) getSharedAncestor((DefaultMutableTreeNode) aNode);
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public N getChildAt(int index) {
        return (N) super.getChildAt(index);
    }


    @Override
    public void add(N node) {
        super.add(node);
    }

    @Override
    public void remove(N node) {
        super.remove(node);
    }

    @Override
    public void insert(N node, int position) {
        super.insert(node, position);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy