diff --git a/jOOQ-console/src/main/java/org/jooq/debug/BreakpointHit.java b/jOOQ-console/src/main/java/org/jooq/debug/BreakpointHit.java index 71ad7179f1..64d22de5cb 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/BreakpointHit.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/BreakpointHit.java @@ -48,6 +48,7 @@ public class BreakpointHit implements Serializable { STEP_THROUGH, RUN_OVER, RUN, + FAIL, } private boolean isBeforeExecution; diff --git a/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java b/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java index be09689389..4361d9ab12 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/DebugListener.java @@ -47,6 +47,7 @@ import java.util.List; import org.jooq.ExecuteContext; import org.jooq.ExecuteType; import org.jooq.debug.BreakpointHit.ExecutionType; +import org.jooq.exception.DataAccessException; import org.jooq.impl.DefaultExecuteListener; import org.jooq.impl.Factory; @@ -241,6 +242,9 @@ public class DebugListener extends DefaultExecuteListener { matchingDebugger = null; } switch(executionType) { + case FAIL: { + throw new DataAccessException("Failing SQL statement."); + } case RUN_OVER: { try { ctx.statement().close(); diff --git a/jOOQ-console/src/main/java/org/jooq/debug/LocalStatementExecutor.java b/jOOQ-console/src/main/java/org/jooq/debug/LocalStatementExecutor.java index 4ff5135526..98d7da154b 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/LocalStatementExecutor.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/LocalStatementExecutor.java @@ -81,30 +81,32 @@ public class LocalStatementExecutor implements StatementExecutor { boolean isAllowed = true; if(executorContext.isReadOnly()) { String simplifiedSql = sql.replaceAll("'[^']*'", ""); - Matcher matcher = Pattern.compile("[a-zA-Z_0-9\\$]+").matcher(simplifiedSql); - boolean isFirst = true; - while(matcher.find()) { - String word = simplifiedSql.substring(matcher.start(), matcher.end()).toUpperCase(Locale.ENGLISH); - if(isFirst && !word.equals("SELECT")) { + switch(SqlQueryType.detectType(simplifiedSql)) { + case SELECT: + String[] forbiddenWords = new String[] { + "INSERT", + "UPDATE", + "DELETE", + "ALTER", + "DROP", + "CREATE", + "EXEC", + "EXECUTE", + }; + Matcher matcher = Pattern.compile("[a-zA-Z_0-9\\$]+").matcher(simplifiedSql); + while(matcher.find()) { + String word = simplifiedSql.substring(matcher.start(), matcher.end()).toUpperCase(Locale.ENGLISH); + for(String keyword: forbiddenWords) { + if(word.equals(keyword)) { + isAllowed = false; + break; + } + } + } + break; + default: isAllowed = false; break; - } - isFirst = false; - for(String keyword: new String[] { - "INSERT", - "UPDATE", - "DELETE", - "ALTER", - "DROP", - "CREATE", - "EXEC", - "EXECUTE", - }) { - if(word.equals(keyword)) { - isAllowed = false; - break; - } - } } } if(!isAllowed) { diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointEditor.java b/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointEditor.java index 4d268019ef..52ee9e097d 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointEditor.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointEditor.java @@ -52,13 +52,11 @@ import java.util.Set; import javax.swing.BorderFactory; import javax.swing.Box; -import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JPanel; -import javax.swing.JRadioButton; import org.jooq.debug.Breakpoint; import org.jooq.debug.SqlQueryType; @@ -92,9 +90,7 @@ public class BreakpointEditor extends JPanel { private JPanel processorPane; private JCheckBox beforeExecutionCheckBox; private StatementProcessorPane beforeExecutionProcessorPane; - private JRadioButton executeRadioButton; -// private JRadioButton doNotExecuteRadioButton; - private JRadioButton replaceExecutionRadioButton; + private JComboBox executeTypeComboBox; private StatementProcessorPane replacementExecutionProcessorPane; private JCheckBox afterExecutionCheckBox; private StatementProcessorPane afterExecutionProcessorPane; @@ -228,35 +224,21 @@ public class BreakpointEditor extends JPanel { beforeExecutionProcessorPane = new StatementProcessorPane(beforeExecutionProcessor); processorPane.add(beforeExecutionProcessorPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(2, 5, 0, 0), 0, 0)); y++; - ButtonGroup executionButtonGroup = new ButtonGroup(); StatementProcessor replacementExecutionProcessor = breakpoint.getReplacementExecutionProcessor(); - executeRadioButton = new JRadioButton("Execute"); - executeRadioButton.setOpaque(false); - executionButtonGroup.add(executeRadioButton); - processorPane.add(executeRadioButton, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); - y++; + executeTypeComboBox = new JComboBox(new String[] {"Execute", "Replace with"}); // doNotExecuteRadioButton = new JRadioButton("Do not execute"); // executionButtonGroup.add(doNotExecuteRadioButton); // processorPane.add(doNotExecuteRadioButton, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); // y++; - replaceExecutionRadioButton = new JRadioButton("Replace with: "); - replaceExecutionRadioButton.setOpaque(false); - executionButtonGroup.add(replaceExecutionRadioButton); if(replacementExecutionProcessor != null) { - replaceExecutionRadioButton.setSelected(true); + executeTypeComboBox.setSelectedIndex(1); } else { - executeRadioButton.setSelected(true); + executeTypeComboBox.setSelectedIndex(0); } - processorPane.add(replaceExecutionRadioButton, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); + processorPane.add(executeTypeComboBox, new GridBagConstraints(0, y, 1, 1, 0, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0)); replacementExecutionProcessorPane = new StatementProcessorPane(breakpoint.getReplacementExecutionProcessor()); processorPane.add(replacementExecutionProcessorPane, new GridBagConstraints(1, y, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 5, 0, 0), 0, 0)); - executeRadioButton.addItemListener(new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - adjustStates(); - } - }); - replaceExecutionRadioButton.addItemListener(new ItemListener() { + executeTypeComboBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { adjustStates(); @@ -296,10 +278,9 @@ public class BreakpointEditor extends JPanel { processorPane.setVisible(isActive && breakpointTypeComboBox.getSelectedItem() == PROCESS); beforeExecutionCheckBox.setEnabled(isActive); beforeExecutionProcessorPane.setLocked(!isActive || !beforeExecutionCheckBox.isSelected()); - executeRadioButton.setEnabled(isActive); - replaceExecutionRadioButton.setEnabled(isActive); + executeTypeComboBox.setEnabled(isActive); // doNotExecuteRadioButton; - replacementExecutionProcessorPane.setLocked(!isActive || !replaceExecutionRadioButton.isSelected()); + replacementExecutionProcessorPane.setLocked(!isActive || executeTypeComboBox.getSelectedIndex() != 1); afterExecutionCheckBox.setEnabled(isActive); afterExecutionProcessorPane.setLocked(!isActive || !afterExecutionCheckBox.isSelected()); } @@ -345,7 +326,7 @@ public class BreakpointEditor extends JPanel { StatementProcessor afterExecutionProcessor = null; if(!isBreaking) { beforeExecutionProcessor = beforeExecutionCheckBox.isSelected()? beforeExecutionProcessorPane.getStatementProcessor(): null; - replacementExecutionProcessor = replaceExecutionRadioButton.isSelected()? replacementExecutionProcessorPane.getStatementProcessor(): null; + replacementExecutionProcessor = executeTypeComboBox.getSelectedIndex() == 1? replacementExecutionProcessorPane.getStatementProcessor(): null; afterExecutionProcessor = afterExecutionCheckBox.isSelected()? afterExecutionProcessorPane.getStatementProcessor(): null; } return new Breakpoint(id, hitCount, statementMatcher, isBreaking, beforeExecutionProcessor, replacementExecutionProcessor, afterExecutionProcessor); diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointHitEditor.java b/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointHitEditor.java index 1c8c934afd..fa9657e3a1 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointHitEditor.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/BreakpointHitEditor.java @@ -120,10 +120,18 @@ public class BreakpointHitEditor extends JPanel { executeTypeNoneRadioButton.setSelected(true); executionTypeGroup.add(executeTypeNoneRadioButton); executionTypePane.add(executeTypeNoneRadioButton); - JRadioButton executeTypeBreakRadioButton = new JRadioButton("Execute and break"); + final JRadioButton executeTypeBreakRadioButton = new JRadioButton("Execute and break"); executeTypeBreakRadioButton.setOpaque(false); executionTypeGroup.add(executeTypeBreakRadioButton); executionTypePane.add(executeTypeBreakRadioButton); + final JRadioButton executeTypeSkipRadioButton = new JRadioButton("Skip"); + executeTypeSkipRadioButton.setOpaque(false); + executionTypeGroup.add(executeTypeSkipRadioButton); + executionTypePane.add(executeTypeSkipRadioButton); + final JRadioButton executeTypeFailRadioButton = new JRadioButton("Throw exception"); + executeTypeFailRadioButton.setOpaque(false); + executionTypeGroup.add(executeTypeFailRadioButton); + executionTypePane.add(executeTypeFailRadioButton); breakpointHitExecutionPane.add(executionTypePane, new GridBagConstraints(0, y++, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.NONE, new Insets(5, 0, 0, 0), 0, 0)); JPanel buttonPane = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0)); buttonPane.setOpaque(false); @@ -134,7 +142,20 @@ public class BreakpointHitEditor extends JPanel { @Override public void actionPerformed(ActionEvent e) { if(breakpointHit.isBeforeExecution()) { - breakpointHit.setExecutionType(executeTypeNoneRadioButton.isSelected()? ExecutionType.RUN: ExecutionType.STEP_THROUGH, replaceStatementCheckBox.isSelected()? replacementSQLTextArea.getText(): null); + String replacementSQL = null; + ExecutionType executionType = ExecutionType.RUN; + if(executeTypeNoneRadioButton.isSelected()) { + executionType = ExecutionType.RUN; + replacementSQL = replaceStatementCheckBox.isSelected()? replacementSQLTextArea.getText(): null; + } else if(executeTypeBreakRadioButton.isSelected()) { + executionType = ExecutionType.STEP_THROUGH; + replacementSQL = replaceStatementCheckBox.isSelected()? replacementSQLTextArea.getText(): null; + } else if(executeTypeSkipRadioButton.isSelected()) { + executionType = ExecutionType.RUN_OVER; + } else if(executeTypeFailRadioButton.isSelected()) { + executionType = ExecutionType.FAIL; + } + breakpointHit.setExecutionType(executionType, replacementSQL); } else { breakpointHit.setExecutionType(ExecutionType.RUN, null); } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java b/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java index 51a4d85977..ae66afc113 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/Console.java @@ -66,13 +66,16 @@ import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JScrollPane; import javax.swing.JTabbedPane; +import javax.swing.JTextArea; import javax.swing.SwingUtilities; import javax.swing.UIManager; import org.jooq.debug.Debugger; import org.jooq.debug.DebuggerRegistry; import org.jooq.debug.LocalDebugger; +import org.jooq.debug.console.misc.JSedRegExBuilder; import org.jooq.debug.console.remote.ClientDebugger; /** @@ -115,6 +118,21 @@ public class Console extends JFrame { JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic('F'); + JMenuItem regExpEditorMenuItem = new JMenuItem("Reg. Exp. Editor"); + regExpEditorMenuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + JDialog dialog = new JDialog(Console.this, "Reg. Exp Editor"); + JPanel contentPane = new JPanel(new BorderLayout()); + contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + contentPane.add(new JSedRegExBuilder("/foo/bar/gi", "axFooax\nbxfoobx"), BorderLayout.CENTER); + dialog.add(contentPane, BorderLayout.CENTER); + dialog.setSize(600, 400); + dialog.setVisible(true); + dialog.setLocationRelativeTo(Console.this); + } + }); + fileMenu.add(regExpEditorMenuItem); JMenuItem exitMenuItem = new JMenuItem("Exit"); exitMenuItem.setMnemonic('x'); exitMenuItem.addActionListener(new ActionListener() { @@ -153,15 +171,17 @@ public class Console extends JFrame { final JDialog aboutDialog = new JDialog(Console.this, "About jOOQ Console", ModalityType.APPLICATION_MODAL); aboutDialog.setResizable(false); Container contentPane = aboutDialog.getContentPane(); - JPanel centerPane = new JPanel(new GridBagLayout()); - centerPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); - centerPane.add(new JLabel("jOOQ library: "), new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); - centerPane.add(new JLabel("Lukas Eder"), new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); - centerPane.add(new JLabel("jOOQ Console: "), new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); - centerPane.add(new JLabel("Christopher Deckers"), new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); - centerPane.add(new JLabel("License: "), new GridBagConstraints(0, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); - centerPane.add(new JLabel("Apache License, Version 2.0"), new GridBagConstraints(1, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); - centerPane.add(new JLabel("Web site: "), new GridBagConstraints(0, 3, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); + JTabbedPane tabbedPane = new JTabbedPane(); + JPanel aboutPane = new JPanel(new GridBagLayout()); + aboutPane.setOpaque(false); + aboutPane.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); + aboutPane.add(new JLabel("jOOQ library: "), new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + aboutPane.add(new JLabel("Lukas Eder"), new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + aboutPane.add(new JLabel("jOOQ Console: "), new GridBagConstraints(0, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); + aboutPane.add(new JLabel("Christopher Deckers"), new GridBagConstraints(1, 1, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); + aboutPane.add(new JLabel("License: "), new GridBagConstraints(0, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); + aboutPane.add(new JLabel("Apache License, Version 2.0"), new GridBagConstraints(1, 2, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); + aboutPane.add(new JLabel("Web site: "), new GridBagConstraints(0, 3, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); JLabel siteLabel = new JLabel("http://www.jooq.org"); siteLabel.setForeground(Color.BLUE); Map attributeMap = new HashMap(); @@ -179,8 +199,30 @@ public class Console extends JFrame { } } }); - centerPane.add(siteLabel, new GridBagConstraints(1, 3, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); - contentPane.add(centerPane, BorderLayout.CENTER); + aboutPane.add(siteLabel, new GridBagConstraints(1, 3, 1, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(5, 0, 0, 0), 0, 0)); + tabbedPane.addTab("About", aboutPane); + JPanel disclaimerPane = new JPanel(new BorderLayout()); + disclaimerPane.setBorder(BorderFactory.createEmptyBorder(2, 5, 5, 5)); + disclaimerPane.setOpaque(false); + JTextArea textArea = new JTextArea( + "This software is provided by the copyright holders and contributors \"as is\"" + + " and any express or implied warranties, including, but not limited to, the" + + " implied warranties of merchantability and fitness for a particular purpose" + + " are disclaimed. In no event shall the copyright owner or contributors be" + + " liable for any direct, indirect, incidental, special, exemplary, or" + + " consequential damages (including, but not limited to, procurement of" + + " substitute goods or services; loss of use, data, or profits; or business" + + " interruption) however caused and on any theory of liability, whether in" + + " contract, strict liability, or tort (including negligence or otherwise)" + + " arising in any way out of the use of this software, even if advised of the" + + " possibility of such damage." + ); + textArea.setLineWrap(true); + textArea.setWrapStyleWord(true); + textArea.setEditable(false); + disclaimerPane.add(new JScrollPane(textArea)); + tabbedPane.addTab("Disclaimer", disclaimerPane); + contentPane.add(tabbedPane, BorderLayout.CENTER); JPanel southPane = new JPanel(new FlowLayout(FlowLayout.RIGHT)); JButton okButton = new JButton("OK"); okButton.addActionListener(new ActionListener() { @@ -191,7 +233,7 @@ public class Console extends JFrame { }); southPane.add(okButton); contentPane.add(southPane, BorderLayout.SOUTH); - aboutDialog.pack(); + aboutDialog.setSize(500, 400); aboutDialog.setLocationRelativeTo(Console.this); aboutDialog.setVisible(true); } diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/DebuggerPane.java b/jOOQ-console/src/main/java/org/jooq/debug/console/DebuggerPane.java index 1c7a599014..19f85700de 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/DebuggerPane.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/DebuggerPane.java @@ -86,6 +86,7 @@ import org.jooq.debug.console.misc.CheckBoxNode; import org.jooq.debug.console.misc.CheckBoxNodeEditor; import org.jooq.debug.console.misc.CheckBoxNodeRenderer; import org.jooq.debug.console.misc.InvisibleSplitPane; +import org.jooq.debug.console.misc.TreeDataTip; /** * @author Christopher Deckers @@ -111,16 +112,18 @@ public class DebuggerPane extends JPanel { setOpaque(false); this.debugger = debugger; JPanel westPane = new JPanel(new BorderLayout()); + westPane.setBorder(BorderFactory.createTitledBorder("Breakpoints")); westPane.setOpaque(false); JPanel breakpointAddPane = new JPanel(new GridBagLayout()); breakpointAddPane.setOpaque(false); breakpointAddPane.setBorder(BorderFactory.createEmptyBorder(0, 0, 2, 0)); + breakpointAddPane.add(new JLabel("Name "), new GridBagConstraints(0, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); final JTextField addBreakpointTextField = new JTextField(7); - breakpointAddPane.add(addBreakpointTextField, new GridBagConstraints(0, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0)); + breakpointAddPane.add(addBreakpointTextField, new GridBagConstraints(1, 0, 1, 1, 1, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 0), 0, 0)); final JButton addBreakpointButton = new JButton("Add"); addBreakpointButton.setOpaque(false); addBreakpointButton.setEnabled(false); - breakpointAddPane.add(addBreakpointButton, new GridBagConstraints(1, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 0), 0, 0)); + breakpointAddPane.add(addBreakpointButton, new GridBagConstraints(2, 0, 1, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 2, 0, 0), 0, 0)); addBreakpointTextField.getDocument().addDocumentListener(new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { @@ -205,6 +208,7 @@ public class DebuggerPane extends JPanel { breakpointTree.setCellEditor(new CheckBoxNodeEditor(breakpointTree)); breakpointTree.setCellRenderer(new CheckBoxNodeRenderer(breakpointTree)); breakpointTree.setEditable(true); + TreeDataTip.activate(breakpointTree); JScrollPane breakpointTreeScrollPane = new JScrollPane(breakpointTree); breakpointTreeScrollPane.setPreferredSize(new Dimension(200, 200)); westPane.add(breakpointTreeScrollPane, BorderLayout.CENTER); @@ -509,9 +513,19 @@ public class DebuggerPane extends JPanel { DefaultMutableTreeNode node = (DefaultMutableTreeNode)paths[0].getLastPathComponent(); if(node instanceof CheckBoxNode) { Object o = node.getUserObject(); - eastPane.add(new BreakpointEditor(this, (Breakpoint)o)); + JPanel contentPane = new JPanel(new BorderLayout()); + contentPane.setBorder(BorderFactory.createTitledBorder("Breakpoint configuration")); + contentPane.setOpaque(false); + BreakpointEditor c = new BreakpointEditor(this, (Breakpoint)o); + contentPane.add(c); + eastPane.add(contentPane); } else if(node instanceof BreakpointHitNode) { - eastPane.add(new BreakpointHitEditor(debugger, this, (BreakpointHitNode)node)); + JPanel contentPane = new JPanel(new BorderLayout()); + contentPane.setBorder(BorderFactory.createTitledBorder("Breakpoint hit details")); + contentPane.setOpaque(false); + BreakpointHitEditor c = new BreakpointHitEditor(debugger, this, (BreakpointHitNode)node); + contentPane.add(c); + eastPane.add(contentPane); } } eastPane.revalidate(); diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/misc/TreeDataTip.java b/jOOQ-console/src/main/java/org/jooq/debug/console/misc/TreeDataTip.java new file mode 100644 index 0000000000..f4a3577ccd --- /dev/null +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/misc/TreeDataTip.java @@ -0,0 +1,206 @@ +/** + * Copyright (c) 2009-2012, Lukas Eder, lukas.eder@gmail.com + * Christopher Deckers, chrriis@gmail.com + * All rights reserved. + * + * This software is licensed to you under the Apache License, Version 2.0 + * (the "License"); You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * . Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * . Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * . Neither the name "jOOQ" nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +package org.jooq.debug.console.misc; + +import java.awt.AWTEvent; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.GraphicsConfiguration; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Window; +import java.awt.event.MouseEvent; + +import javax.swing.CellRendererPane; +import javax.swing.JComponent; +import javax.swing.JToolTip; +import javax.swing.JTree; +import javax.swing.Popup; +import javax.swing.PopupFactory; +import javax.swing.SwingUtilities; +import javax.swing.event.MouseInputAdapter; +import javax.swing.event.MouseInputListener; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; + +public class TreeDataTip { + + private TreeDataTip() {} + + public static void activate(final JTree tree) { + MouseInputListener dataTipListener = new MouseInputAdapter() { + @Override + public void mouseEntered(MouseEvent e) { + showTip(e.getPoint()); + } + @Override + public void mouseMoved(MouseEvent e) { + showTip(e.getPoint()); + } + @Override + public void mouseExited(MouseEvent e) { + hideTip(); + } + @Override + public void mousePressed(MouseEvent e) { + hideTip(); + } + private Popup popup; + private int currentRow = -1; + private void hideTip() { + if(popup != null) { + popup.hide(); + popup = null; + } + } + private void showTip(Point mousePosition) { + int row = tree.getRowForLocation(mousePosition.x, mousePosition.y); + if(row != currentRow) { + hideTip(); + currentRow = row; + if(row >= 0) { + TreePath treePath = tree.getPathForRow(row); + TreeCellRenderer renderer = tree.getCellRenderer(); + boolean isSelected = false;//breakpointTree.isPathSelected(treePath); + boolean isExpanded = tree.isExpanded(treePath); + boolean hasFocus = false;//tree.hasFocus() && rowIndex == tree.getLeadSelectionRow(); + Object item = treePath.getLastPathComponent(); + boolean isLeaf = tree.getModel().isLeaf(item); + final JComponent rendererComponent = (JComponent)renderer.getTreeCellRendererComponent(tree, item, isSelected, isExpanded, isLeaf, row, hasFocus); + rendererComponent.setFont(tree.getFont()); + Rectangle visRect = tree.getVisibleRect(); + Rectangle cellBounds = tree.getPathBounds(treePath); + Rectangle visibleCellRectangle = cellBounds.intersection(visRect); + if (!visibleCellRectangle.contains(mousePosition)) { + return; + } + Dimension rendCompDim = rendererComponent.getMinimumSize(); + Rectangle rendCompBounds = new Rectangle(cellBounds.getLocation(), rendCompDim); + visRect.x -= 1000; + visRect.width += 1000; + visRect.y -= 1000; + visRect.height += 2000; + // We do not care about vertical visibility. + rendCompBounds.height = 1; + if(cellBounds.contains(rendCompBounds) && visRect.contains(rendCompBounds)) { + return; + } + Dimension preferredSize = rendererComponent.getPreferredSize(); + Point tipPosition = cellBounds.getLocation(); + int width = Math.max(cellBounds.width, preferredSize.width); + int height = cellBounds.height;//Math.max(cellBounds.height, preferredSize.height); // We don't care about vertical visibility + Dimension tipDimension = new Dimension(width, height); + final Color backgroundColor = tree.getBackground(); + class DataTipComponent extends JToolTip { + private CellRendererPane rendererPane = new CellRendererPane(); + private boolean isHeavyWeight; + public DataTipComponent() { + add(rendererPane); + setFocusable(false); + setBorder(null); + enableEvents(AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | AWTEvent.MOUSE_WHEEL_EVENT_MASK); + } + @Override + public void updateUI() { + } + @Override + public boolean contains(int x, int y) { + return isHeavyWeight; + } + @Override + public void paintComponent(Graphics g) { + // Leave the component's opacity settings as is, just paint the background myself. + // This seems to be the only viable solution: DefaultTableCellRenderer overrides isOpaque() and returns + // true only if renderer color does not equal parent color. The problem is that rendererPane.paintComponent() + // re-parents the renderer. + g.setColor(backgroundColor); + int width = getWidth(); + int height = getHeight(); + g.fillRect(0, 0, width, height); + g.setColor(Color.black); + g.drawRect(0, 0, width - 1, height - 1); + g.setClip(1, 1, width - 2, height - 2); + int row = currentRow; + TreePath treePath = tree.getPathForRow(row); + boolean isSelected = false;//breakpointTree.isPathSelected(treePath); + boolean isExpanded = tree.isExpanded(treePath); + boolean hasFocus = false;//tree.hasFocus() && rowIndex == tree.getLeadSelectionRow(); + Object item = treePath.getLastPathComponent(); + boolean isLeaf = tree.getModel().isLeaf(item); + TreeCellRenderer renderer = tree.getCellRenderer(); + JComponent rendererComponent = (JComponent)renderer.getTreeCellRendererComponent(tree, item, isSelected, isExpanded, isLeaf, row, hasFocus); + rendererComponent.setFont(tree.getFont()); + rendererPane.paintComponent(g, rendererComponent, this, 1, 1, width - 1, height - 1); + } + public void setHeavyWeight(boolean isHeavyWeight) { + this.isHeavyWeight = isHeavyWeight; + } + } + DataTipComponent dataTipComponent = new DataTipComponent(); + Dimension tipDimensionClipped = new Dimension(tipDimension.width, tipDimension.height); + Window windowAncestor = SwingUtilities.getWindowAncestor(tree); + GraphicsConfiguration gc = windowAncestor.getGraphicsConfiguration(); + Rectangle screenBounds = gc.getBounds(); + // Chrriis: we add the border around, not in + Point tipScreenPosition = new Point(tipPosition.x, tipPosition.y); + SwingUtilities.convertPointToScreen(tipScreenPosition, tree); + Point tipPositionClipped = new Point(); + tipPositionClipped.x = Math.max(tipScreenPosition.x - 1, screenBounds.x); + tipPositionClipped.y = Math.max(tipScreenPosition.y - 1, screenBounds.y); + // Chrriis: we add the border around, not in + tipDimensionClipped.width = Math.min(screenBounds.x + screenBounds.width - tipPositionClipped.x, tipDimensionClipped.width + 2); + tipDimensionClipped.height = Math.min(screenBounds.y + screenBounds.height - tipPositionClipped.y, tipDimensionClipped.height + 2); + SwingUtilities.convertPointFromScreen(tipPositionClipped, tree); + dataTipComponent.setPreferredSize(tipDimensionClipped); + SwingUtilities.convertPointToScreen(tipPosition, tree); + PopupFactory popupFactory = PopupFactory.getSharedInstance(); + popup = popupFactory.getPopup(tree, dataTipComponent, tipPosition.x - 1, tipPosition.y - 1); + popup.show(); + Window componentWindow = SwingUtilities.windowForComponent(tree); + Window tipWindow = SwingUtilities.windowForComponent(dataTipComponent); + boolean isHeavyWeight = tipWindow != null && tipWindow != componentWindow; + dataTipComponent.setHeavyWeight(isHeavyWeight); + } + } + } + }; + tree.addMouseListener(dataTipListener); + tree.addMouseMotionListener(dataTipListener); + } + +} diff --git a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientStatementExecutionResultSetResult.java b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientStatementExecutionResultSetResult.java index be122a8aad..c0b83d2e6d 100644 --- a/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientStatementExecutionResultSetResult.java +++ b/jOOQ-console/src/main/java/org/jooq/debug/console/remote/ClientStatementExecutionResultSetResult.java @@ -59,7 +59,21 @@ public class ClientStatementExecutionResultSetResult implements StatementExecuti columnNames = rsResult.getColumnNames(); typeInfos = rsResult.getTypeInfos(); columnClasses = rsResult.getColumnClasses(); - rowData = rsResult.getRowData(); + Object[][] rowData_ = rsResult.getRowData(); + rowData = new Object[rowData_.length][]; + for(int i=0; i