diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ContextMenuContributor.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ContextMenuContributor.java new file mode 100644 index 0000000..54f7c73 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ContextMenuContributor.java @@ -0,0 +1,12 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import java.util.List; + +public interface ContextMenuContributor { + + boolean supports(TreeNode node); + + void contribute(TreeNode node); + + List getActions(TreeNode node); +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ContextMenuRegistry.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ContextMenuRegistry.java new file mode 100644 index 0000000..7175f34 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ContextMenuRegistry.java @@ -0,0 +1,20 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import java.util.ArrayList; +import java.util.List; + +public class ContextMenuRegistry { + + private final List contributors = new ArrayList<>(); + + public void register(ContextMenuContributor contributor) { + contributors.add(contributor); + } + + public List getActions(TreeNode node) { + return contributors.stream() + .filter(c -> c.supports(node)) + .flatMap(c -> c.getActions(node).stream()) + .toList(); + } +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/DefaultNodeTypes.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/DefaultNodeTypes.java new file mode 100644 index 0000000..68fd15d --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/DefaultNodeTypes.java @@ -0,0 +1,21 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +public enum DefaultNodeTypes implements TreeNodeType { + PROJECT, + DATABASES, + DATABASE, + TABLES, + TABLE, + COLUMNS, + COLUMN, + INDICES, + INDEX, + VIEWS, + VIEW, + JOB_AGENT; + + @Override + public String getId() { + return this.name(); + } +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ExplorerEventBus.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ExplorerEventBus.java new file mode 100644 index 0000000..1fac253 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ExplorerEventBus.java @@ -0,0 +1,21 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import java.util.ArrayList; +import java.util.List; + +public class ExplorerEventBus { + + private final List listeners = new ArrayList<>(); + + public void register(TreeListener listener) { + listeners.add(listener); + } + + public void fireSelected(TreeNode node) { + listeners.forEach(l -> l.onNodeSelected(node)); + } + + public void fireDoubleClick(TreeNode node) { + listeners.forEach(l -> l.onNodeDoubleClicked(node)); + } +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/IconProvider.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/IconProvider.java new file mode 100644 index 0000000..0638efb --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/IconProvider.java @@ -0,0 +1,7 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import javax.swing.*; + +public interface IconProvider { + Icon getIcon(TreeNode node); +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/NodeChildrenProvider.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/NodeChildrenProvider.java new file mode 100644 index 0000000..449dd17 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/NodeChildrenProvider.java @@ -0,0 +1,10 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import java.util.List; + +public interface NodeChildrenProvider { + + boolean supports(TreeNode node); + + List loadChildren(TreeNode node); +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/NodeStructureRegistry.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/NodeStructureRegistry.java new file mode 100644 index 0000000..9a038c9 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/NodeStructureRegistry.java @@ -0,0 +1,32 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import java.util.ArrayList; +import java.util.List; + +public class NodeStructureRegistry { + + private final List providers = new ArrayList<>(); + + private final List contributors = new ArrayList<>(); + + public void register(NodeChildrenProvider provider) { + providers.add(provider); + } + + public List loadChildren(TreeNode node) { + return providers.stream() + .filter(p -> p.supports(node)) + .flatMap(p -> p.loadChildren(node).stream()) + .toList(); + } + + + public void loadAdditionalChildren(TreeNode node) { + for (ContextMenuContributor c : contributors) { + if (c.supports(node)) { + c.contribute(node); + } + + } + } + } \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ProjectTreeBuilder.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ProjectTreeBuilder.java new file mode 100644 index 0000000..828114c --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/ProjectTreeBuilder.java @@ -0,0 +1,66 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import de.julianweinelt.databench.dbx.database.ADatabase; + +public class ProjectTreeBuilder { + + private final ADatabase database; + private final NodeStructureRegistry structureRegistry; + + public ProjectTreeBuilder(ADatabase database, + NodeStructureRegistry structureRegistry) { + this.database = database; + this.structureRegistry = structureRegistry; + } + + public TreeNode build() { + + TreeNode root = new TreeNode( + DefaultNodeTypes.PROJECT, + "Project", + null + ); + + TreeNode dbRoot = new TreeNode( + DefaultNodeTypes.DATABASES, + "Databases", + null + ); + + root.addChild(dbRoot); + + for (String dbName : database.getDatabases()) { + + TreeNode dbNode = new TreeNode( + DefaultNodeTypes.DATABASE, + dbName, + dbName + ); + + dbRoot.addChild(dbNode); + + loadDatabaseChildren(dbNode, dbName); + } + + return root; + } + + private void loadDatabaseChildren(TreeNode dbNode, String dbName) { + + TreeNode tablesFolder = new TreeNode( + DefaultNodeTypes.TABLES, + "Tables", + null + ); + + dbNode.addChild(tablesFolder); + + for (String table : database.getTables(dbName)) { + tablesFolder.addChild( + new TreeNode(DefaultNodeTypes.TABLE, table, table) + ); + } + + structureRegistry.loadAdditionalChildren(dbNode); + } +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/SwingTreeAdapter.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/SwingTreeAdapter.java new file mode 100644 index 0000000..94fc01c --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/SwingTreeAdapter.java @@ -0,0 +1,16 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import javax.swing.tree.DefaultMutableTreeNode; + +public class SwingTreeAdapter { + public DefaultMutableTreeNode toSwingNode(TreeNode node) { + DefaultMutableTreeNode swing = + new DefaultMutableTreeNode(node); + + for (TreeNode child : node.getChildren()) { + swing.add(toSwingNode(child)); + } + + return swing; + } +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeContextAction.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeContextAction.java new file mode 100644 index 0000000..a30d542 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeContextAction.java @@ -0,0 +1,12 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import javax.swing.*; + +public interface TreeContextAction { + + String getName(); + + Icon getIcon(); + + void execute(TreeNode node); +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeListener.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeListener.java new file mode 100644 index 0000000..8f2df47 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeListener.java @@ -0,0 +1,8 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +public interface TreeListener { + + void onNodeSelected(TreeNode node); + + void onNodeDoubleClicked(TreeNode node); +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeNode.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeNode.java new file mode 100644 index 0000000..99aac3d --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeNode.java @@ -0,0 +1,25 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class TreeNode { + + private final TreeNodeType type; + private final String label; + private final Object value; + private final List children = new ArrayList<>(); + + public TreeNode(TreeNodeType type, String label, Object value) { + this.type = type; + this.label = label; + this.value = value; + } + + public void addChild(TreeNode node) { + children.add(node); + } +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeNodeType.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeNodeType.java new file mode 100644 index 0000000..d965e18 --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeNodeType.java @@ -0,0 +1,5 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +public interface TreeNodeType { + String getId(); +} \ No newline at end of file diff --git a/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeUIRegistry.java b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeUIRegistry.java new file mode 100644 index 0000000..d8159ed --- /dev/null +++ b/dbx/src/main/java/de/julianweinelt/datacat/dbx/api/ui/tree/TreeUIRegistry.java @@ -0,0 +1,18 @@ +package de.julianweinelt.datacat.dbx.api.ui.tree; + +import javax.swing.*; +import java.util.HashMap; +import java.util.Map; + +public class TreeUIRegistry { + + private final Map iconProviders = new HashMap<>(); + + public void registerIcon(TreeNodeType type, IconProvider provider) { + iconProviders.put(type, provider); + } + + public Icon resolveIcon(TreeNode node) { + return iconProviders.getOrDefault(node.getType(), n -> null).getIcon(node); + } +} \ No newline at end of file diff --git a/ui/src/main/java/de/julianweinelt/datacat/api/DConnection.java b/ui/src/main/java/de/julianweinelt/datacat/api/DConnection.java index ab71a8e..22decd8 100644 --- a/ui/src/main/java/de/julianweinelt/datacat/api/DConnection.java +++ b/ui/src/main/java/de/julianweinelt/datacat/api/DConnection.java @@ -1,5 +1,6 @@ package de.julianweinelt.datacat.api; + import de.julianweinelt.datacat.data.Project; import de.julianweinelt.datacat.data.ProjectManager; import de.julianweinelt.datacat.dbx.api.drivers.DriverManagerService; @@ -68,8 +69,6 @@ public class DConnection implements IFileWatcherListener { private final boolean lightEdit; private final DatabaseMetaData databaseTypeMeta; - private final TreeFilter treeFilter = new TreeFilter(); - public DConnection(Project project, BenchUI benchUI) { this.lightEdit = false; this.benchUI = benchUI; @@ -163,6 +162,8 @@ private void showPopup(MouseEvent e, DefaultMutableTreeNode node) { createProjectFolder(); JScrollPane fileTreeScroll = new JScrollPane(fileTree); + JPanel flowPanel = new JPanel(); + JTabbedPane leftTabs = new JTabbedPane(); if (!lightEdit) leftTabs.addTab(translate("project.tabs.database"), projectTreeScroll); leftTabs.addTab(translate("project.tabs.files"), fileTreeScroll); @@ -196,6 +197,7 @@ private void showPopup(MouseEvent e, DefaultMutableTreeNode node) { } private void createProjectFolder() { + //TODO: Use another folder in users home File folder = new File("projects", project.getUuid().toString()); if (folder.mkdirs()) log.debug("Created workspace folder for project {}", project.getName()); fileWatcher = new FileWatcher(folder, this); @@ -322,7 +324,7 @@ public void handleFileEvent(File file) { } else { log.warn("Unknown file type: {}", file.getName()); int val = JOptionPane.showConfirmDialog(benchUI.getFrame(), "Unknown file type: " + file.getName() - + "\n\nShould DataCat try to load it anyway?", "File type mismatch", JOptionPane.YES_NO_OPTION); + + "\n\nShould DataBench try to load it anyway?", "File type mismatch", JOptionPane.YES_NO_OPTION); if (val == JOptionPane.YES_OPTION) { addEditorTab(FileUtil.readFile(file)); } @@ -331,7 +333,7 @@ public void handleFileEvent(File file) { public void addCreateTableTab(String dbContext) { log.info("Selected database context: {}", dbContext); - addTab(new CreateTableTab(this).newTable(dbContext)); + addTab(new CreateTableTab(this).newTable()); } public void addCreateViewTab() { @@ -440,10 +442,10 @@ public void updateTitle(IEditorTab tab) { } public void handleWindowClosing(JFrame frame) { - log.info("Saving tabs..."); FileManager.instance().save(editorTabs, project); boolean unsaved = hasUnsavedChanges(); if (!unsaved) { + frame.dispose(); return; } @@ -472,6 +474,7 @@ public void handleWindowClosing(JFrame frame) { removeTab(tab); } } + frame.dispose(); } public boolean hasUnsavedChanges() { @@ -512,13 +515,9 @@ public CompletableFuture connect() { log.warn("SQL Server (Windows Auth) connection failed: {}", ex.getMessage()); } future.completeExceptionally(ex); - return future; } finally { current.setContextClassLoader(previous); } - - String ver = getDatabaseTypeFromDB(); - log.info("Connected to {} as {}", project.getServer(), ver); return future; } @@ -540,16 +539,6 @@ public boolean checkConnection() { } } - private String getDatabaseTypeFromDB() { - try (ResultSet set = conn.createStatement().executeQuery(databaseTypeMeta.syntax().getType())) { - if (set.next()) return set.getString(1); - } catch (SQLException e) { - log.error(e.getMessage()); - throw new RuntimeException(e); - } - return "Unknown"; - } - public List getDatabases() { List databases = new ArrayList<>(); try (PreparedStatement pS = conn.prepareStatement(databaseTypeMeta.syntax().showDatabases())) { @@ -593,6 +582,7 @@ public SQLAnswer executeSQL(String sql) { } catch (SQLException e) { return new SQLAnswer(false, null, -1, e.getMessage(), -1); } + log.info("Executing SQL: {}", sql); SQLAnswer answer; try (PreparedStatement pS = conn.prepareStatement(sql)) { pS.execute(); @@ -822,14 +812,7 @@ public JPopupMenu createContextMenu(DefaultMutableTreeNode node, BenchUI ui) { else if (name.equals(translate("connection.tree.node.tables.title"))) { JMenuItem create = new JMenuItem("Create new Table"); create.addActionListener(e -> addCreateTableTab(((DefaultMutableTreeNode) node.getParent()).getUserObject().toString())); - - JMenuItem filter = new JMenuItem("Filter..."); - filter.addActionListener(e -> { - treeFilter.showFilterDialog(ui.getFrame()); - }); - menu.add(create); - menu.add(filter); JMenuItem refresh = new JMenuItem(translate("connection.button.refresh")); refresh.addActionListener(e -> getProjectTree()); menu.add(refresh); @@ -857,10 +840,6 @@ else if (node.getParent() != null && int result = JOptionPane.showConfirmDialog(ui.getFrame(), "Do you really want to drop (delete) this table? This cannot be undone!"); if (result == JOptionPane.YES_OPTION) t.execute(); }); - JMenuItem filter = new JMenuItem("Filter..."); - filter.addActionListener(e -> { - treeFilter.showFilterDialog(ui.getFrame()); - }); JMenuItem truncate = new JMenuItem("Truncate Table "); truncate.addActionListener(e -> { String tableName = node.getUserObject().toString(); @@ -881,7 +860,7 @@ else if (node.getParent() != null && String db = ((DefaultMutableTreeNode) node.getParent().getParent()).getUserObject().toString(); String create = getCreateStatement(db, node.getUserObject().toString()); addEditorTab(""" - /** Script generated by DataCat SQL Generator **/ + /** Script generated by DataBench SQL Generator **/ %s """.formatted(create)); }); @@ -889,7 +868,6 @@ else if (node.getParent() != null && menu.add(edit); menu.add(select); menu.add(drop); - menu.add(filter); menu.add(truncate); menu.add(alter); menu.add(generatorMenu);