View Javadoc
1   package com.jsql.view.swing.tree;
2   
3   import com.jsql.model.bean.database.AbstractElementDatabase;
4   import com.jsql.model.bean.database.Column;
5   import com.jsql.model.bean.database.Database;
6   import com.jsql.model.bean.database.Table;
7   import com.jsql.util.LogLevelUtil;
8   import com.jsql.view.swing.tree.model.AbstractNodeModel;
9   import com.jsql.view.swing.tree.model.NodeModelColumn;
10  import com.jsql.view.swing.tree.model.NodeModelDatabase;
11  import com.jsql.view.swing.tree.model.NodeModelTable;
12  import com.jsql.view.swing.util.MediatorHelper;
13  import org.apache.logging.log4j.LogManager;
14  import org.apache.logging.log4j.Logger;
15  
16  import javax.swing.*;
17  import javax.swing.tree.DefaultMutableTreeNode;
18  import javax.swing.tree.DefaultTreeModel;
19  import javax.swing.tree.TreePath;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  public class TreeDatabase extends JTree {
25      
26      private static final Logger LOGGER = LogManager.getRootLogger();
27  
28      /**
29       * Map a database element with the corresponding tree node.<br>
30       * The injection model sends a database element to the view, then
31       * the view accesses its graphic component for update.
32       */
33      private final transient Map<AbstractElementDatabase, DefaultMutableTreeNode> mapNodes = new HashMap<>();
34  
35      public TreeDatabase(DefaultMutableTreeNode root) {
36          super(root);
37      }
38      
39      public void reloadNodes() {
40          if (this.isRootVisible()) {  // I18n of tree empty node
41              DefaultTreeModel model = (DefaultTreeModel) this.getModel();
42              DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
43              model.reload(root);
44              this.revalidate();
45          }
46      }
47  
48      public void reset() {
49          this.getTreeNodeModels().clear();
50  
51          DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();  // Tree model for refreshing the tree
52          DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();  // The tree root
53  
54          root.removeAllChildren();  // Remove tree nodes
55          treeModel.nodeChanged(root);  // Refresh the root
56          treeModel.reload();  // Refresh the tree
57          
58          this.setRootVisible(true);
59      }
60      
61      public void addColumns(List<Column> columns) {
62          DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
63          DefaultMutableTreeNode tableNode = null;  // The table to update
64  
65          // Loop into the list of columns
66          for (Column column: columns) {
67              AbstractNodeModel newTreeNodeModel = new NodeModelColumn(column);  // Create a node model with the column element
68              var newNode = new DefaultMutableTreeNode(newTreeNodeModel);  // Create the node
69              tableNode = this.getTreeNodeModels().get(column.getParent());  // Get the parent table
70              
71              // Fix #1805 : NullPointerException on tableNode.getChildCount()
72              if (tableNode != null) {
73                  treeModel.insertNodeInto(newNode, tableNode, tableNode.getChildCount());  // Add the column to the table
74              }
75          }
76  
77          if (tableNode != null) {
78              this.expandPath(new TreePath(tableNode.getPath()));  // Open the table node
79              ((AbstractNodeModel) tableNode.getUserObject()).setLoaded(true);  // The table has just been search (avoid double check)
80          }
81      }
82      
83      public void addDatabases(List<Database> databases) {
84          DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
85          DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot();  // First node in tree
86  
87          // Loop into the list of databases
88          for (Database database: databases) {
89              AbstractNodeModel newTreeNodeModel = new NodeModelDatabase(database);  // Create a node model with the database element
90              var newNode = new DefaultMutableTreeNode(newTreeNodeModel);  // Create the node
91              this.getTreeNodeModels().put(database, newNode);  // Save the node
92              root.add(newNode);  // Add the node to the tree
93          }
94  
95          // Refresh the tree
96          treeModel.reload(root);
97          
98          // Open the root node
99          this.expandPath(new TreePath(root.getPath()));
100         this.setRootVisible(false);
101     }
102     
103     public void addTables(List<Table> tables) {
104         DefaultMutableTreeNode databaseNode = null;  // The database to update
105 
106         for (Table table: tables) {
107             AbstractNodeModel newTreeNodeModel = new NodeModelTable(table);  // Create a node model with the table element
108             var newNode = new DefaultMutableTreeNode(newTreeNodeModel);  // Create the node
109             this.getTreeNodeModels().put(table, newNode);  // Save the node
110             databaseNode = this.getTreeNodeModels().get(table.getParent());
111 
112             // Report NullPointerException #1670
113             if (databaseNode != null) {
114                 DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
115                 treeModel.insertNodeInto(newNode, databaseNode, databaseNode.getChildCount());  // Add the table to the database
116             } else {
117                 LOGGER.log(LogLevelUtil.CONSOLE_ERROR, "Missing database for table {}.", () -> table);
118             }
119         }
120 
121         if (databaseNode != null) {
122             this.expandPath(new TreePath(databaseNode.getPath()));  // Open the database node
123             ((AbstractNodeModel) databaseNode.getUserObject()).setLoaded(true);  // The database has just been search (avoid double check)
124         }
125     }
126     
127     public void createValuesTab(String[][] data, String[] columnNames, AbstractElementDatabase table) {
128         // Report NullPointerException #1683
129         DefaultMutableTreeNode node = this.getTreeNodeModels().get(table);
130         if (node != null) {
131             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
132             progressingTreeNodeModel.setIndexProgress(table.getChildCount());  // Update the progress value of the model, end the progress
133             progressingTreeNodeModel.setRunning(false);  // Mark the node model as 'no stop/pause/resume button'
134             MediatorHelper.tabResults().addTabValues(data, columnNames, table);
135         }
136     }
137     
138     public void endIndeterminateProgress(AbstractElementDatabase dataElementDatabase) {
139         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
140         // Fix #1806 : NullPointerException on ...odels().get(dataElementDatabase).getUserObject()
141         if (node != null) {
142             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
143             progressingTreeNodeModel.setProgressing(false);  // Mark the node model as 'no loading bar'
144             progressingTreeNodeModel.setRunning(false);  // Mark the node model as 'no stop/pause/resume button'
145             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
146             treeModel.nodeChanged(node);  // Update the node
147         }
148     }
149     
150     public void endProgress(AbstractElementDatabase dataElementDatabase) {
151         // Report NullPointerException #1671
152         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
153         if (node != null) {
154             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
155             progressingTreeNodeModel.setLoading(false);  // Mark the node model as 'no progress bar'
156             progressingTreeNodeModel.setRunning(false);  // Mark the node model as 'no stop/pause/resume button'
157             progressingTreeNodeModel.setIndexProgress(0);  // Reset the progress value of the model
158             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
159             treeModel.nodeChanged(node);  // Update the node and progress bar
160         }
161     }
162     
163     public void startIndeterminateProgress(AbstractElementDatabase dataElementDatabase) {
164         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
165         // Fix #45540: NullPointerException on node.getUserObject()
166         if (node != null) {
167             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
168             progressingTreeNodeModel.setProgressing(true);  // Mark the node model as 'loading'
169             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
170             treeModel.nodeChanged(node);  // Update the node
171         }
172     }
173     
174     public void startProgress(AbstractElementDatabase dataElementDatabase) {
175         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
176         // Fix rare #73981
177         if (node != null) {
178             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
179             // Fix rare Unhandled NullPointerException #66340
180             if (progressingTreeNodeModel != null) {
181                 progressingTreeNodeModel.setLoading(true);  // Mark the node model as 'display progress bar'
182             }
183             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
184             treeModel.nodeChanged(node);  // Update the node
185         }
186     }
187     
188     public void updateProgress(AbstractElementDatabase dataElementDatabase, int dataCount) {
189         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
190         // Fix Report #1368: ignore if no element database, usually for mock (e.g. metadata, file, shell, list databases)
191         if (node != null) {
192             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
193             progressingTreeNodeModel.setIndexProgress(dataCount);  // Update the progress value of the model
194             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
195             treeModel.nodeChanged(node);  // Update the node
196         }
197     }
198     
199     public final Map<AbstractElementDatabase, DefaultMutableTreeNode> getTreeNodeModels() {
200         return this.mapNodes;
201     }
202 }