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         MediatorHelper.frame().getSplitNS().invokeLaterWithSplitOrientation(() -> {
129             // Report NullPointerException #1683
130             DefaultMutableTreeNode node = this.getTreeNodeModels().get(table);
131             if (node != null) {
132                 AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
133                 progressingTreeNodeModel.setIndexProgress(table.getChildCount());  // Update the progress value of the model, end the progress
134                 progressingTreeNodeModel.setRunning(false);  // Mark the node model as 'no stop/pause/resume button'
135                 MediatorHelper.tabResults().addTabValues(data, columnNames, table);
136             }
137         });
138     }
139     
140     public void endIndeterminateProgress(AbstractElementDatabase dataElementDatabase) {
141         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
142         // Fix #1806 : NullPointerException on ...odels().get(dataElementDatabase).getUserObject()
143         if (node != null) {
144             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
145             progressingTreeNodeModel.setProgressing(false);  // Mark the node model as 'no loading bar'
146             progressingTreeNodeModel.setRunning(false);  // Mark the node model as 'no stop/pause/resume button'
147             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
148             treeModel.nodeChanged(node);  // Update the node
149         }
150     }
151     
152     public void endProgress(AbstractElementDatabase dataElementDatabase) {
153         // Report NullPointerException #1671
154         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
155         if (node != null) {
156             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
157             progressingTreeNodeModel.setLoading(false);  // Mark the node model as 'no progress bar'
158             progressingTreeNodeModel.setRunning(false);  // Mark the node model as 'no stop/pause/resume button'
159             progressingTreeNodeModel.setIndexProgress(0);  // Reset the progress value of the model
160             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
161             treeModel.nodeChanged(node);  // Update the node and progress bar
162         }
163     }
164     
165     public void startIndeterminateProgress(AbstractElementDatabase dataElementDatabase) {
166         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
167         // Fix #45540: NullPointerException on node.getUserObject()
168         if (node != null) {
169             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
170             progressingTreeNodeModel.setProgressing(true);  // Mark the node model as 'loading'
171             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
172             treeModel.nodeChanged(node);  // Update the node
173         }
174     }
175     
176     public void startProgress(AbstractElementDatabase dataElementDatabase) {
177         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
178         // Fix rare #73981
179         if (node != null) {
180             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
181             // Fix rare Unhandled NullPointerException #66340
182             if (progressingTreeNodeModel != null) {
183                 progressingTreeNodeModel.setLoading(true);  // Mark the node model as 'display progress bar'
184             }
185             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
186             treeModel.nodeChanged(node);  // Update the node
187         }
188     }
189     
190     public void updateProgress(AbstractElementDatabase dataElementDatabase, int dataCount) {
191         DefaultMutableTreeNode node = this.getTreeNodeModels().get(dataElementDatabase);
192         // Fix Report #1368: ignore if no element database, usually for mock (e.g. metadata, file, shell, list databases)
193         if (node != null) {
194             AbstractNodeModel progressingTreeNodeModel = (AbstractNodeModel) node.getUserObject();
195             progressingTreeNodeModel.setIndexProgress(dataCount);  // Update the progress value of the model
196             DefaultTreeModel treeModel = (DefaultTreeModel) this.getModel();
197             treeModel.nodeChanged(node);  // Update the node
198         }
199     }
200     
201     public final Map<AbstractElementDatabase, DefaultMutableTreeNode> getTreeNodeModels() {
202         return this.mapNodes;
203     }
204 }