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

org.nuiton.jaxx.runtime.swing.nav.treetable.NavTreeTableNode 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.treetable;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.treetable.DefaultMutableTreeTableNode;
import org.jdesktop.swingx.treetable.MutableTreeTableNode;
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 java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

/**
 * Implementation of {@link NavNode} used to create in tree table
 * This node extends {@link DefaultMutableTreeTableNode} used by
 * {@link JXTreeTable}
 *
 * @author Sylvain Lletellier
 * @since 2.2
 */
public class NavTreeTableNode> extends DefaultMutableTreeTableNode 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 NavTreeTableNodeChildLoador childLoador;

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

    public NavTreeTableNode(Class internalClass,
                            String id,
                            String context,
                            NavTreeTableNodeChildLoador 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;
    }

    public boolean isRoot() {
        return getParent() == null;
    }

    @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 = 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()) {
            N node = enumeration.nextElement();
            if (id.equals(node.getId())) {
                return node;
            }
        }

        // 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()) {

            // 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((NavTreeTableBridge) 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 getChildAt(int index) {
        return (N) super.getChildAt(index);
    }

    // It's a hack to prevent concurent modification exception, caused by
    // children() of AbstractMutableTreeTableNode return Collections.enumeration
    @SuppressWarnings({"unchecked"})
    @Override
    public Enumeration children() {
        List newChildrenList =
                new ArrayList<>(children);

        return (Enumeration) Collections.enumeration(newChildrenList);
    }

    public void removeAllChildren() {
        children.clear();
    }

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

    @SuppressWarnings({"unchecked"})
    public N[] getPathToRoot(NavTreeTableNode aNode, int depth) {

        NavTreeTableNode[] retNodes;

        /* Check for null, in case someone passed in a null node, or
         they passed in an element that isn't rooted at root. */
        if (aNode == null) {
            if (depth == 0) {
                return null;
            } else {
                retNodes = new NavTreeTableNode[depth];
            }
        } else {
            depth++;
            retNodes = getPathToRoot(aNode.getParent(), depth);
            retNodes[retNodes.length - depth] = aNode;
        }
        return (N[]) retNodes;
    }

    @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