org.nuiton.jaxx.runtime.swing.nav.tree.NavTreeNode Maven / Gradle / Ivy
The newest version!
/*
* #%L
* JAXX :: Runtime
* %%
* Copyright (C) 2008 - 2024 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, ?, N> childLoador;
protected NavTreeNode(String id) {
this(String.class, id, null, null);
}
public NavTreeNode(Class> internalClass,
String id,
String context,
NavTreeNodeChildLoador, ?, N> 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);
}
}