OSDN Git Service

[#34405] Define and use default table types not only getTableTypes (DatabaseInfoTree)
[stew/Stew4.git] / src / net / argius / stew / ui / window / DatabaseInfoTree.java
index 921e4aa..267b194 100644 (file)
@@ -7,11 +7,11 @@ import static java.awt.event.KeyEvent.VK_C;
 import static java.util.Collections.emptyList;
 import static java.util.Collections.nCopies;
 import static javax.swing.KeyStroke.getKeyStroke;
+import static net.argius.stew.text.TextUtilities.join;
 import static net.argius.stew.ui.window.AnyActionKey.copy;
 import static net.argius.stew.ui.window.AnyActionKey.refresh;
 import static net.argius.stew.ui.window.DatabaseInfoTree.ActionKey.*;
 import static net.argius.stew.ui.window.WindowOutputProcessor.showInformationMessageDialog;
-
 import java.awt.*;
 import java.awt.event.*;
 import java.io.*;
@@ -19,13 +19,10 @@ import java.sql.*;
 import java.util.*;
 import java.util.Map.Entry;
 import java.util.List;
-
 import javax.swing.*;
 import javax.swing.event.*;
 import javax.swing.tree.*;
-
 import net.argius.stew.*;
-import net.argius.stew.text.*;
 
 /**
  * The Database Information Tree is a tree pane that provides to
@@ -47,6 +44,13 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
     private static final Logger log = Logger.getLogger(DatabaseInfoTree.class);
     private static final ResourceManager res = ResourceManager.getInstance(DatabaseInfoTree.class);
 
+    private static final String TABLE_TYPE_TABLE = "TABLE";
+    private static final String TABLE_TYPE_VIEW = "VIEW";
+    private static final String TABLE_TYPE_INDEX = "INDEX";
+    private static final String TABLE_TYPE_SEQUENCE = "SEQUENCE";
+    private static final List<String> DEFAULT_TABLE_TYPES = //
+    Arrays.asList(TABLE_TYPE_TABLE, TABLE_TYPE_VIEW, TABLE_TYPE_INDEX, TABLE_TYPE_SEQUENCE);
+
     static volatile boolean showColumnNumber;
 
     private Connector currentConnector;
@@ -76,30 +80,16 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         }
     }
 
-    static Set<String> collectTableName(TreePath[] paths, List<ColumnNode> out) {
-        Set<String> a = new LinkedHashSet<String>();
-        for (TreePath path : paths) {
-            InfoNode node = (InfoNode)path.getLastPathComponent();
-            if (node instanceof TableNode) {
-                TableNode tableNode = (TableNode)node;
-                if (tableNode.isKindOfTable()) {
-                    a.add(tableNode.getNodeFullName());
-                }
-            } else if (node instanceof ColumnNode) {
-                ColumnNode columnNode = (ColumnNode)node;
-                if (columnNode.getTableNode().isKindOfTable()) {
-                    a.add(columnNode.getTableNode().getName());
-                    out.add(columnNode);
-                }
-            }
-        }
-        return a;
-    }
-
     @Override
     public void anyActionPerformed(AnyActionEvent ev) {
         log.atEnter("anyActionPerformed", ev);
-        if (ev.isAnyOf(copySimpleName)) {
+        if (ev.isAnyOf(copy)) {
+            final String cmd = ev.getActionCommand();
+            Action action = getActionMap().get(cmd);
+            if (action != null) {
+                action.actionPerformed(new ActionEvent(this, 1001, cmd));
+            }
+        } else if (ev.isAnyOf(copySimpleName)) {
             copySimpleName();
         } else if (ev.isAnyOf(copyFullName)) {
             copyFullName();
@@ -108,49 +98,35 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
                 refresh((InfoNode)path.getLastPathComponent());
             }
         } else if (ev.isAnyOf(generateWherePhrase)) {
-            TreePath[] paths = getSelectionPaths();
-            List<ColumnNode> columns = new ArrayList<ColumnNode>();
-            Set<String> tableNames = collectTableName(paths, columns);
-            if (!tableNames.isEmpty()) {
-                insertTextToTextArea(generateEquivalentJoinClause(columns));
+            List<ColumnNode> columnNodes = new ArrayList<ColumnNode>();
+            for (TreeNode node : getSelectionNodes()) {
+                if (node instanceof ColumnNode) {
+                    columnNodes.add((ColumnNode)node);
+                }
+            }
+            final String phrase = generateEquivalentJoinClause(columnNodes);
+            if (phrase.length() > 0) {
+                insertTextIntoTextArea(addCommas(phrase));
             }
         } else if (ev.isAnyOf(generateSelectPhrase)) {
-            TreePath[] paths = getSelectionPaths();
-            List<ColumnNode> columns = new ArrayList<ColumnNode>();
-            Set<String> tableNames = collectTableName(paths, columns);
-            if (!tableNames.isEmpty()) {
-                List<String> columnNames = new ArrayList<String>();
-                final boolean one = tableNames.size() == 1;
-                for (ColumnNode node : columns) {
-                    columnNames.add(one ? node.getName() : node.getNodeFullName());
-                }
-                final String columnString = (columnNames.isEmpty())
-                        ? "*"
-                        : TextUtilities.join(", ", columnNames);
-                final String tableString = joinByComma(new ArrayList<String>(tableNames));
-                insertTextToTextArea(String.format("SELECT %s FROM %s WHERE ", columnString, tableString));
-            }
-        } else if (ev.isAnyOf(generateUpdateStatement)) {
-            TreePath[] paths = getSelectionPaths();
-            List<ColumnNode> columns = new ArrayList<ColumnNode>();
-            Set<String> tableNames = collectTableName(paths, columns);
-            if (tableNames.isEmpty()) {
-                return;
-            }
-            if (tableNames.size() != 1 || columns.isEmpty()) {
-                showInformationMessageDialog(this, res.get("e.enables-select-just-1-table"), "");
-            } else {
-                final String tableName = tableNames.toArray(new String[1])[0];
-                List<String> columnExpressions = new ArrayList<String>();
-                for (ColumnNode columnNode : columns) {
-                    columnExpressions.add(columnNode.getName() + "=?");
+            final String phrase = generateSelectPhrase(getSelectionNodes());
+            if (phrase.length() > 0) {
+                insertTextIntoTextArea(phrase + " WHERE ");
+            }
+        } else if (ev.isAnyOf(generateUpdateStatement, generateInsertStatement)) {
+            final boolean isInsert = ev.isAnyOf(generateInsertStatement);
+            try {
+                final String phrase = generateUpdateOrInsertPhrase(getSelectionNodes(), isInsert);
+                if (phrase.length() > 0) {
+                    if (isInsert) {
+                        insertTextIntoTextArea(addCommas(phrase));
+                    } else {
+                        insertTextIntoTextArea(phrase + " WHERE ");
+                    }
                 }
-                insertTextToTextArea(String.format("UPDATE %s SET %s WHERE ",
-                                                   tableName,
-                                                   joinByComma(columnExpressions)));
+            } catch (IllegalArgumentException ex) {
+                showInformationMessageDialog(this, ex.getMessage(), "");
             }
-        } else if (ev.isAnyOf(generateInsertStatement)) {
-            generateInsertStatement();
         } else if (ev.isAnyOf(jumpToColumnByName)) {
             jumpToColumnByName();
         } else if (ev.isAnyOf(toggleShowColumnNumber)) {
@@ -162,20 +138,26 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         log.atExit("anyActionPerformed");
     }
 
-    private void insertTextToTextArea(String s) {
-        AnyActionEvent ev = new AnyActionEvent(this, ConsoleTextArea.ActionKey.insertText, s);
-        anyActionListener.anyActionPerformed(ev);
+    @Override
+    public TreePath[] getSelectionPaths() {
+        TreePath[] a = super.getSelectionPaths();
+        if (a == null) {
+            return new TreePath[0];
+        }
+        return a;
     }
 
-    private static String joinByComma(List<?> a) {
-        StringBuilder buffer = new StringBuilder();
-        for (int i = 0, n = a.size(); i < n; i++) {
-            if (i != 0) {
-                buffer.append(", ");
-            }
-            buffer.append(a.get(i));
+    List<TreeNode> getSelectionNodes() {
+        List<TreeNode> a = new ArrayList<TreeNode>();
+        for (TreePath path : getSelectionPaths()) {
+            a.add((TreeNode)path.getLastPathComponent());
         }
-        return buffer.toString();
+        return a;
+    }
+
+    private void insertTextIntoTextArea(String s) {
+        AnyActionEvent ev = new AnyActionEvent(this, ConsoleTextArea.ActionKey.insertText, s);
+        anyActionListener.anyActionPerformed(ev);
     }
 
     private void copySimpleName() {
@@ -220,41 +202,6 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         ClipboardHelper.setStrings(names);
     }
 
-    private void generateInsertStatement() {
-        TreePath[] paths = getSelectionPaths();
-        List<ColumnNode> columns = new ArrayList<ColumnNode>();
-        Set<String> tableNames = collectTableName(paths, columns);
-        if (tableNames.isEmpty()) {
-            return;
-        }
-        if (tableNames.size() != 1) {
-            showInformationMessageDialog(this, res.get("e.enables-select-just-1-table"), "");
-            return;
-        }
-        final String tableName = tableNames.toArray(new String[1])[0];
-        List<String> columnNames = new ArrayList<String>();
-        final Iterable<ColumnNode> columnNodes;
-        if (columns.isEmpty()) {
-            final TreePath path = paths[0];
-            TableNode tableNode = (TableNode)path.getLastPathComponent();
-            if (tableNode.getChildCount() == 0) {
-                expandPath(path);
-            }
-            @SuppressWarnings("unchecked")
-            Iterable<ColumnNode> it = Collections.list(tableNode.children());
-            columnNodes = it;
-        } else {
-            columnNodes = columns;
-        }
-        for (ColumnNode node : columnNodes) {
-            columnNames.add(node.getName());
-        }
-        insertTextToTextArea(String.format("INSERT INTO %s (%s) VALUES (%s);",
-                                           tableName,
-                                           joinByComma(columnNames),
-                                           joinByComma(nCopies(columnNames.size(), "?"))));
-    }
-
     private void jumpToColumnByName() {
         TreePath[] paths = getSelectionPaths();
         if (paths == null || paths.length == 0) {
@@ -271,6 +218,165 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         }
     }
 
+    private static String addCommas(String phrase) {
+        int c = 0;
+        for (final char ch : phrase.toCharArray()) {
+            if (ch == '?') {
+                ++c;
+            }
+        }
+        if (c >= 1) {
+            return String.format("%s;%s", phrase, join("", nCopies(c - 1, ",")));
+        }
+        return phrase;
+    }
+
+    static String generateEquivalentJoinClause(List<ColumnNode> nodes) {
+        if (nodes.isEmpty()) {
+            return "";
+        }
+        Set<String> tableNames = new LinkedHashSet<String>();
+        ListMap columnMap = new ListMap();
+        for (ColumnNode node : nodes) {
+            final String tableName = node.getTableNode().getNodeFullName();
+            final String columnName = node.getName();
+            tableNames.add(tableName);
+            columnMap.add(columnName, String.format("%s.%s", tableName, columnName));
+        }
+        assert tableNames.size() >= 1;
+        List<String> expressions = new ArrayList<String>();
+        if (tableNames.size() == 1) {
+            for (ColumnNode node : nodes) {
+                expressions.add(String.format("%s=?", node.getName()));
+            }
+        } else { // size >= 2
+            List<String> expressions2 = new ArrayList<String>();
+            for (Entry<String, List<String>> entry : columnMap.entrySet()) {
+                List<String> a = entry.getValue();
+                final int n = a.size();
+                assert n >= 1;
+                expressions2.add(String.format("%s=?", a.get(0)));
+                if (n >= 2) {
+                    for (int i = 0; i < n; i++) {
+                        for (int j = i + 1; j < n; j++) {
+                            expressions.add(String.format("%s=%s", a.get(i), a.get(j)));
+                        }
+                    }
+                }
+            }
+            expressions.addAll(expressions2);
+        }
+        return String.format("%s", join(" AND ", expressions));
+    }
+
+    static String generateSelectPhrase(List<TreeNode> nodes) {
+        Set<String> tableNames = new LinkedHashSet<String>();
+        ListMap columnMap = new ListMap();
+        for (TreeNode node : nodes) {
+            if (node instanceof TableNode) {
+                final String tableFullName = ((TableNode)node).getNodeFullName();
+                tableNames.add(tableFullName);
+                columnMap.add(tableFullName);
+            } else if (node instanceof ColumnNode) {
+                ColumnNode cn = (ColumnNode)node;
+                final String tableFullName = cn.getTableNode().getNodeFullName();
+                tableNames.add(tableFullName);
+                columnMap.add(tableFullName, cn.getNodeFullName());
+            }
+        }
+        if (tableNames.isEmpty()) {
+            return "";
+        }
+        List<String> columnNames = new ArrayList<String>();
+        if (tableNames.size() == 1) {
+            List<String> a = new ArrayList<String>();
+            for (TreeNode node : nodes) {
+                if (node instanceof ColumnNode) {
+                    ColumnNode cn = (ColumnNode)node;
+                    a.add(cn.getName());
+                }
+            }
+            if (a.isEmpty()) {
+                columnNames.add("*");
+            } else {
+                columnNames.addAll(a);
+            }
+        } else { // size >= 2
+            for (Entry<String, List<String>> entry : columnMap.entrySet()) {
+                final List<String> columnsInTable = entry.getValue();
+                if (columnsInTable.isEmpty()) {
+                    columnNames.add(entry.getKey() + ".*");
+                } else {
+                    columnNames.addAll(columnsInTable);
+                }
+            }
+        }
+        return String.format("SELECT %s FROM %s", join(", ", columnNames), join(", ", tableNames));
+    }
+
+    static String generateUpdateOrInsertPhrase(List<TreeNode> nodes, boolean isInsert) {
+        Set<String> tableNames = new LinkedHashSet<String>();
+        ListMap columnMap = new ListMap();
+        for (TreeNode node : nodes) {
+            if (node instanceof TableNode) {
+                final String tableFullName = ((TableNode)node).getNodeFullName();
+                tableNames.add(tableFullName);
+                columnMap.add(tableFullName);
+            } else if (node instanceof ColumnNode) {
+                ColumnNode cn = (ColumnNode)node;
+                final String tableFullName = cn.getTableNode().getNodeFullName();
+                tableNames.add(tableFullName);
+                columnMap.add(tableFullName, cn.getName());
+            }
+        }
+        if (tableNames.isEmpty()) {
+            return "";
+        }
+        if (tableNames.size() >= 2) {
+            throw new IllegalArgumentException(res.get("e.enables-select-just-1-table"));
+        }
+        final String tableName = join("", tableNames);
+        List<String> columnsInTable = columnMap.get(tableName);
+        if (columnsInTable.isEmpty()) {
+            if (isInsert) {
+                List<TableNode> tableNodes = new ArrayList<TableNode>();
+                for (TreeNode node : nodes) {
+                    if (node instanceof TableNode) {
+                        tableNodes.add((TableNode)node);
+                        break;
+                    }
+                }
+                TableNode tableNode = tableNodes.get(0);
+                if (tableNode.getChildCount() == 0) {
+                    throw new IllegalArgumentException(res.get("i.can-only-use-after-tablenode-expanded"));
+                }
+                for (int i = 0, n = tableNode.getChildCount(); i < n; i++) {
+                    ColumnNode child = (ColumnNode)tableNode.getChildAt(i);
+                    columnsInTable.add(child.getName());
+                }
+            } else {
+                return "";
+            }
+        }
+        final String phrase;
+        if (isInsert) {
+            final int columnCount = columnsInTable.size();
+            phrase = String.format("INSERT INTO %s (%s) VALUES (%s)",
+                                   tableName,
+                                   join(",", columnsInTable),
+                                   join(",", nCopies(columnCount, "?")));
+        } else {
+            List<String> columnExpressions = new ArrayList<String>();
+            for (final String columnName : columnsInTable) {
+                columnExpressions.add(columnName + "=?");
+            }
+            phrase = String.format("UPDATE %s SET %s", tableName, join(", ", columnExpressions));
+        }
+        return phrase;
+    }
+
+    // text-search
+
     @Override
     public boolean search(Matcher matcher) {
         return search(resolveTargetPath(getSelectionPath()), matcher);
@@ -318,6 +424,8 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         // empty
     }
 
+    // node expansion
+
     /**
      * Refreshes the root and its children.
      * @param env Environment
@@ -384,7 +492,7 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         this.currentConnector = c;
         // auto-expansion
         try {
-            File confFile = new File(Bootstrap.getDirectory(), "autoexpansion.tsv");
+            File confFile = Bootstrap.getSystemFile("autoexpansion.tsv");
             if (confFile.exists() && confFile.length() > 0) {
                 AnyAction aa = new AnyAction(this);
                 Scanner r = new Scanner(confFile);
@@ -502,6 +610,12 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
                         }
                     });
                 } catch (SQLException ex) {
+                    try {
+                        if (dbmeta.getConnection().isClosed())
+                            return;
+                    } catch (SQLException exx) {
+                        ex.setNextException(exx);
+                    }
                     throw new RuntimeException(ex);
                 }
             }
@@ -523,55 +637,6 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         }
     }
 
-    static String generateEquivalentJoinClause(List<ColumnNode> nodes) {
-        if (nodes.isEmpty()) {
-            return "";
-        }
-        ListMap tm = new ListMap();
-        ListMap cm = new ListMap();
-        for (ColumnNode node : nodes) {
-            final String tableName = node.getTableNode().getName();
-            final String columnName = node.getName();
-            tm.add(tableName, columnName);
-            cm.add(columnName, String.format("%s.%s", tableName, columnName));
-        }
-        List<String> expressions = new ArrayList<String>();
-        if (tm.size() == 1) {
-            for (ColumnNode node : nodes) {
-                expressions.add(String.format("%s=?", node.getName()));
-            }
-        } else {
-            final String tableName = nodes.get(0).getTableNode().getName();
-            for (String c : tm.get(tableName)) {
-                expressions.add(String.format("%s.%s=?", tableName, c));
-            }
-            for (Entry<String, List<String>> entry : cm.entrySet()) {
-                if (!entry.getKey().equals(tableName) && entry.getValue().size() == 1) {
-                    expressions.add(String.format("%s=?", entry.getValue().get(0)));
-                }
-            }
-            for (Entry<String, List<String>> entry : cm.entrySet()) {
-                Object[] a = entry.getValue().toArray();
-                final int n = a.length;
-                for (int i = 0; i < n; i++) {
-                    for (int j = i + 1; j < n; j++) {
-                        expressions.add(String.format("%s=%s", a[i], a[j]));
-                    }
-                }
-            }
-        }
-        return TextUtilities.join(" AND ", expressions) + ';';
-    }
-
-    @Override
-    public TreePath[] getSelectionPaths() {
-        TreePath[] a = super.getSelectionPaths();
-        if (a == null) {
-            return new TreePath[0];
-        }
-        return a;
-    }
-
     // subclasses
 
     private static final class ListMap extends LinkedHashMap<String, List<String>> {
@@ -580,11 +645,13 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
             // empty
         }
 
-        void add(String key, String value) {
+        void add(String key, String... values) {
             if (get(key) == null) {
                 put(key, new ArrayList<String>());
             }
-            get(key).add(value);
+            for (String value : values) {
+                get(key).add(value);
+            }
         }
 
     }
@@ -650,20 +717,28 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         static List<TableTypeNode> getTableTypeNodes(DatabaseMetaData dbmeta,
                                                      String catalog,
                                                      String schema) throws SQLException {
-            List<TableTypeNode> a = new ArrayList<TableTypeNode>();
-            ResultSet rs = dbmeta.getTableTypes();
+            List<String> tableTypes = new ArrayList<String>(DEFAULT_TABLE_TYPES);
             try {
-                while (rs.next()) {
-                    TableTypeNode typeNode = new TableTypeNode(catalog, schema, rs.getString(1));
-                    if (typeNode.hasItems(dbmeta)) {
-                        a.add(typeNode);
+                ResultSet rs = dbmeta.getTableTypes();
+                try {
+                    while (rs.next()) {
+                        final String tableType = rs.getString(1);
+                        if (!DEFAULT_TABLE_TYPES.contains(tableType)) {
+                            tableTypes.add(tableType);
+                        }
                     }
+                } finally {
+                    rs.close();
                 }
-            } finally {
-                rs.close();
+            } catch (SQLException ex) {
+                log.warn("getTableTypes at getTableTypeNodes", ex);
             }
-            if (a.isEmpty()) {
-                a.add(new TableTypeNode(catalog, schema, "TABLE"));
+            List<TableTypeNode> a = new ArrayList<TableTypeNode>();
+            for (final String tableType : tableTypes) {
+                TableTypeNode typeNode = new TableTypeNode(catalog, schema, tableType);
+                if (typeNode.hasItems(dbmeta)) {
+                    a.add(typeNode);
+                }
             }
             return a;
         }
@@ -777,8 +852,7 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
                 while (rs.next()) {
                     final String table = rs.getString(3);
                     final String type = rs.getString(4);
-                    final boolean kindOfTable = type.matches("TABLE|VIEW|SYNONYM");
-                    a.add(new TableNode(catalog, schema, table, kindOfTable));
+                    a.add(new TableNode(catalog, schema, table, type));
                 }
             } finally {
                 rs.close();
@@ -811,14 +885,14 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
         private final String catalog;
         private final String schema;
         private final String name;
-        private final boolean kindOfTable;
+        private final String tableType;
 
-        TableNode(String catalog, String schema, String name, boolean kindOfTable) {
+        TableNode(String catalog, String schema, String name, String tableType) {
             super(name);
             this.catalog = catalog;
             this.schema = schema;
             this.name = name;
-            this.kindOfTable = kindOfTable;
+            this.tableType = tableType;
         }
 
         @Override
@@ -841,6 +915,9 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
 
         @Override
         public boolean isLeaf() {
+            if (TABLE_TYPE_SEQUENCE.equals(tableType)) {
+                return true;
+            }
             return false;
         }
 
@@ -854,17 +931,13 @@ final class DatabaseInfoTree extends JTree implements AnyActionListener, TextSea
                 a.add(schema);
             }
             a.add(name);
-            return TextUtilities.join(".", a);
+            return join(".", a);
         }
 
         String getName() {
             return name;
         }
 
-        boolean isKindOfTable() {
-            return kindOfTable;
-        }
-
     }
 
     static final class ColumnNode extends InfoNode {