/* * Copyright 2007 - 2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sf.jailer.ui.databrowser; import java.awt.BasicStroke; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.BufferedReader; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.sql.Blob; import java.sql.Clob; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.SQLXML; import java.sql.Types; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.concurrent.PriorityBlockingQueue; import javax.swing.DefaultComboBoxModel; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.RowSorter; import javax.swing.SortOrder; import javax.swing.SwingUtilities; import javax.swing.event.PopupMenuEvent; import javax.swing.event.PopupMenuListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.DefaultTreeModel; import net.sf.jailer.ExecutionContext; import net.sf.jailer.configuration.DBMS; import net.sf.jailer.database.InlineViewStyle; import net.sf.jailer.database.Session; import net.sf.jailer.database.Session.AbstractResultSetReader; import net.sf.jailer.database.SqlException; import net.sf.jailer.datamodel.Association; import net.sf.jailer.datamodel.Cardinality; import net.sf.jailer.datamodel.Column; import net.sf.jailer.datamodel.DataModel; import net.sf.jailer.datamodel.PrimaryKey; import net.sf.jailer.datamodel.RestrictionDefinition; import net.sf.jailer.datamodel.RowIdSupport; import net.sf.jailer.datamodel.Table; import net.sf.jailer.extractionmodel.ExtractionModel; import net.sf.jailer.modelbuilder.JDBCMetaDataBasedModelElementFinder; import net.sf.jailer.subsetting.ScriptFormat; import net.sf.jailer.ui.ConditionEditor; import net.sf.jailer.ui.DataModelManager; import net.sf.jailer.ui.DbConnectionDialog; import net.sf.jailer.ui.ExtractionModelFrame; import net.sf.jailer.ui.QueryBuilderDialog; import net.sf.jailer.ui.QueryBuilderDialog.Relationship; import net.sf.jailer.ui.UIUtil; import net.sf.jailer.ui.databrowser.Desktop.RowBrowser; import net.sf.jailer.ui.scrollmenu.JScrollC2Menu; import net.sf.jailer.ui.scrollmenu.JScrollPopupMenu; import net.sf.jailer.util.CancellationException; import net.sf.jailer.util.CancellationHandler; import net.sf.jailer.util.CellContentConverter; import net.sf.jailer.util.Pair; import net.sf.jailer.util.Quoting; import net.sf.jailer.util.SqlUtil; /** * Content UI of a row browser frame (as {@link JInternalFrame}s). Contains a * table for rendering rows. * * @author Ralf Wisser */ @SuppressWarnings("serial") public abstract class BrowserContentPane extends javax.swing.JPanel { /** * Allowed row limits. */ private static final Integer[] ROW_LIMITS = new Integer[] { 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 70000, 100000 }; /** * Concurrently loads rows. */ final class LoadJob implements RunnableWithPriority { private List<Row> rows = Collections.synchronizedList(new ArrayList<Row>()); private Throwable exception; private boolean isCanceled; private final int limit; private final String andCond; private final boolean selectDistinct; private boolean finished; public LoadJob(int limit, String andCond, boolean selectDistinct) { this.andCond = andCond; this.selectDistinct = selectDistinct; synchronized (this) { this.limit = limit; finished = false; isCanceled = false; } } public void run() { int l; synchronized (this) { l = limit; if (isCanceled) { CancellationHandler.reset(this); return; } } rowCountCache.clear(); try { reloadRows(andCond, rows, this, l + 1, selectDistinct); CancellationHandler.checkForCancellation(this); synchronized (this) { finished = true; } } catch (SQLException e) { synchronized (rows) { exception = e; } } catch (CancellationException e) { Session._log.info("cancelled"); CancellationHandler.reset(this); return; } catch (Throwable e) { synchronized (rows) { exception = e; } } CancellationHandler.reset(this); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { Throwable e; int l; boolean limitExceeded = false; synchronized (rows) { e = exception; l = limit; while (rows.size() > limit) { limitExceeded = true; rows.remove(rows.size() - 1); } isCanceled = true; // done } if (e != null) { updateMode("error"); unhide(); UIUtil.showException(BrowserContentPane.this, "Error", e); } else { Set<String> prevIDs = new TreeSet<String>(); long prevHash = 0; if (BrowserContentPane.this.rows != null) { for (Row r: BrowserContentPane.this.rows) { prevIDs.add(r.rowId); try { for (Object v: r.values) { if (v != null) { prevHash = 2 * prevHash + v.hashCode(); } } } catch (RuntimeException e1) { // ignore } } } onContentChange(new ArrayList<Row>(), false); BrowserContentPane.this.rows.clear(); BrowserContentPane.this.rows.addAll(rows); updateTableModel(l, limitExceeded); Set<String> currentIDs = new TreeSet<String>(); long currentHash = 0; if (rows != null) { for (Row r: rows) { currentIDs.add(r.rowId); try { for (Object v: r.values) { if (v != null) { currentHash = 2 * currentHash + v.hashCode(); } } } catch (RuntimeException e1) { // ignore } } } setPendingState(false, true); onContentChange(rows, true); // rows.isEmpty() || currentHash != prevHash || rows.size() != prevSize || !prevIDs.equals(currentIDs) || rows.size() != currentIDs.size()); updateMode("table"); updateWhereField(); if (reloadAction != null) { reloadAction.run(); } } } }); } public void cancel() { synchronized (this) { if (isCanceled) { return; } isCanceled = true; if (finished) { return; } } CancellationHandler.cancel(this); } @Override public int getPriority() { return 100; } } private Runnable reloadAction; public void setOnReloadAction(Runnable runnable) { reloadAction = runnable; } /** * Current LoadJob. */ private LoadJob currentLoadJob; /** * Table to read rows from. */ Table table; /** * Parent row, or <code>null</code>. */ Row parentRow; /** * The data model. */ private final DataModel dataModel; /** * The execution context. */ protected final ExecutionContext executionContext; /** * {@link RowIdSupport} for data model. */ private final RowIdSupport rowIdSupport; /** * {@link Association} with parent row, or <code>null</code>. */ Association association; /** * Rows to render. */ List<Row> rows = new ArrayList<Row>(); /** * Cache for association row count. */ private final Map<Pair<String, Association>, Pair<Long, Long>> rowCountCache = Collections.synchronizedMap(new HashMap<Pair<String,Association>, Pair<Long,Long>>()); private final long MAX_ROWCOUNTCACHE_RETENTION_TIME = 5 * 60 * 1000L; /** * Number of non-distinct rows; */ private int noNonDistinctRows; /** * Number of distinct rows; */ private int noDistinctRows; /** * Indexes of highlighted rows. */ Set<Integer> highlightedRows = new HashSet<Integer>(); /** * Indexes of primary key columns. */ private Set<Integer> pkColumns = new HashSet<Integer>(); /** * Indexes of foreign key columns. */ private Set<Integer> fkColumns = new HashSet<Integer>(); /** * DB session. */ Session session; private int currentRowSelection = -1; private List<Row> parentRows; protected final Set<Pair<BrowserContentPane, Row>> currentClosure; protected Set<Pair<BrowserContentPane, String>> currentClosureRowIDs; private DetailsView singleRowDetailsView; private int initialRowHeight; public SQLBrowserContentPane sqlBrowserContentPane; private boolean suppressReload; /** * Alias for row number column. */ private static final String ROWNUMBERALIAS = "RN"; protected static final String NULL = "null"; protected static final String UNKNOWN = "- unknown column -"; protected static final int MAXLOBLENGTH = 2000; /** * And-condition-combobox model. */ private final DefaultComboBoxModel andCondModel = new DefaultComboBoxModel(); static class SqlStatementTable extends Table { public SqlStatementTable(String name, PrimaryKey primaryKey, boolean defaultUpsert) { super(name, primaryKey, defaultUpsert, false); } } /** * Constructor. * * @param dataModel * the data model * @param table * to read rows from. Opens SQL browser if table is <code>null</code>. * @param condition * initial condition * @param session * DB session * @param parentRow * parent row * @param parentRows * all parent rows, if there are more than 1 * @param association * {@link Association} with parent row * @param selectDistinct * @param limit * @param selectDistinct * @param limit * @param reload */ public BrowserContentPane(final DataModel dataModel, final Table table, String condition, Session session, Row parentRow, List<Row> parentRows, final Association association, Frame parentFrame, Set<Pair<BrowserContentPane, Row>> currentClosure, Set<Pair<BrowserContentPane, String>> currentClosureRowIDs, Integer limit, Boolean selectDistinct, boolean reload, ExecutionContext executionContext) { this.table = table; this.session = session; this.dataModel = dataModel; this.rowIdSupport = new RowIdSupport(dataModel, session.dbms, executionContext); this.parentRow = parentRow; this.parentRows = parentRows; this.association = association; this.currentClosure = currentClosure; this.currentClosureRowIDs = currentClosureRowIDs; this.executionContext = executionContext; rowIdSupport.setUseRowIdsOnlyForTablesWithoutPK(true); suppressReload = true; if (table == null) { this.table = new SqlStatementTable(null, null, false); } initComponents(); setPendingState(false, false); dropA.setText(null); dropA.setIcon(dropDownIcon); sqlLabel1.setIcon(dropDownIcon); dropA.addMouseListener(new java.awt.event.MouseAdapter() { public void mousePressed(java.awt.event.MouseEvent evt) { openColumnDropDownBox(dropA, "A", table); } public void mouseEntered(java.awt.event.MouseEvent evt) { dropA.setEnabled(false); } public void mouseExited(java.awt.event.MouseEvent evt) { dropA.setEnabled(true); } }); if (association != null) { dropB.setText(null); dropB.setIcon(dropDownIcon); dropB.addMouseListener(new java.awt.event.MouseAdapter() { public void mousePressed(java.awt.event.MouseEvent evt) { openColumnDropDownBox(dropB, "B", association.source); } public void mouseEntered(java.awt.event.MouseEvent evt) { dropB.setEnabled(false); } public void mouseExited(java.awt.event.MouseEvent evt) { dropB.setEnabled(true); } }); } andCondModel.addElement(""); andCondition.setModel(andCondModel); andCondition.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (!suppessReloadOnAndConditionAction) { if (e.getStateChange() == ItemEvent.SELECTED) { historizeAndCondition(e.getItem()); reloadRows(); } } } }); initialRowHeight = rowsTable.getRowHeight(); rowsTable = new JTable() { private int x[] = new int[2]; private int y[] = new int[2]; private Color color = new Color(0, 0, 200); @Override public void paint(Graphics graphics) { super.paint(graphics); if (!(graphics instanceof Graphics2D)) return; // RowSorter<? extends TableModel> rowSorter = rowsTable.getRowSorter(); // for (int i = 0; i < rowsTable.getRowCount(); ++i) { // if (rowSorter.convertRowIndexToView(i) != i) { // return; // } // } int maxI = Math.min(rowsTable.getRowCount(), rows.size()); for (int i = 0; i < maxI; ++i) { if (rows.get(i).isBlockEnd()) { Graphics2D g2d = (Graphics2D) graphics; g2d.setColor(color); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setStroke(new BasicStroke(1)); Rectangle r = rowsTable.getCellRect(i, 0, false); x[0] = (int) r.getMinX(); y[0] = (int) r.getMaxY(); r = rowsTable.getCellRect(i, rowsTable.getColumnCount() - 1, false); x[1] = (int) r.getMaxX(); y[1] = (int) r.getMaxY(); g2d.drawPolyline(x, y, 2); } } } }; rowsTable.setAutoCreateRowSorter(true); rowsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF); rowsTableScrollPane.setViewportView(rowsTable); setAndCondition(condition, true); from.setText(table == null? "" : this.dataModel.getDisplayName(table)); adjustGui(); rowsTable.setShowGrid(false); final TableCellRenderer defaultTableCellRenderer = rowsTable.getDefaultRenderer(String.class); rowsTable.setDefaultRenderer(Object.class, new TableCellRenderer() { final Color BG1 = new Color(255, 255, 255); final Color BG2 = new Color(230, 255, 255); final Color BG3 = new Color(190, 195, 255); final Color FG1 = new Color(155, 0, 0); final Color FG2 = new Color(0, 0, 255); final Font font = new JLabel().getFont(); final Font nonbold = new Font(font.getName(), font.getStyle() & ~Font.BOLD, font.getSize()); final Font bold = new Font(nonbold.getName(), nonbold.getStyle() | Font.BOLD, nonbold.getSize()); final Font italic = new Font(nonbold.getName(), nonbold.getStyle() | Font.ITALIC, nonbold.getSize()); final Font italicBold = new Font(nonbold.getName(), nonbold.getStyle() | Font.ITALIC | Font.BOLD, nonbold.getSize()); public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { isSelected = currentRowSelection == row || currentRowSelection == -2; Component render = defaultTableCellRenderer.getTableCellRendererComponent(table, value, isSelected, false, row, column); if (value instanceof Row) { Row theRow = (Row) value; Pair<BrowserContentPane, String> pair = new Pair<BrowserContentPane, String>(BrowserContentPane.this, theRow.rowId); singleRowDetailsView.setBorderColor(isSelected? render.getBackground() : BrowserContentPane.this.currentClosureRowIDs.contains(pair)? BG3 : Color.WHITE); return singleRowDetailsView; } final RowSorter<?> rowSorter = rowsTable.getRowSorter(); if (render instanceof JLabel) { if (!isSelected) { if (row < rows.size() && BrowserContentPane.this.currentClosureRowIDs.contains(new Pair<BrowserContentPane, String>(BrowserContentPane.this, rows.get(rowSorter.convertRowIndexToModel(row)).rowId))) { ((JLabel) render).setBackground(BG3); } else { ((JLabel) render).setBackground((row % 2 == 0) ? BG1 : BG2); } } ((JLabel) render).setForeground( pkColumns.contains(rowsTable.convertColumnIndexToModel(column)) ? FG1 : fkColumns.contains(rowsTable.convertColumnIndexToModel(column)) ? FG2 : Color.BLACK); boolean isNull = false; if (((JLabel) render).getText() == NULL || ((JLabel) render).getText() == UNKNOWN) { ((JLabel) render).setForeground(Color.gray); ((JLabel) render).setFont(italic); isNull = true; } try { if (isNull) { ((JLabel) render).setFont(highlightedRows.contains(rowSorter.convertRowIndexToModel(row)) ? italicBold : italic); } else { ((JLabel) render).setFont(highlightedRows.contains(rowSorter.convertRowIndexToModel(row)) ? bold : nonbold); } } catch (Exception e) { // ignore } } return render; } }); rowsTable.setRowSelectionAllowed(false); rowsTable.setColumnSelectionAllowed(false); rowsTable.setCellSelectionEnabled(false); rowsTable.setEnabled(false); rowsTableScrollPane.getVerticalScrollBar().setUnitIncrement(32); rowsTable.addMouseListener(new MouseListener() { private JPopupMenu lastMenu; @Override public void mouseReleased(MouseEvent e) { } @Override public void mousePressed(MouseEvent e) { int ri = rowsTable.rowAtPoint(e.getPoint()); if (ri >= 0) { int i = rowsTable.getRowSorter().convertRowIndexToModel(ri); Row row = rows.get(i); if (lastMenu == null || !lastMenu.isVisible()) { if (e.getButton() != MouseEvent.BUTTON1 || e.getClickCount() > 1) { currentRowSelection = ri; onRedraw(); // setCurrentRowSelection(ri); Rectangle r = rowsTable.getCellRect(ri, 0, false); int x = Math.max((int) e.getPoint().x, (int) r.getMinX()); int y = (int) r.getMaxY() - 2; if (singleRowDetailsView != null) { y = e.getY(); } Point p = SwingUtilities.convertPoint(rowsTable, x, y, null); JPopupMenu popup; popup = createPopupMenu(row, i, p.x + getOwner().getX(), p.y + getOwner().getY(), rows.size() == 1); popup.show(rowsTable, x, y); popup.addPropertyChangeListener("visible", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (Boolean.FALSE.equals(evt.getNewValue())) { currentRowSelection = -1; onRedraw(); // setCurrentRowSelection(-1); } } }); lastMenu = popup; } else { setCurrentRowSelection(ri); setCurrentRowSelection(-1); } } } } @Override public void mouseExited(MouseEvent e) { } @Override public void mouseEntered(MouseEvent e) { } @Override public void mouseClicked(MouseEvent e) { } }); andConditionEditor = new ConditionEditor(parentFrame, null); openEditorLabel.setIcon(conditionEditorIcon); openEditorLabel.setText(null); openEditorLabel.addMouseListener(new java.awt.event.MouseAdapter() { @Override public void mouseReleased(MouseEvent e) { mouseClicked(e); } @Override public void mouseClicked(MouseEvent e) { loadButton.grabFocus(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { String cond = andConditionEditor.edit(getAndConditionText(), "Table", "A", table, null, null, null, false); if (cond != null) { if (!getAndConditionText().equals(ConditionEditor.toSingleLine(cond))) { setAndCondition(cond, true); loadButton.grabFocus(); reloadRows(); } } openEditorLabel.setIcon(conditionEditorSelectedIcon); } }); } public void mouseEntered(java.awt.event.MouseEvent evt) { openEditorLabel.setIcon(conditionEditorSelectedIcon); } public void mouseExited(java.awt.event.MouseEvent evt) { openEditorLabel.setIcon(conditionEditorIcon); } }); relatedRowsLabel.setIcon(dropDownIcon); relatedRowsPanel.addMouseListener(new java.awt.event.MouseAdapter() { private JPopupMenu popup; private boolean in = false; @Override public void mousePressed(MouseEvent e) { loadButton.grabFocus(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // if (rows.size() == 1) { // popup = createPopupMenu(rows.get(0), 0, 0, 0); // } else { popup = createPopupMenu(null, -1, 0, 0, false); // } setCurrentRowSelection(-2); popup.addPropertyChangeListener("visible", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (Boolean.FALSE.equals(evt.getNewValue())) { popup = null; updateBorder(); setCurrentRowSelection(-1); } } }); popup.show(relatedRowsPanel, 0, relatedRowsPanel.getHeight()); } }); } public void mouseEntered(java.awt.event.MouseEvent evt) { in = true; updateBorder(); } public void mouseExited(java.awt.event.MouseEvent evt) { in = false; updateBorder(); } private void updateBorder() { relatedRowsPanel.setBorder(new javax.swing.border.SoftBevelBorder((in || popup != null) ? javax.swing.border.BevelBorder.LOWERED : javax.swing.border.BevelBorder.RAISED)); } }); sqlPanel.addMouseListener(new java.awt.event.MouseAdapter() { private JPopupMenu popup; private boolean in = false; @Override public void mousePressed(MouseEvent e) { loadButton.grabFocus(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { popup = createSqlPopupMenu(BrowserContentPane.this.parentRow, 0, 300, 300, false); setCurrentRowSelection(-2); popup.addPropertyChangeListener("visible", new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if (Boolean.FALSE.equals(evt.getNewValue())) { popup = null; updateBorder(); setCurrentRowSelection(-1); } } }); popup.show(sqlPanel, 0, sqlPanel.getHeight()); } }); } public void mouseEntered(java.awt.event.MouseEvent evt) { in = true; updateBorder(); } public void mouseExited(java.awt.event.MouseEvent evt) { in = false; updateBorder(); } private void updateBorder() { sqlPanel.setBorder(new javax.swing.border.SoftBevelBorder((in || popup != null) ? javax.swing.border.BevelBorder.LOWERED : javax.swing.border.BevelBorder.RAISED)); } }); limitBox.setModel(new DefaultComboBoxModel(ROW_LIMITS)); limitBox.setSelectedItem(association == null? 200 : 500); if (limit != null) { limitBox.setSelectedItem(limit); } if (selectDistinct != null) { selectDistinctCheckBox.setSelected(selectDistinct); } updateTableModel(0, false); if (this.table instanceof SqlStatementTable) { sqlBrowserContentPane = new SQLBrowserContentPane(); removeAll(); GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; add(sqlBrowserContentPane, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 5; gridBagConstraints.gridwidth = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; gridBagConstraints.weightx = 0; gridBagConstraints.weighty = 0; sqlBrowserContentPane.editorPanel.add(limitBox, gridBagConstraints); sqlBrowserContentPane.rowListPanel.add(cardPanel, java.awt.BorderLayout.CENTER); cardPanel.setVisible(true); sqlBrowserContentPane.sqlEditorPane.setContentType("text/sql"); sqlBrowserContentPane.sqlEditorPane.setText(condition); sqlBrowserContentPane.sqlEditorPane.setCaretPosition(0); sqlBrowserContentPane.reloadButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { reloadRows(); } }); sqlBrowserContentPane.detailsButton.setEnabled(false); sqlBrowserContentPane.detailsButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openDetails(300, 300); } }); } suppressReload = false; if (reload) { reloadRows(); } } boolean isPending = false; void setPendingState(boolean pending, boolean propagate) { isPending = pending; ((CardLayout) pendingNonpendingPanel.getLayout()).show(pendingNonpendingPanel, pending? "pending" : "nonpending"); if (propagate) { for (RowBrowser child: getChildBrowsers()) { child.browserContentPane.setPendingState(pending, propagate); } } } protected void historizeAndCondition(Object item) { if (item == null) { return; } for (int i = 0; i < andCondModel.getSize(); ++i) { if (item.equals(andCondModel.getElementAt(i))) { return; } } andCondModel.insertElementAt(item, 1); } private boolean suppessReloadOnAndConditionAction = false; void setAndCondition(String cond, boolean historize) { try { suppessReloadOnAndConditionAction = true; andCondition.setSelectedItem(ConditionEditor.toSingleLine(cond)); if (historize) { historizeAndCondition(ConditionEditor.toSingleLine(cond)); } } finally { suppessReloadOnAndConditionAction = false; } } String getAndConditionText() { Object sel = andCondition.getSelectedItem(); if (sel == null) { return ""; } return sel.toString().trim(); } private void adjustGui() { if (this.association == null) { joinPanel.setVisible(false); onPanel.setVisible(false); wherePanel.setVisible(false); jLabel1.setText(" "); jLabel4.setText(" "); jLabel9.setText(" "); jLabel6.setVisible(false); dropB.setVisible(false); andLabel.setText(" Where "); java.awt.GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 8; gridBagConstraints.gridy = 4; gridBagConstraints.gridheight = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; } else { join.setText(this.dataModel.getDisplayName(this.association.source)); on.setText(!this.association.reversed ? SqlUtil.reversRestrictionCondition(this.association.getUnrestrictedJoinCondition()) : this.association .getUnrestrictedJoinCondition()); updateWhereField(); join.setToolTipText(join.getText()); on.setToolTipText(on.getText()); where.setToolTipText(where.getText()); } } private class AllNonEmptyItem extends JMenuItem { int todo; int done; private List<ActionListener> todoList = new ArrayList<ActionListener>(); private final String initText = "Counting rows... "; AllNonEmptyItem() { setEnabled(false); addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Component parent = SwingUtilities.getWindowAncestor(BrowserContentPane.this); if (parent == null) { parent = BrowserContentPane.this; } parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { for (ActionListener al: todoList) { al.actionPerformed(e); } } finally { parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } }); } public void rowsCounted(long count, ActionListener itemAction) { ++done; if (count != 0) { todoList.add(itemAction); } int p = todo > 0? (100 * done) / todo : 0; if (done < todo) { setText(initText + p + "%"); } else { setText("All non-empty (" + todoList.size() + ")"); setEnabled(true); } } public void setInitialText() { if (todoList.isEmpty()) { setText("All non-empty (0)"); } else { setText(initText + "100%"); } } }; private String currentSelectedRowCondition = ""; /** * Creates popup menu for navigation. * @param navigateFromAllRows */ public JPopupMenu createPopupMenu(final Row row, final int rowIndex, final int x, final int y, boolean navigateFromAllRows) { List<String> assList = new ArrayList<String>(); Map<String, Association> assMap = new HashMap<String, Association>(); for (Association a : table.associations) { int n = 0; for (Association a2 : table.associations) { if (a.destination == a2.destination) { ++n; } } char c = ' '; if (a.isIgnored()) { c = '4'; } else if (a.isInsertDestinationBeforeSource()) { c = '1'; } else if (a.isInsertSourceBeforeDestination()) { c = '2'; } else { c = '3'; } String name = c + a.getDataModel().getDisplayName(a.destination) + (n > 1 ? " on " + a.getName() : ""); assList.add(name); assMap.put(name, a); } Collections.sort(assList); final Object context = new Object(); AllNonEmptyItem allNonEmpty = new AllNonEmptyItem(); JPopupMenu popup = new JPopupMenu(); popup = createNavigationMenu(popup, row, rowIndex, assList, assMap, "Parents", "1", navigateFromAllRows, 80, allNonEmpty, context); popup = createNavigationMenu(popup, row, rowIndex, assList, assMap, "Children", "2", navigateFromAllRows, 60, allNonEmpty, context); popup = createNavigationMenu(popup, row, rowIndex, assList, assMap, "Associated Rows", "3", navigateFromAllRows, 40, allNonEmpty, context); popup = createNavigationMenu(popup, row, rowIndex, assList, assMap, "Detached Rows", "4", navigateFromAllRows, 40, allNonEmpty, context); popup.addPopupMenuListener(new PopupMenuListener() { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { } @Override public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { cancel(); } @Override public void popupMenuCanceled(PopupMenuEvent e) { cancel(); } private void cancel() { CancellationHandler.cancelSilently(context); getRunnableQueue().add(new RunnableWithPriority() { @Override public void run() { CancellationHandler.reset(context); } @Override public int getPriority() { return 0; } }); } }); if (!isPending && !rows.isEmpty()) { popup.add(allNonEmpty); allNonEmpty.setInitialText(); } if (row != null) { if (!(table instanceof SqlStatementTable)) { popup.add(new JSeparator()); } JMenuItem det = new JMenuItem("Details"); popup.add(det); det.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JDialog d = new JDialog(getOwner(), (table instanceof SqlStatementTable)? "" : dataModel.getDisplayName(table), true); d.getContentPane().add(new DetailsView(rows, rowsTable.getRowCount(), dataModel, table, rowIndex, rowsTable.getRowSorter(), true, rowIdSupport) { @Override protected void onRowChanged(int row) { setCurrentRowSelection(row); } }); d.pack(); d.setLocation(x, y); d.setSize(400, d.getHeight() + 20); UIUtil.fit(d); d.setVisible(true); setCurrentRowSelection(-1); } }); if (!(table instanceof SqlStatementTable)) { popup.add(new JSeparator()); JMenuItem qb = new JMenuItem("Query Builder"); popup.add(qb); qb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { List<String> whereClauses = new ArrayList<String>(); whereClauses.add(SqlUtil.replaceAliases(row.rowId, "A", "A")); getQueryBuilderDialog().buildQuery(table, true, false, new ArrayList<Association>(), whereClauses, dataModel); } }); if (!currentSelectedRowCondition.equals("") && currentSelectedRowCondition.equals(getAndConditionText()) && rows.size() == 1) { JMenuItem sr = new JMenuItem("Deselect Row"); popup.add(sr); sr.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { andCondition.setSelectedItem(""); reloadRows(); } }); } else { JMenuItem sr = new JMenuItem("Select Row"); sr.setEnabled(rows.size() > 1); popup.add(sr); sr.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String cond = SqlUtil.replaceAliases(row.rowId, "A", "A"); String currentCond = getAndConditionText().trim(); if (currentCond.length() > 0) { cond = "(" + cond + ") and (" + currentCond + ")"; } andCondition.setSelectedItem(cond); currentSelectedRowCondition = cond; reloadRows(); } }); } JMenu sql = new JMenu("SQL/DML"); final String rowName = dataModel.getDisplayName(table) + "(" + SqlUtil.replaceAliases(row.rowId, null, null) + ")"; JMenuItem insert = new JMenuItem("Insert"); sql.add(insert); insert.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openSQLDialog("Insert Row " + rowName, x, y, SQLDMLBuilder.buildInsert(table, row, true, session)); } }); JMenuItem update = new JMenuItem("Update"); sql.add(update); update.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openSQLDialog("Update Row " + rowName, x, y, SQLDMLBuilder.buildUpdate(table, row, true, session)); } }); JMenuItem delete = new JMenuItem("Delete"); sql.add(delete); delete.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openSQLDialog("Delete Row " + rowName, x, y, SQLDMLBuilder.buildDelete(table, row, true, session)); } }); popup.add(sql); } } return popup; } /** * Creates popup menu for SQL. * @param forNavTree */ public JPopupMenu createSqlPopupMenu(final Row parentrow, final int rowIndex, final int x, final int y, boolean forNavTree) { JPopupMenu popup = new JPopupMenu(); JMenuItem qb = new JMenuItem("Open Query Builder"); popup.add(qb); qb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openQueryBuilder(); } }); popup.add(new JSeparator()); JMenuItem exportData = new JMenuItem("Export Data"); popup.add(exportData); exportData.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openExtractionModelEditor(true); } }); JMenuItem extractionModel = new JMenuItem("Create Extraction Model"); popup.add(extractionModel); extractionModel.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openExtractionModelEditor(false); } }); popup.add(new JSeparator()); if (!forNavTree) { JMenuItem det = new JMenuItem("Details"); popup.add(det); det.setEnabled(rows.size() > 0); det.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openDetails(x, y); } }); } // popup.add(new JSeparator()); JMenu sqlDml = new JMenu("SQL/DML"); popup.add(sqlDml); JMenuItem insertNewRow = new JMenuItem("Insert New Row"); sqlDml.add(insertNewRow); final String tableName = dataModel.getDisplayName(table); insertNewRow.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openSQLDialog("Insert New Row Into " + tableName, x, y, SQLDMLBuilder.buildInsert(table, createNewRow(parentrow, table), true, session)); } }); JMenuItem insert = new JMenuItem("Inserts"); sqlDml.add(insert); insert.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openSQLDialog("Insert Into " + tableName, x, y, new Object() { public String toString() { return SQLDMLBuilder.buildInsert(table, rows, session); }}); } }); JMenuItem update = new JMenuItem("Updates"); sqlDml.add(update); update.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openSQLDialog("Update " + tableName, x, y, new Object() { public String toString() { return SQLDMLBuilder.buildUpdate(table, rows, session); }}); } }); JMenuItem delete = new JMenuItem("Deletes"); sqlDml.add(delete); delete.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { openSQLDialog("Delete from " + tableName, x, y, new Object() { public String toString() { return SQLDMLBuilder.buildDelete(table, rows, session); }}); } }); insert.setEnabled(rows.size() > 0); update.setEnabled(rows.size() > 0); delete.setEnabled(rows.size() > 0); popup.add(new JSeparator()); JMenuItem snw = new JMenuItem("Show in New Window"); popup.add(snw); snw.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { showInNewWindow(); } }); JMenuItem al = new JMenuItem("Append Layout..."); popup.add(al); al.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { appendLayout(); } }); popup.add(new JSeparator()); JMenuItem m = new JMenuItem("Hide from View"); popup.add(m); m.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { onHide(); } }); m = new JMenuItem("Close" + (getChildBrowsers().isEmpty()? "" : " Subtree")); popup.add(m); m.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { closeSubTree(BrowserContentPane.this); } private void closeSubTree(BrowserContentPane cp) { for (RowBrowser c: cp.getChildBrowsers()) { closeSubTree(c.browserContentPane); } cp.close(); } }); return popup; } void openExtractionModelEditor(boolean doExport) { Component parent = SwingUtilities.getWindowAncestor(this); if (parent == null) { parent = this; } parent.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { String file; String ts = new SimpleDateFormat("HH-mm-ss-SSS").format(new Date()); File newFile; Table stable = table; String subjectCondition; QueryBuilderDialog.Relationship root = createQBRelations(false); Collection<Association> restrictedAssociations = new HashSet<Association>(); Collection<Association> restrictedDependencies = new HashSet<Association>(); Collection<RestrictionDefinition> restrictedDependencyDefinitions = new HashSet<RestrictionDefinition>(); List<RestrictionDefinition> restrictionDefinitions = createRestrictions(root, stable, restrictedAssociations, restrictedDependencies, restrictedDependencyDefinitions); // if (!restrictedDependencies.isEmpty()) { Set<String> parents = new TreeSet<String>(); for (Association association: restrictedDependencies) { parents.add(dataModel.getDisplayName(association.destination)); } // String pList = ""; // int i = 0; // for (String p: parents) { // pList += p + "\n"; // if (++i > 20) { // break; // } // } final SbEDialog sbEDialog = new SbEDialog(SwingUtilities.getWindowAncestor(this), (doExport? "Export rows and related rows from \"" : "Create Extraction Model for Subject \"") + dataModel.getDisplayName(stable) + "\".", (parents.isEmpty()? "" : ("\n\n" + parents.size() + " disregarded parent tables."))); if (doExport) { sbEDialog.setTitle("Export Data"); } sbEDialog.regardButton.setVisible(!parents.isEmpty()); sbEDialog.dispose(); DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer() { @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { Component result = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (value instanceof DefaultMutableTreeNode) { if (!(((DefaultMutableTreeNode) value).getUserObject() instanceof String)) { if (result instanceof JLabel) { ((JLabel) result).setForeground(Color.red); } else { ((JLabel) result).setForeground(Color.black); } } } return result; } }; renderer.setOpenIcon(null); renderer.setLeafIcon(null); renderer.setClosedIcon(null); sbEDialog.browserTree.setCellRenderer(renderer); int[] count = new int[] { 0 }; DefaultTreeModel treeModel = new DefaultTreeModel(addChildNodes(this, restrictedDependencies, count)); sbEDialog.browserTree.setModel(treeModel); for (int i = 0; i < count[0]; ++i) { sbEDialog.browserTree.expandRow(i); } sbEDialog.browserTree.scrollRowToVisible(0); sbEDialog.browserTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { sbEDialog.browserTree.getSelectionModel().clearSelection(); } }); sbEDialog.setVisible(true); if (!sbEDialog.ok) { return; } if (sbEDialog.regardButton.isSelected()) { restrictionDefinitions.removeAll(restrictedDependencyDefinitions); for (Association a: restrictedDependencies) { disableDisregardedNonParentsOfDestination(a, restrictedAssociations, restrictionDefinitions); } } // int option = JOptionPane.showOptionDialog(parent, "Disregarded parent tables:\n\n" + pList + "\n", "Disregarded parent tables", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, null, new Object[] { "regard parent tables", "Ok" }, "regard parent tables"); // switch (option) { // case 0: // restrictionDefinitions.removeAll(restrictedDependencyDefinitions); // for (Association a: restrictedDependencies) { // disableDisregardedNonParentsOfDestination(a, restrictedAssociations, restrictionDefinitions); // } // break; // case 1: // break; // default: return; // } // } subjectCondition = root.needsAnchor? root.anchorWhereClause : root.whereClause; if (doExport && (getParentBrowser() != null || isLimitExceeded)) { subjectCondition = root.whereClause; StringBuilder sb = new StringBuilder(); boolean f = true; for (Row row: rows) { if (f) { f = false; } else { sb.append(" or \n"); } sb.append("(" + row.rowId + ")"); } if (subjectCondition != null && subjectCondition.trim().length() > 0) { subjectCondition = "(" + subjectCondition + ")\nand\n(" + sb + ")"; } else { subjectCondition = sb.toString(); } } if (subjectCondition == null) { subjectCondition = ""; } // if (doExport && isLimitExceeded && rows != null && !rows.isEmpty()) { // StringBuilder sb = new StringBuilder(); // boolean f = true; // for (Row row: rows) { // if (f) { // f = false; // } else { // sb.append(" or "); // } // sb.append("(" + row.rowId + ")"); // } // subjectCondition = sb.toString(); // } subjectCondition = SqlUtil.replaceAliases(subjectCondition, "T", "T"); for (int i = 1; ; ++i) { file = "extractionmodel" + File.separator + "by-example"; newFile = new File(file); newFile.mkdirs(); file += File.separator + "SbE-" + (dataModel.getDisplayName(stable).replaceAll("[\"'\\[\\]]", "")) + "-" + ts + (i > 1? "-" + Integer.toString(i) : "") + ".csv"; newFile = new File(file); if (!newFile.exists()) { break; } } Map<String, Map<String, double[]>> positions = new TreeMap<String, Map<String,double[]>>(); collectPositions(positions); String currentModelSubfolder = DataModelManager.getCurrentModelSubfolder(); dataModel.save(file, stable, subjectCondition, ScriptFormat.SQL, restrictionDefinitions, positions, new ArrayList<ExtractionModel.AdditionalSubject>(), currentModelSubfolder); if (DataBrowserContext.isStandAlone() && !doExport) { parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); JOptionPane.showMessageDialog(parent, "Jailer Extraction Model created:\n'" + file+ "'\n\nJailer Database Subsetter Tool can be found at http://jailer.sourceforge.net", "Jailer Extraction Model", JOptionPane.INFORMATION_MESSAGE); } else { ExtractionModelFrame extractionModelFrame = ExtractionModelFrame.createFrame(file, false, !doExport); extractionModelFrame.setDbConnectionDialogClone(getDbConnectionDialog()); if (doExport) { extractionModelFrame.openExportDialog(false, new Runnable() { @Override public void run() { try { reloadDataModel(); } catch (Exception e) { throw new RuntimeException(e); } } }); extractionModelFrame.dispose(); } else { extractionModelFrame.markDirty(); extractionModelFrame.expandAll(); } newFile.delete(); } } catch (Throwable e) { parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); UIUtil.showException(this, "Error", e, session); } finally { parent.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } } private DefaultMutableTreeNode addChildNodes(BrowserContentPane browserContentPane, Collection<Association> restrictedDependencies, int[] count) { DefaultMutableTreeNode node = new DefaultMutableTreeNode(dataModel.getDisplayName(browserContentPane.table)); count[0]++; Set<Table> regardedChildren = new HashSet<Table>(); for (RowBrowser rb: browserContentPane.getChildBrowsers()) { DefaultMutableTreeNode childNode = addChildNodes(rb.browserContentPane, restrictedDependencies, count); node.add(childNode); regardedChildren.add(rb.browserContentPane.table); } for (final Association dep: restrictedDependencies) { if (dep.source == browserContentPane.table && !regardedChildren.contains(dep.destination)) { node.add(new DefaultMutableTreeNode(new Object() { String item = dataModel.getDisplayName(dep.destination); public String toString() { return item; } })); count[0]++; } } return node; } private void disableDisregardedNonParentsOfDestination(Association a, Collection<Association> regardedAssociations, List<RestrictionDefinition> restrictionDefinitions) { for (Association npa: a.destination.associations) { if (!regardedAssociations.contains(npa)) { regardedAssociations.add(npa); if (npa.isInsertDestinationBeforeSource()) { disableDisregardedNonParentsOfDestination(npa, regardedAssociations, restrictionDefinitions); } else { RestrictionDefinition rest = new RestrictionDefinition(npa.source, npa.destination, npa.getName(), null, true); restrictionDefinitions.add(rest); } } } } private static class RestrictionLiteral { public String condition; public int distanceFromRoot; public boolean isIgnored; public boolean isIgnoredIfReversalIsRestricted = false; public String toString() { return "Cond:" + condition + " Dist: " + distanceFromRoot + " isIgnored: " + isIgnored + " isIgnoredIfReversalIsRestricted: " + isIgnoredIfReversalIsRestricted; } } /** * Creates restriction according to the given {@link Relationship} tree. * * @param root root of tree * @return restrictions */ private List<RestrictionDefinition> createRestrictions(Relationship root, Table subject, Collection<Association> regardedAssociations, Collection<Association> restrictedDependencies, Collection<RestrictionDefinition> restrictedDependencyDefinitions) { List<RestrictionDefinition> restrictionDefinitions = new ArrayList<RestrictionDefinition>(); Map<Association, List<RestrictionLiteral>> restrictionLiterals = new HashMap<Association, List<RestrictionLiteral>>(); collectRestrictionLiterals(restrictionLiterals, root, subject, 0); for (Association association: restrictionLiterals.keySet()) { RestrictionDefinition rest = createRestrictionDefinition(association, restrictionLiterals, true); regardedAssociations.add(association); if (rest.isIgnored || (rest.condition != null && rest.condition.trim().length() != 0)) { restrictionDefinitions.add(rest); if (association.isInsertDestinationBeforeSource() && rest.isIgnored) { restrictedDependencies.add(association); restrictedDependencyDefinitions.add(rest); } } } return restrictionDefinitions; } private RestrictionDefinition createRestrictionDefinition(Association association, Map<Association, List<RestrictionLiteral>> restrictionLiterals, boolean checkReversal) { List<RestrictionLiteral> lits = restrictionLiterals.get(association); boolean useDistance = false; boolean hasTrue = false; boolean hasNotTrue = false; boolean hasNotFalse = false; Integer lastDist = null; for (RestrictionLiteral l: lits) { if (l.isIgnoredIfReversalIsRestricted) { if (checkReversal) { RestrictionDefinition revRest = createRestrictionDefinition(association.reversalAssociation, restrictionLiterals, false); if (revRest.isIgnored || (revRest.condition != null && revRest.condition.trim().length() > 0)) { l.isIgnored = true; } else { l.isIgnored = false; l.condition = null; } l.isIgnoredIfReversalIsRestricted = false; } else { l.isIgnored = true; l.isIgnoredIfReversalIsRestricted = false; } } // disabled since 5.0 // if (lastDist != null && lastDist != l.distanceFromRoot) { // useDistance = true; // } // lastDist = l.distanceFromRoot; if (!l.isIgnored) { hasNotFalse = true; if (l.condition == null || l.condition.trim().length() == 0) { hasTrue = true; } else { hasNotTrue = true; } } else { hasNotTrue = true; } } boolean isIgnored; String condition = null; if (!hasNotFalse) { isIgnored = true; } else if (!hasNotTrue) { isIgnored = false; } else if (hasTrue && !useDistance) { isIgnored = false; } else { for (RestrictionLiteral l: lits) { if (!l.isIgnored) { String c = null; if (useDistance) { c = l.distanceFromRoot == 0? "A.$IS_SUBJECT" : ("A.$DISTANCE=" + l.distanceFromRoot); } if (l.condition != null && l.condition.trim().length() > 0) { if (c == null) { c = l.condition; } else { c = c + " and (" + l.condition + ")"; } } if (condition == null) { condition = c; } else { condition += " or " + c; } } } isIgnored = false; } RestrictionDefinition rest = new RestrictionDefinition(association.source, association.destination, association.getName(), condition, isIgnored); return rest; } /** * Collects restriction literals per association according to a given {@link Relationship} tree. * * @param restrictionLiterals to put literals into * @param root root of tree * @param distanceFromRoot distance */ private void collectRestrictionLiterals(Map<Association, List<RestrictionLiteral>> restrictionLiterals, Relationship root, Table subject, int distanceFromRoot) { for (Association association: subject.associations) { List<Relationship> children = new ArrayList<QueryBuilderDialog.Relationship>(); for (Relationship r: root.children) { if (r.association == association) { children.add(r); } } if (children.isEmpty()) { children.add(null); } for (Relationship child: children) { RestrictionLiteral restrictionLiteral = new RestrictionLiteral(); restrictionLiteral.distanceFromRoot = distanceFromRoot; restrictionLiteral.isIgnored = false; if (child == null) { restrictionLiteral.isIgnored = true; // if (association.isInsertDestinationBeforeSource()) { if (root.association != null && association == root.association.reversalAssociation) { if (association.getCardinality() == Cardinality.MANY_TO_ONE || association.getCardinality() == Cardinality.ONE_TO_ONE) { restrictionLiteral.isIgnoredIfReversalIsRestricted = true; } } // } } else { restrictionLiteral.condition = child.whereClause == null? "" : SqlUtil.replaceAliases(child.whereClause, "B", "A"); collectRestrictionLiterals(restrictionLiterals, child, association.destination, distanceFromRoot + 1); } List<RestrictionLiteral> literals = restrictionLiterals.get(association); if (literals == null) { literals = new ArrayList<BrowserContentPane.RestrictionLiteral>(); restrictionLiterals.put(association, literals); } literals.add(restrictionLiteral); } } } private void openSQLDialog(String titel, int x, int y, Object sql) { setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); JDialog d; try { String LF = System.getProperty("line.separator", "\n"); String sqlString = sql.toString().trim() + LF; if (sqlString.length() > 2L*1024L*1024L) { if (1 == JOptionPane.showOptionDialog(this, "SQL Script is too long (" + (sqlString.length() / 1024) + " KB) to be edited.", "SQL Script too long", JOptionPane.YES_NO_OPTION, JOptionPane.INFORMATION_MESSAGE, null, new Object[] { "Ok", "Save Script" }, "Save Script")) { String fn = UIUtil.choseFile(null, ".", "Save SQL Script", ".sql", this, false, false, false); if (fn != null) { try { PrintWriter out = new PrintWriter(new FileWriter(fn)); out.print(sqlString); out.close(); } catch (Throwable e) { UIUtil.showException(this, "Error saving script", e, UIUtil.EXCEPTION_CONTEXT_USER_ERROR); } } } return; } d = new JDialog(getOwner(), "SQL/DML - " + titel, true); d.getContentPane().add(new SQLDMLPanel(sqlString, session, new Runnable() { @Override public void run() { reloadRows(); } })); d.pack(); d.setLocation(x - 50, y - 100); d.setSize(700, Math.max(d.getHeight() + 20, 400)); d.setLocation(getOwner().getX() + (getOwner().getWidth() - d.getWidth()) / 2, Math.max(0, getOwner().getY() + (getOwner().getHeight() - d.getHeight()) / 2)); UIUtil.fit(d); } catch (Throwable e) { UIUtil.showException(this, "Error", e); return; } finally { setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); } d.setVisible(true); } protected void setCurrentRowSelection(int i) { currentRowSelection = i; if (i >= 0) { currentClosure.clear(); findClosure(rows.get(rowsTable.getRowSorter().convertRowIndexToModel(i))); Rectangle visibleRect = rowsTable.getVisibleRect(); Rectangle pos = rowsTable.getCellRect(i, 0, false); rowsTable.scrollRectToVisible(new Rectangle(visibleRect.x, pos.y, 1, pos.height)); } // if (currentClosure.size() == 1) { // currentClosure.clear(); // } currentClosureRowIDs.clear(); for (Pair<BrowserContentPane, Row> r: currentClosure) { currentClosureRowIDs.add(new Pair<BrowserContentPane, String>(r.a, r.b.rowId)); } // Set<RowBrowser> toAdjust = new HashSet<Desktop.RowBrowser>(getChildBrowsers()); // if (getParentBrowser() != null) { // toAdjust.add(getParentBrowser()); // } adjustClosure(this); } private JPopupMenu createNavigationMenu(JPopupMenu popup, final Row row, final int rowIndex, List<String> assList, Map<String, Association> assMap, String title, String prefix, final boolean navigateFromAllRows, final int rowCountPriority, final AllNonEmptyItem allNonEmptyItem, final Object context) { JMenu nav = new JScrollC2Menu(title); if (prefix.equals("1")) { nav.setForeground(new java.awt.Color(170, 0, 0)); } if (prefix.equals("2")) { nav.setForeground(new java.awt.Color(0, 112, 0)); } if (prefix.equals("3")) { nav.setForeground(new java.awt.Color(0, 100, 255)); } if (prefix.equals("4")) { nav.setForeground(new java.awt.Color(153, 153, 153)); } JMenu current = nav; int l = 0; for (String name : assList) { if (!name.startsWith(prefix)) { continue; } final Association association = assMap.get(name); ++l; // if (++l > 300) { // JMenu p = new JMenu("more..."); // if (current != null) { // GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints(); // gridBagConstraints.gridx = 1; // gridBagConstraints.gridy = l; // gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; // gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; // gridBagConstraints.weightx = 1.0; // current.getPopupMenu().add(p, gridBagConstraints); // } else { // popup.add(p); // } // l = 1; // current = p; // current.getPopupMenu().setLayout(new GridBagLayout()); // } final JMenuItem item = new JMenuItem(" " + (name.substring(1)) + " "); final JLabel countLabel = new JLabel(". >99999 ") { @Override public void paint(Graphics g) { if (!getText().startsWith(".")) { super.paint(g); } } }; boolean excludeFromANEmpty = false; for (RowBrowser child: getChildBrowsers()) { if (association == child.association) { if (rowIndex < 0 && child.rowIndex < 0 || rowIndex == child.rowIndex) { item.setFont(new Font(item.getFont().getName(), item.getFont().getStyle() | Font.ITALIC, item.getFont().getSize())); excludeFromANEmpty = true; break; } } } final ActionListener itemAction = new ActionListener() { public void actionPerformed(ActionEvent e) { highlightedRows.add(rowIndex); if (navigateFromAllRows) { navigateTo(association, -1, null); } else { navigateTo(association, rowIndex, row); } } }; item.addActionListener(itemAction); if (!excludeFromANEmpty) { allNonEmptyItem.todo++; } final boolean fExcludeFromANEmpty = excludeFromANEmpty; if (!isPending && !rows.isEmpty()) { getRunnableQueue().add(new RunnableWithPriority() { final int MAX_RC = 1000; @Override public int getPriority() { return rowCountPriority; } @Override public void run() { List<Row> r; Pair<String, Association> key; if (rowIndex < 0) { r = rows; key = new Pair<String, Association>("", association); } else { r = Collections.singletonList(row); key = new Pair<String, Association>(row.rowId, association); } Pair<Long, Long> cachedCount = rowCountCache.get(key); long rowCount; if (cachedCount != null && cachedCount.b > System.currentTimeMillis()) { rowCount = cachedCount.a; } else { RowCounter rc = new RowCounter(table, association, r, session, rowIdSupport); try { rowCount = rc.countRows(getAndConditionText(), context, MAX_RC + 1, false); } catch (SQLException e) { rowCount = -1; } rowCountCache.put(key, new Pair<Long, Long>(rowCount, System.currentTimeMillis() + MAX_ROWCOUNTCACHE_RETENTION_TIME)); } final long count = rowCount; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { String cs = " " + (count < 0? "?" : (count > MAX_RC)? (">" + MAX_RC) : count) + " "; countLabel.setText(cs); if (count == 0) { countLabel.setForeground(Color.lightGray); } if (!fExcludeFromANEmpty) { allNonEmptyItem.rowsCounted(count, itemAction); } } }); } }); } if (current != null) { GridBagConstraints gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = l; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.weightx = 1.0; current.getPopupMenu().add(item, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = l; gridBagConstraints.fill = java.awt.GridBagConstraints.NONE; gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; gridBagConstraints.weightx = 1.0; current.getPopupMenu().add(countLabel, gridBagConstraints); } else { popup.add(item); } } if (l > 0) { popup.add(nav); } return popup; } private long lastReloadTS = 0; /** * Reloads rows. */ public void reloadRows() { if (!suppressReload) { lastReloadTS = System.currentTimeMillis(); cancelLoadJob(true); setPendingState(true, true); rows.clear(); updateMode("loading"); setPendingState(false, false); int limit = 100; if (limitBox.getSelectedItem() instanceof Integer) { limit = (Integer) limitBox.getSelectedItem(); } LoadJob reloadJob = new LoadJob(limit, (table instanceof SqlStatementTable)? sqlBrowserContentPane.sqlEditorPane.getText() : getAndConditionText(), selectDistinctCheckBox.isSelected()); synchronized (this) { currentLoadJob = reloadJob; } getRunnableQueue().add(reloadJob); } } /** * Reload rows from {@link #table}. * * @param rows * to put the rows into * @param context * cancellation context * @param limit * row number limit */ private void reloadRows(String andCond, final List<Row> rows, Object context, int limit, boolean selectDistinct) throws SQLException { try { session.setSilent(true); reloadRows(andCond, rows, context, limit, selectDistinct, null); return; } catch (SQLException e) { Session._log.warn("failed, try another strategy (" + e.getMessage() + ")"); } finally { session.setSilent(false); } Set<String> existingColumnsLowerCase = null; if (!(table instanceof SqlStatementTable)) { existingColumnsLowerCase = findColumnsLowerCase(table, session); } reloadRows(andCond, rows, context, limit, selectDistinct, existingColumnsLowerCase); } /** * Finds the columns of a given {@link Table}. * * @param table the table * @param session the statement executor for executing SQL-statements */ private Set<String> findColumnsLowerCase(Table table, Session session) { try { Set<String> columns = new HashSet<String>(); DatabaseMetaData metaData = session.getMetaData(); Quoting quoting = new Quoting(session); String defaultSchema = JDBCMetaDataBasedModelElementFinder.getDefaultSchema(session, session.getSchema()); String schema = quoting.unquote(table.getOriginalSchema(defaultSchema)); String tableName = quoting.unquote(table.getUnqualifiedName()); ResultSet resultSet = JDBCMetaDataBasedModelElementFinder.getColumns(session, metaData, schema, tableName, "%"); while (resultSet.next()) { String colName = resultSet.getString(4).toLowerCase(); columns.add(colName); } resultSet.close(); if (columns.isEmpty()) { if (session.getMetaData().storesUpperCaseIdentifiers()) { schema = schema.toUpperCase(); tableName = tableName.toUpperCase(); } else { schema = schema.toLowerCase(); tableName = tableName.toLowerCase(); } resultSet = JDBCMetaDataBasedModelElementFinder.getColumns(session, metaData, schema, tableName, "%"); while (resultSet.next()) { String colName = resultSet.getString(4).toLowerCase(); columns.add(colName); } } if (columns.isEmpty()) { return null; } return columns; } catch (Exception e) { return null; } } /** * Reload rows from {@link #table}. * * @param rows * to put the rows into * @param context * cancellation context * @param limit * row number limit */ private void reloadRows(String andCond, final List<Row> rows, Object context, int limit, boolean selectDistinct, Set<String> existingColumnsLowerCase) throws SQLException { if (table instanceof SqlStatementTable) { try { session.setSilent(true); Map<String, List<Row>> rowsMap = new HashMap<String, List<Row>>(); reloadRows(null, andCond, null, rowsMap, context, limit, false, null, existingColumnsLowerCase); if (rowsMap.get("") != null) { rows.addAll(rowsMap.get("")); } } finally { session.setSilent(false); } return; } List<Row> pRows = parentRows; if (pRows == null) { pRows = Collections.singletonList(parentRow); } else { pRows = new ArrayList<Row>(pRows); } Map<String, Row> rowSet = new HashMap<String, Row>(); if (parentRows != null) { beforeReload(); } noNonDistinctRows = 0; noDistinctRows = 0; if (association != null && rowIdSupport.getPrimaryKey(association.source).getColumns().isEmpty()) { loadRowBlocks(null, andCond, rows, context, limit, selectDistinct, pRows, rowSet, 1, existingColumnsLowerCase); } else { if (useInlineViewForResolvingAssociation(session)) { try { InlineViewStyle inlineViewStyle = session.getInlineViewStyle(); if (inlineViewStyle != null) { loadRowBlocks(inlineViewStyle, andCond, rows, context, limit, selectDistinct, pRows, rowSet, 510, existingColumnsLowerCase); return; } } catch (Exception e) { Session._log.warn("failed, try another blocking-size (" + e.getMessage() + ")"); } } try { loadRowBlocks(null, andCond, rows, context, limit, selectDistinct, pRows, rowSet, 510, existingColumnsLowerCase); return; } catch (SQLException e) { Session._log.warn("failed, try another blocking-size (" + e.getMessage() + ")"); } try { loadRowBlocks(null, andCond, rows, context, limit, selectDistinct, pRows, rowSet, 300, existingColumnsLowerCase); return; } catch (SQLException e) { Session._log.warn("failed, try another blocking-size (" + e.getMessage() + ")"); } try { loadRowBlocks(null, andCond, rows, context, limit, selectDistinct, pRows, rowSet, 100, existingColumnsLowerCase); return; } catch (SQLException e) { Session._log.warn("failed, try another blocking-size (" + e.getMessage() + ")"); } try { loadRowBlocks(null, andCond, rows, context, limit, selectDistinct, pRows, rowSet, 40, existingColumnsLowerCase); return; } catch (SQLException e) { Session._log.warn("failed, try another blocking-size (" + e.getMessage() + ")"); } } loadRowBlocks(null, andCond, rows, context, limit, selectDistinct, pRows, rowSet, 1, existingColumnsLowerCase); } static boolean useInlineViewForResolvingAssociation(Session session) { return session.dbms.isUseInlineViewsInDataBrowser(); } private void loadRowBlocks(InlineViewStyle inlineViewStyle, String andCond, final List<Row> rows, Object context, int limit, boolean selectDistinct, List<Row> pRows, Map<String, Row> rowSet, int NUM_PARENTS, Set<String> existingColumnsLowerCase) throws SQLException { List<List<Row>> parentBlocks = new ArrayList<List<Row>>(); List<Row> currentBlock = new ArrayList<Row>(); Set<String> regPRows = new HashSet<String>(); parentBlocks.add(currentBlock); for (Row pRow : pRows) { if (currentBlock.size() >= NUM_PARENTS) { currentBlock = new ArrayList<Row>(); parentBlocks.add(currentBlock); } currentBlock.add(pRow); } if (!pRows.isEmpty()) for (List<Row> pRowBlockI : parentBlocks) { List<Row> pRowBlock = pRowBlockI; Map<String, List<Row>> newBlockRows = new HashMap<String, List<Row>>(); boolean loaded = false; if (pRowBlock.size() == 1 && pRowBlock.get(0) == null) { pRowBlock = null; } if (session.dbms.getSqlLimitSuffix() != null) { try { session.setSilent(true); reloadRows(inlineViewStyle, andCond, pRowBlock, newBlockRows, context, limit, false, session.dbms.getSqlLimitSuffix(), existingColumnsLowerCase); loaded = true; } catch (SQLException e) { Session._log.warn("failed, try another limit-strategy (" + e.getMessage() + ")"); } finally { session.setSilent(false); } } if (!loaded) { try { session.setSilent(true); reloadRows(inlineViewStyle, andCond, pRowBlock, newBlockRows, context, limit, true, null, existingColumnsLowerCase); loaded = true; } catch (SQLException e) { Session._log.warn("failed, try another limit-strategy (" + e.getMessage() + ")"); } finally { session.setSilent(false); } if (!loaded) { try { session.setSilent(true); reloadRows(inlineViewStyle, andCond, pRowBlock, newBlockRows, context, limit, false, null, existingColumnsLowerCase); } finally { session.setSilent(false); } } } if (pRowBlock == null) { pRowBlock = new ArrayList<Row>(); pRowBlock.add(null); } int blockNr = 0; for (Row pRow: pRowBlock) { boolean dupParent = false; if (pRow != null) { if (regPRows.contains(pRow.rowId)) { dupParent = true; } regPRows.add(pRow.rowId); } List<Row> newRows = new ArrayList<Row>(); String rId = pRow == null? "" : pRow.rowId; if (newBlockRows.get(rId) != null) { newRows.addAll(newBlockRows.get(rId)); } if (parentRows != null) { if (!newRows.isEmpty()) { newRows.get(newRows.size() - 1).setBlockEnd(true); ++blockNr; for (Row r: newRows) { r.setBlockNr(blockNr); } } for (Row row : newRows) { Row exRow = rowSet.get(row.rowId); if (!dupParent) { if (exRow != null) { ++noNonDistinctRows; } else { ++noDistinctRows; } } if (exRow != null && (selectDistinct || dupParent)) { addRowToRowLink(pRow, exRow); } else { rows.add(row); addRowToRowLink(pRow, row); rowSet.put(row.rowId, row); --limit; } } } else { rows.addAll(newRows); limit -= newRows.size(); } if (limit <= 0) { break; } } if (limit <= 0) { break; } } } /** * Reload rows from {@link #table}. * * @param rows * to put the rows into * @param context * cancellation context * @param rowCache * @param allPRows */ private void reloadRows(InlineViewStyle inlineViewStyle, String andCond, final List<Row> parentRows, final Map<String, List<Row>> rows, Object context, int limit, boolean useOLAPLimitation, String sqlLimitSuffix, Set<String> existingColumnsLowerCase) throws SQLException { reloadRows0(inlineViewStyle, andCond, parentRows, rows, context, parentRows == null? limit : Math.max(5000, limit), useOLAPLimitation, sqlLimitSuffix, existingColumnsLowerCase); } /** * Gets qualified table name. * * @param t the table * @return qualified name of t */ private String qualifiedTableName(Table t, Quoting quoting) { String schema = t.getSchema(""); if (schema.length() == 0) { return quoting.requote(t.getUnqualifiedName()); } return quoting.requote(schema) + "." + quoting.requote(t.getUnqualifiedName()); } /** * Reload rows from {@link #table}. * * @param rows * to put the rows into * @param context * cancellation context */ private void reloadRows0(InlineViewStyle inlineViewStyle, String andCond, final List<Row> parentRows, final Map<String, List<Row>> rows, Object context, int limit, boolean useOLAPLimitation, String sqlLimitSuffix, Set<String> existingColumnsLowerCase) throws SQLException { String sql = "Select "; final Quoting quoting = new Quoting(session); final Set<String> pkColumnNames = new HashSet<String>(); final Set<String> parentPkColumnNames = new HashSet<String>(); final boolean selectParentPK = association != null && parentRows != null && parentRows.size() > 1; final Set<Integer> unknownColumnIndexes = new HashSet<Integer>(); if (table instanceof SqlStatementTable) { sql = andCond; table.setColumns(new ArrayList<Column>()); } else { String olapPrefix = "Select "; String olapSuffix = ") S Where S." + ROWNUMBERALIAS + " <= " + limit; boolean limitSuffixInSelectClause = sqlLimitSuffix != null && (sqlLimitSuffix.toLowerCase().startsWith("top ") || sqlLimitSuffix.toLowerCase().startsWith("first ")); if (sqlLimitSuffix != null && limitSuffixInSelectClause) { sql += (sqlLimitSuffix.replace("%s", Integer.toString(limit))) + " "; } int colI = 1; boolean f = true; if (selectParentPK) { int i = 0; for (Column column: rowIdSupport.getPrimaryKey(association.source).getColumns()) { String name = quoting.requote(column.name); sql += (!f ? ", " : "") + "B." + name + " AS B" + i; olapPrefix += (!f ? ", " : "") + "S.B" + i; ++i; ++colI; f = false; } } int i = 0; for (Column column : rowIdSupport.getColumns(table)) { String name = quoting.requote(column.name); if (existingColumnsLowerCase != null && !existingColumnsLowerCase.contains(quoting.unquote(name).toLowerCase())) { sql += (!f ? ", " : "") + "'?' AS A" + i; unknownColumnIndexes.add(colI); } else { sql += (!f ? ", " : "") + "A." + name + " AS A" + i; } olapPrefix += (!f ? ", " : "") + "S.A" + i; ++i; ++colI; f = false; } f = true; String orderBy = ""; String olapOrderBy = ""; if (selectParentPK) { int j = 0; for (Column pk: rowIdSupport.getPrimaryKey(association.source).getColumns()) { parentPkColumnNames.add(quoting.requote(pk.name)); orderBy += (f ? "" : ", ") + "B." + quoting.requote(pk.name); olapOrderBy += (f ? "" : ", ") + "S.B" + j; ++j; f = false; } } int j = 0; for (Column pk: rowIdSupport.getPrimaryKey(table).getColumns()) { pkColumnNames.add(quoting.requote(pk.name)); orderBy += (f ? "" : ", ") + "A." + quoting.requote(pk.name); olapOrderBy += (f ? "" : ", ") + "S.A" + j; ++j; f = false; } if (useOLAPLimitation) { sql += ", row_number() over("; if (useOLAPLimitation) { sql += "order by -1"; // + orderBy; } sql += ") as " + ROWNUMBERALIAS + ""; } sql += " From "; if (association != null) { sql += qualifiedTableName(association.source, quoting) + " B join "; } sql += qualifiedTableName(table, quoting) + " A"; if (association != null) { if (association.reversed) { sql += " on " + association.getUnrestrictedJoinCondition(); } else { sql += " on " + SqlUtil.reversRestrictionCondition(association.getUnrestrictedJoinCondition()); } } boolean whereExists = false; if (parentRows != null && !parentRows.isEmpty()) { if (association != null && parentRows.get(0).rowId.length() == 0) { throw new SqlException("Missing primary key for table: \"" + association.source.getName() + "\" ", null, null); } if (parentRows.size() == 1) { sql += " Where (" + parentRows.get(0).rowId + ")"; } else { StringBuilder sb = new StringBuilder(); if (inlineViewStyle != null && association != null) { sb.append(" join "); List<String> columnNames = new ArrayList<String>(); for (Column pkColumn: rowIdSupport.getPrimaryKey(association.source).getColumns()) { columnNames.add(pkColumn.name); } String[] columnNamesAsArray = columnNames.toArray(new String[columnNames.size()]); sb.append(inlineViewStyle.head(columnNamesAsArray)); int rowNumber = 0; for (Row parentRow: parentRows) { if (rowNumber > 0) { sb.append(inlineViewStyle.separator()); } sb.append(inlineViewStyle.item(parentRow.primaryKey, columnNamesAsArray, rowNumber)); ++rowNumber; } sb.append(inlineViewStyle.terminator("C", columnNamesAsArray)); sb.append(" on ("); boolean f2 = true; for (String pkColumnName: columnNames) { if (!f2) { sb.append(" and "); } sb.append("B." + pkColumnName + " = " + "C." + pkColumnName); f2 = false; } sb.append(")"); } else { for (Row parentRow: parentRows) { if (sb.length() == 0) { sb.append(" Where (("); } else { sb.append(" or ("); } sb.append(parentRow.rowId).append(")"); } sb.append(")"); } sql += sb.toString(); } whereExists = true; } if (andCond.trim().length() > 0) { sql += (whereExists ? " and" : " Where") + " (" + ConditionEditor.toMultiLine(andCond) + ")"; } if (orderBy.length() > 0) { if (sqlLimitSuffix != null && !useOLAPLimitation) { sql += " order by " + orderBy; } } olapPrefix += " From ("; if (useOLAPLimitation) { sql = olapPrefix + sql + olapSuffix + " Order by " + olapOrderBy; } if (sqlLimitSuffix != null && !limitSuffixInSelectClause) { sql += " " + (sqlLimitSuffix.replace("%s", Integer.toString(limit))); } } if (sql.length() > 0) { final Map<Integer, Integer> pkPosToColumnPos = new HashMap<Integer, Integer>(); if (!(table instanceof SqlStatementTable)) { List<Column> pks = rowIdSupport.getPrimaryKey(table).getColumns(); List<Column> columns = rowIdSupport.getColumns(table); for (int i = 0; i < columns.size(); ++i) { Column column = columns.get(i); for (int j = 0; j < pks.size(); ++j) { if (column.name.equals(pks.get(j).name)) { pkPosToColumnPos.put(j, i); break; } } } } session.executeQuery(sql, new AbstractResultSetReader() { Map<Integer, Integer> typeCache = new HashMap<Integer, Integer>(); int rowNr = 0; @Override public void readCurrentRow(ResultSet resultSet) throws SQLException { if ((table instanceof SqlStatementTable) && rows.isEmpty()) { for (int ci = 1; ci <= getMetaData(resultSet).getColumnCount(); ++ci) { table.getColumns().add(new Column(getMetaData(resultSet).getColumnLabel(ci), getMetaData(resultSet).getColumnTypeName(ci), -1, -1)); } } int i = 1, vi = 0; String parentRowId = ""; if (selectParentPK) { Object v[] = new Object[rowIdSupport.getPrimaryKey(association.source).getColumns().size()]; for (Column column: rowIdSupport.getPrimaryKey(association.source).getColumns()) { parentRowId = readRowFromResultSet(parentPkColumnNames, resultSet, i, vi, parentRowId, v, column, null, null, unknownColumnIndexes); ++i; ++vi; } } else { if (parentRows != null && parentRows.size() == 1) { parentRowId = parentRows.get(0).rowId; } } Map<String, String> pkColumn = new HashMap<String, String>(); Map<String, String> pkColumnValue = new HashMap<String, String>(); Object v[] = new Object[rowIdSupport.getColumns(table).size()]; vi = 0; for (Column column: rowIdSupport.getColumns(table)) { readRowFromResultSet(pkColumnNames, resultSet, i, vi, "", v, column, pkColumn, pkColumnValue, unknownColumnIndexes); ++i; ++vi; } String rowId = ""; String[] primaryKey = null; PrimaryKey primaryKeys = rowIdSupport.getPrimaryKey(table); if (primaryKeys != null) { int pkPos = 0; primaryKey = new String[primaryKeys.getColumns().size()]; for (Column column : primaryKeys.getColumns()) { if (rowId.length() > 0) { rowId += " and "; } rowId += pkColumn.get(column.name); Integer colPos = pkPosToColumnPos.get(pkPos); if (colPos != null) { primaryKey[pkPos] = pkColumnValue.get(column.name); } ++pkPos; } } else { rowId = Integer.toString(++rowNr); } List<Row> cRows = rows.get(parentRowId); if (cRows == null) { cRows = new ArrayList<Row>(); rows.put(parentRowId, cRows); } cRows.add(new Row(rowId, primaryKey, v)); } private String readClob(Clob clob) throws SQLException, IOException { return readCharacterStream(clob.getCharacterStream()); } private String readSQLXML(SQLXML xml) throws SQLException, IOException { return readCharacterStream(xml.getCharacterStream()); } private String readCharacterStream(final Reader reader) throws IOException { final StringBuilder sb = new StringBuilder(); final BufferedReader br = new BufferedReader(reader); int b; while(-1 != (b = br.read())) { sb.append((char)b); if (sb.length() > MAXLOBLENGTH) { sb.append("..."); break; } } br.close(); return sb.toString(); } private String readRowFromResultSet(final Set<String> pkColumnNames, ResultSet resultSet, int i, int vi, String rowId, Object[] v, Column column, Map<String, String> pkColumn, Map<String, String> pkColumnValue, Set<Integer> unknownColumnIndexes) throws SQLException { Object value = ""; if (unknownColumnIndexes.contains(i)) { value = new UnknownValue() { public String toString() { return "?"; } }; } else { int type = SqlUtil.getColumnType(resultSet, getMetaData(resultSet), i, typeCache); if (type == Types.BLOB || type == Types.CLOB || type == Types.NCLOB || type == Types.SQLXML) { Object object = resultSet.getObject(i); if (object == null || resultSet.wasNull()) { value = null; } if (object instanceof Blob) { try { final long length = ((Blob) object).length(); value = new LobValue() { public String toString() { return "<Blob> " + length + " bytes"; } }; } catch (Exception e) { value = new LobValue() { public String toString() { return "<Blob>"; } }; } } if (object instanceof Clob) { try { final String content = readClob((Clob) object); value = new LobValue() { public String toString() { return content; } }; } catch (Exception e) { value = new LobValue() { public String toString() { return "<Clob>"; } };e.printStackTrace(); } } if (object instanceof SQLXML) { try { final String content = readSQLXML((SQLXML) object); value = new LobValue() { public String toString() { return content; } }; } catch (Exception e) { value = new LobValue() { public String toString() { return "<XML>"; } };e.printStackTrace(); } } } else { CellContentConverter cellContentConverter = getCellContentConverter(resultSet, session, session.dbms); Object o = cellContentConverter.getObject(resultSet, i); boolean isPK = false; if (pkColumnNames.isEmpty()) { isPK = type != Types.BLOB && type != Types.CLOB && type != Types.DATALINK && type != Types.JAVA_OBJECT && type != Types.NCLOB && type != Types.NULL && type != Types.OTHER && type != Types.REF && type != Types.SQLXML && type != Types.STRUCT; } if (pkColumnNames.contains(quoting.requote(column.name)) || isPK) { String cVal = cellContentConverter.toSql(o); String pkValue = "B." + quoting.requote(column.name) + ("null".equalsIgnoreCase(cVal)? " is null" : ("=" + cVal)); if (pkColumn != null) { pkColumn.put(column.name, pkValue); } if (pkColumnValue != null) { pkColumnValue.put(column.name, cVal); } rowId += (rowId.length() == 0 ? "" : " and ") + pkValue; } if (o == null || resultSet.wasNull()) { value = null; } if (o != null) { value = o; } } } v[vi] = value; return rowId; } }, null, context, limit); } } /** * True if row-limit is exceeded. */ private boolean isLimitExceeded = false; /** * Parent having row-limit exceeded. */ private RowBrowser parentWithExceededLimit() { RowBrowser parent = getParentBrowser(); while (parent != null) { if (parent.browserContentPane.isLimitExceeded) { return parent; } parent = parent.browserContentPane.getParentBrowser(); } return null; } public static class TableModelItem { public int blockNr; public Object value; public String toString() { return String.valueOf(value); } } /** * Updates the model of the {@link #rowsTable}. * * @param limit * row limit * @param limitExceeded */ private void updateTableModel(int limit, boolean limitExceeded) { pkColumns.clear(); List<Column> columns = rowIdSupport.getColumns(table); String[] columnNames = new String[columns.size()]; final Set<String> pkColumnNames = new HashSet<String>(); if (rowIdSupport.getPrimaryKey(table) != null) { for (Column pk : rowIdSupport.getPrimaryKey(table).getColumns()) { pkColumnNames.add(pk.name); } } for (int i = 0; i < columnNames.length; ++i) { columnNames[i] = columns.get(i).name; if (pkColumnNames.contains(columnNames[i])) { pkColumns.add(i); } } fkColumns.clear(); final Set<String> fkColumnNames = new HashSet<String>(); for (Association a: table.associations) { if (a.isInsertDestinationBeforeSource()) { Map<Column, Column> m = a.createSourceToDestinationKeyMapping(); for (Column fkColumn: m.keySet()) { fkColumnNames.add(fkColumn.name); } } } if (rowIdSupport.getPrimaryKey(table) != null) { for (Column pk : rowIdSupport.getPrimaryKey(table).getColumns()) { pkColumnNames.add(pk.name); } } for (int i = 0; i < columnNames.length; ++i) { if (fkColumnNames.contains(columnNames[i])) { fkColumns.add(i); } } DefaultTableModel dtm; singleRowDetailsView = null; int rn = 0; if (rows.size() != 1) { Map<String, Integer> columnNameMap = new HashMap<String, Integer>(); for (int i = 0; i < columns.size(); ++i) { columnNameMap.put(columnNames[i], i); } dtm = new DefaultTableModel(columnNames, 0) { @Override public boolean isCellEditable(int row, int column) { return false; } }; boolean stripHour[] = new boolean[columns.size()]; final String HOUR = " 00:00:00.0"; for (int i = 0; i < columns.size(); ++i) { stripHour[i] = true; for (Row row : rows) { Object value = row.values[i]; if (value == null) { continue; } if (!(value instanceof java.sql.Date) && !(value instanceof java.sql.Timestamp)) { stripHour[i] = false; break; } String asString = value.toString(); if (asString.endsWith(HOUR)) { continue; } stripHour[i] = false; break; } } for (Row row : rows) { Object[] rowData = new Object[columns.size()]; for (int i = 0; i < columns.size(); ++i) { rowData[i] = row.values[i]; if (rowData[i] == null) { rowData[i] = NULL; } else if (rowData[i] instanceof UnknownValue) { rowData[i] = UNKNOWN; } if (stripHour[i] && (rowData[i] instanceof java.sql.Date || rowData[i] instanceof java.sql.Timestamp)) { String asString = rowData[i].toString(); rowData[i] = asString.substring(0, asString.length() - HOUR.length()); } } if (tableContentViewFilter != null) { tableContentViewFilter.filter(rowData, columnNameMap); } for (int i = 0; i < columns.size(); ++i) { TableModelItem item = new TableModelItem(); item.blockNr = row.getBlockNr(); item.value = rowData[i]; rowData[i] = item; } dtm.addRow(rowData); if (++rn >= limit) { break; } } rowsTable.setModel(dtm); rowsTable.setRowHeight(initialRowHeight); TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(dtm) { @Override protected boolean useToString(int column) { return false; } @Override public Comparator<?> getComparator(int n) { List<? extends SortKey> sortKeys = super.getSortKeys(); final boolean desc = sortKeys.size() > 0 && sortKeys.get(0).getSortOrder() == SortOrder.DESCENDING; return new Comparator<Object>() { @SuppressWarnings("unchecked") @Override public int compare(Object o1, Object o2) { if (o1 instanceof TableModelItem && o2 instanceof TableModelItem) { int b1 = ((TableModelItem) o1).blockNr; int b2 = ((TableModelItem) o2).blockNr; if (b1 != b2) { return (b1 - b2) * (desc? -1 : 1); } } if (o1 instanceof TableModelItem) { o1 = ((TableModelItem) o1).value; } if (o2 instanceof TableModelItem) { o2 = ((TableModelItem) o2).value; } if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } if (o1.getClass().equals(o2.getClass())) { if (o1 instanceof Comparable<?>) { return ((Comparable<Object>) o1).compareTo(o2); } return 0; } return o1.getClass().getName().compareTo(o2.getClass().getName()); } }; } }; rowsTable.setRowSorter(sorter); } else { singleRowDetailsView = new DetailsView(Collections.singletonList(rows.get(0)), 1, dataModel, BrowserContentPane.this.table, 0, null, false, rowIdSupport) { @Override protected void onRowChanged(int row) { } }; dtm = new DefaultTableModel(new String[] { "Single Row Details" }, 0) { @Override public boolean isCellEditable(int row, int column) { return false; } }; for (Row row : rows) { dtm.addRow(new Object[] { row }); if (++rn >= limit) { break; } } rowsTable.setModel(dtm); rowsTable.setRowHeight(singleRowDetailsView.getPreferredSize().height); } adjustRowTableColumnsWidth(); rowsTable.setIntercellSpacing(new Dimension(0, 0)); int size = rows.size(); if (size > limit) { size = limit; } rowsCount.setText((limitExceeded ? " more than " : " ") + size + " row" + (size != 1 ? "s" : "")); RowBrowser theParentWithExceededLimit = parentWithExceededLimit(); rowsCount.setForeground(limitExceeded || theParentWithExceededLimit != null? Color.RED : new JLabel().getForeground()); if (theParentWithExceededLimit == null) { rowsCount.setToolTipText(null); } else { rowsCount.setToolTipText("potentially incomplete because " + theParentWithExceededLimit.internalFrame.getTitle() + " exceeded row limit"); } int nndr = noNonDistinctRows; if (noDistinctRows + noNonDistinctRows >= limit) { --nndr; } selectDistinctCheckBox.setVisible(nndr > 0); selectDistinctCheckBox.setText("select distinct (-" + nndr + " row" + (nndr == 1? "" : "s") + ")"); if (sqlBrowserContentPane != null) { sqlBrowserContentPane.detailsButton.setEnabled(!rows.isEmpty()); } isLimitExceeded = limitExceeded; } public void adjustRowTableColumnsWidth() { DefaultTableModel dtm = (DefaultTableModel) rowsTable.getModel(); for (int i = 0; i < rowsTable.getColumnCount(); i++) { TableColumn column = rowsTable.getColumnModel().getColumn(i); int width = ((int) (Desktop.BROWSERTABLE_DEFAULT_WIDTH * getLayoutFactor()) - 18) / rowsTable.getColumnCount(); Component comp = rowsTable.getDefaultRenderer(String.class).getTableCellRendererComponent(rowsTable, column.getHeaderValue(), false, false, 0, i); width = Math.max(width, comp.getPreferredSize().width); for (int line = 0; line < rowsTable.getRowCount(); ++line) { comp = rowsTable.getCellRenderer(line, i).getTableCellRendererComponent(rowsTable, dtm.getValueAt(line, i), false, false, line, i); width = Math.max(width, comp.getPreferredSize().width + (singleRowDetailsView == null ? 16 : 0)); if (singleRowDetailsView == null) { width = Math.min(width, 400); } if (line > 2000) { break; } } column.setPreferredWidth(width); } } /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" // <editor-fold defaultstate="collapsed" // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; wherePanel = new javax.swing.JPanel(); where = new javax.swing.JLabel(); jPanel7 = new javax.swing.JPanel(); loadButton = new javax.swing.JButton(); andCondition = new net.sf.jailer.ui.JComboBox(); onPanel = new javax.swing.JPanel(); on = new javax.swing.JLabel(); joinPanel = new javax.swing.JPanel(); join = new javax.swing.JLabel(); jLabel6 = new javax.swing.JLabel(); jPanel10 = new javax.swing.JPanel(); from = new javax.swing.JLabel(); jLabel5 = new javax.swing.JLabel(); pendingNonpendingPanel = new javax.swing.JPanel(); cardPanel = new javax.swing.JPanel(); jPanel2 = new javax.swing.JPanel(); jLabel2 = new javax.swing.JLabel(); cancelLoadButton = new javax.swing.JButton(); jPanel1 = new javax.swing.JPanel(); rowsTableScrollPane = new javax.swing.JScrollPane(); rowsTable = new javax.swing.JTable(); jPanel6 = new javax.swing.JPanel(); rowsCount = new javax.swing.JLabel(); selectDistinctCheckBox = new javax.swing.JCheckBox(); jPanel5 = new javax.swing.JPanel(); jLabel10 = new javax.swing.JLabel(); jPanel4 = new javax.swing.JPanel(); jLabel8 = new javax.swing.JLabel(); jPanel8 = new javax.swing.JPanel(); jLabel11 = new javax.swing.JLabel(); jLabel1 = new javax.swing.JLabel(); jLabel4 = new javax.swing.JLabel(); jLabel9 = new javax.swing.JLabel(); andLabel = new javax.swing.JLabel(); openEditorLabel = new javax.swing.JLabel(); jLabel3 = new javax.swing.JLabel(); fetchLabel = new javax.swing.JLabel(); jPanel3 = new javax.swing.JPanel(); limitBox = new net.sf.jailer.ui.JComboBox(); relatedRowsPanel = new javax.swing.JPanel(); relatedRowsLabel = new javax.swing.JLabel(); jPanel9 = new javax.swing.JPanel(); sqlPanel = new javax.swing.JPanel(); sqlLabel1 = new javax.swing.JLabel(); dropA = new javax.swing.JLabel(); dropB = new javax.swing.JLabel(); setLayout(new java.awt.GridBagLayout()); wherePanel.setMinimumSize(new java.awt.Dimension(66, 17)); wherePanel.setLayout(new java.awt.BorderLayout()); where.setFont(new java.awt.Font("DejaVu Sans", 0, 13)); // NOI18N where.setText("jLabel3"); wherePanel.add(where, java.awt.BorderLayout.CENTER); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 7; gridBagConstraints.gridwidth = 4; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST; gridBagConstraints.weightx = 1.0; add(wherePanel, gridBagConstraints); jPanel7.setLayout(new java.awt.GridBagLayout()); loadButton.setText(" Reload "); loadButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { loadButtonActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 1; jPanel7.add(loadButton, gridBagConstraints); andCondition.setEditable(true); andCondition.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; jPanel7.add(andCondition, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 8; gridBagConstraints.gridwidth = 5; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; add(jPanel7, gridBagConstraints); onPanel.setMinimumSize(new java.awt.Dimension(66, 17)); onPanel.setLayout(new java.awt.BorderLayout()); on.setFont(new java.awt.Font("DejaVu Sans", 0, 13)); // NOI18N on.setText("jLabel3"); onPanel.add(on, java.awt.BorderLayout.CENTER); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 6; gridBagConstraints.gridwidth = 4; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST; gridBagConstraints.weightx = 1.0; add(onPanel, gridBagConstraints); joinPanel.setMinimumSize(new java.awt.Dimension(66, 17)); joinPanel.setLayout(new java.awt.GridBagLayout()); join.setFont(new java.awt.Font("DejaVu Sans", 0, 13)); // NOI18N join.setText("jLabel3"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; joinPanel.add(join, gridBagConstraints); jLabel6.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N jLabel6.setText(" as B "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; joinPanel.add(jLabel6, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 5; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST; gridBagConstraints.weightx = 1.0; add(joinPanel, gridBagConstraints); jPanel10.setMinimumSize(new java.awt.Dimension(66, 17)); jPanel10.setLayout(new java.awt.GridBagLayout()); from.setFont(new java.awt.Font("DejaVu Sans", 0, 13)); // NOI18N from.setText("jLabel3"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; jPanel10.add(from, gridBagConstraints); jLabel5.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N jLabel5.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); jLabel5.setText(" as A"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; jPanel10.add(jLabel5, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 4; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; add(jPanel10, gridBagConstraints); pendingNonpendingPanel.setLayout(new java.awt.CardLayout()); cardPanel.setLayout(new java.awt.CardLayout()); jPanel2.setLayout(new java.awt.GridBagLayout()); jLabel2.setFont(new java.awt.Font("DejaVu Sans", 1, 14)); // NOI18N jLabel2.setForeground(new java.awt.Color(141, 16, 16)); jLabel2.setText("loading..."); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(8, 8, 8, 0); jPanel2.add(jLabel2, gridBagConstraints); cancelLoadButton.setText("Cancel"); cancelLoadButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { cancelLoadButtonActionPerformed(evt); } }); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; jPanel2.add(cancelLoadButton, gridBagConstraints); cardPanel.add(jPanel2, "loading"); jPanel1.setLayout(new java.awt.GridBagLayout()); rowsTableScrollPane.setWheelScrollingEnabled(false); rowsTable.setAutoCreateRowSorter(true); rowsTable.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { {null, null, null, null}, {null, null, null, null}, {null, null, null, null}, {null, null, null, null} }, new String [] { "Title 1", "Title 2", "Title 3", "Title 4" } )); rowsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_OFF); rowsTableScrollPane.setViewportView(rowsTable); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; jPanel1.add(rowsTableScrollPane, gridBagConstraints); jPanel6.setLayout(new java.awt.GridBagLayout()); rowsCount.setText("jLabel3"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.weightx = 1.0; jPanel6.add(rowsCount, gridBagConstraints); selectDistinctCheckBox.setSelected(true); selectDistinctCheckBox.setText("select distinct (-100 rows)"); selectDistinctCheckBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { selectDistinctCheckBoxActionPerformed(evt); } }); jPanel6.add(selectDistinctCheckBox, new java.awt.GridBagConstraints()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTH; jPanel1.add(jPanel6, gridBagConstraints); cardPanel.add(jPanel1, "table"); jPanel5.setLayout(new java.awt.GridBagLayout()); jLabel10.setFont(new java.awt.Font("DejaVu Sans", 1, 14)); // NOI18N jLabel10.setForeground(new java.awt.Color(141, 16, 16)); jLabel10.setText("Error"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(8, 8, 8, 0); jPanel5.add(jLabel10, gridBagConstraints); cardPanel.add(jPanel5, "error"); jPanel4.setLayout(new java.awt.GridBagLayout()); jLabel8.setFont(new java.awt.Font("DejaVu Sans", 1, 14)); // NOI18N jLabel8.setForeground(new java.awt.Color(141, 16, 16)); jLabel8.setText("Cancelled"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(8, 8, 8, 0); jPanel4.add(jLabel8, gridBagConstraints); cardPanel.add(jPanel4, "cancelled"); pendingNonpendingPanel.add(cardPanel, "nonpending"); jPanel8.setLayout(new java.awt.GridBagLayout()); jLabel11.setFont(new java.awt.Font("DejaVu Sans", 1, 14)); // NOI18N jLabel11.setForeground(new java.awt.Color(141, 16, 16)); jLabel11.setText("pending..."); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(8, 8, 8, 0); jPanel8.add(jLabel11, gridBagConstraints); pendingNonpendingPanel.add(jPanel8, "pending"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 12; gridBagConstraints.gridwidth = 7; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; add(pendingNonpendingPanel, gridBagConstraints); jLabel1.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N jLabel1.setText(" Join "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 5; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; add(jLabel1, gridBagConstraints); jLabel4.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N jLabel4.setText(" On "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 6; gridBagConstraints.gridwidth = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; add(jLabel4, gridBagConstraints); jLabel9.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N jLabel9.setText(" Where "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 7; gridBagConstraints.gridwidth = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; add(jLabel9, gridBagConstraints); andLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N andLabel.setText(" And "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 8; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; add(andLabel, gridBagConstraints); openEditorLabel.setText(" And "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 8; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; add(openEditorLabel, gridBagConstraints); jLabel3.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N jLabel3.setText(" From "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 4; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; add(jLabel3, gridBagConstraints); fetchLabel.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N fetchLabel.setText(" Limit "); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 9; gridBagConstraints.gridwidth = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0); add(fetchLabel, gridBagConstraints); jPanel3.setLayout(new javax.swing.BoxLayout(jPanel3, javax.swing.BoxLayout.LINE_AXIS)); limitBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" })); limitBox.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { limitBoxItemStateChanged(evt); } }); jPanel3.add(limitBox); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 4; gridBagConstraints.gridy = 9; gridBagConstraints.gridwidth = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 0); add(jPanel3, gridBagConstraints); relatedRowsPanel.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); relatedRowsPanel.setLayout(new javax.swing.BoxLayout(relatedRowsPanel, javax.swing.BoxLayout.LINE_AXIS)); relatedRowsLabel.setText(" Related Rows "); relatedRowsPanel.add(relatedRowsLabel); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 5; gridBagConstraints.gridy = 9; gridBagConstraints.gridwidth = 4; gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 2); add(relatedRowsPanel, gridBagConstraints); jPanel9.setLayout(new java.awt.GridBagLayout()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 5; gridBagConstraints.gridy = 5; gridBagConstraints.gridwidth = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(0, 8, 0, 0); add(jPanel9, gridBagConstraints); sqlPanel.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); sqlPanel.setLayout(new javax.swing.BoxLayout(sqlPanel, javax.swing.BoxLayout.LINE_AXIS)); sqlLabel1.setText(" Menu "); sqlPanel.add(sqlLabel1); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 8; gridBagConstraints.gridy = 4; gridBagConstraints.gridheight = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 2); add(sqlPanel, gridBagConstraints); dropA.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N dropA.setText("drop"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 4; add(dropA, gridBagConstraints); dropB.setFont(new java.awt.Font("DejaVu Sans", 1, 13)); // NOI18N dropB.setText("drop"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 5; add(dropB, gridBagConstraints); }// </editor-fold>//GEN-END:initComponents private void cancelLoadButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelLoadButtonActionPerformed cancelLoadJob(false); updateMode("cancelled"); }//GEN-LAST:event_cancelLoadButtonActionPerformed private void selectDistinctCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectDistinctCheckBoxActionPerformed reloadRows(); }//GEN-LAST:event_selectDistinctCheckBoxActionPerformed private void loadButtonActionPerformed(java.awt.event.ActionEvent evt) {// GEN-FIRST:event_loadButtonActionPerformed if (System.currentTimeMillis() - lastReloadTS > 200) { reloadRows(); } }// GEN-LAST:event_loadButtonActionPerformed private void limitBoxItemStateChanged(java.awt.event.ItemEvent evt) {// GEN-FIRST:event_limitBoxItemStateChanged reloadRows(); }// GEN-LAST:event_limitBoxItemStateChanged private void openQueryBuilder() { QueryBuilderDialog.Relationship root = createQBRelations(true); root.selectColumns = true; getQueryBuilderDialog().buildQuery(table, root, dataModel); } // Variables declaration - do not modify//GEN-BEGIN:variables javax.swing.JComboBox andCondition; private javax.swing.JLabel andLabel; private javax.swing.JButton cancelLoadButton; private javax.swing.JPanel cardPanel; private javax.swing.JLabel dropA; private javax.swing.JLabel dropB; private javax.swing.JLabel fetchLabel; private javax.swing.JLabel from; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel10; private javax.swing.JLabel jLabel11; private javax.swing.JLabel jLabel2; private javax.swing.JLabel jLabel3; private javax.swing.JLabel jLabel4; private javax.swing.JLabel jLabel5; private javax.swing.JLabel jLabel6; private javax.swing.JLabel jLabel8; private javax.swing.JLabel jLabel9; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel10; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel3; private javax.swing.JPanel jPanel4; private javax.swing.JPanel jPanel5; private javax.swing.JPanel jPanel6; private javax.swing.JPanel jPanel7; private javax.swing.JPanel jPanel8; private javax.swing.JPanel jPanel9; private javax.swing.JLabel join; private javax.swing.JPanel joinPanel; javax.swing.JComboBox limitBox; private javax.swing.JButton loadButton; private javax.swing.JLabel on; private javax.swing.JPanel onPanel; private javax.swing.JLabel openEditorLabel; private javax.swing.JPanel pendingNonpendingPanel; private javax.swing.JLabel relatedRowsLabel; private javax.swing.JPanel relatedRowsPanel; private javax.swing.JLabel rowsCount; public javax.swing.JTable rowsTable; javax.swing.JScrollPane rowsTableScrollPane; javax.swing.JCheckBox selectDistinctCheckBox; private javax.swing.JLabel sqlLabel1; private javax.swing.JPanel sqlPanel; private javax.swing.JLabel where; private javax.swing.JPanel wherePanel; // End of variables declaration//GEN-END:variables private ConditionEditor andConditionEditor; private Icon conditionEditorIcon; private Icon conditionEditorSelectedIcon; { String dir = "/net/sf/jailer/ui/resource"; // load images try { conditionEditorIcon = new ImageIcon(getClass().getResource(dir + "/edit.png")); } catch (Exception e) { e.printStackTrace(); } try { conditionEditorSelectedIcon = new ImageIcon(getClass().getResource(dir + "/edit_s.png")); } catch (Exception e) { e.printStackTrace(); } } /** * Cancels current load job. * @param propagate */ public void cancelLoadJob(boolean propagate) { LoadJob cLoadJob; synchronized (this) { cLoadJob = currentLoadJob; } if (cLoadJob != null) { cLoadJob.cancel(); } if (propagate) { for (RowBrowser child: getChildBrowsers()) { child.browserContentPane.cancelLoadJob(propagate); } } } private void updateMode(String mode) { ((CardLayout) cardPanel.getLayout()).show(cardPanel, mode); // relatedRowsPanel.setVisible("table".equals(mode) && rows.size() >= 1); } /** * Opens a drop-down box which allows the user to select columns for restriction definitions. */ private void openColumnDropDownBox(JLabel label, String alias, Table table) { JPopupMenu popup = new JScrollPopupMenu(); List<String> columns = new ArrayList<String>(); for (Column c: table.getColumns()) { columns.add(alias + "." + c.name); } for (final String c: columns) { if (c.equals("")) { popup.add(new JSeparator()); continue; } JMenuItem m = new JMenuItem(c); m.addActionListener(new ActionListener () { public void actionPerformed(ActionEvent e) { if (andCondition.isEnabled()) { if (andCondition.isEditable()) { if (andCondition.getEditor() != null && (andCondition.getEditor().getEditorComponent() instanceof JTextField)) { JTextField f = ((JTextField) andCondition.getEditor().getEditorComponent()); int pos = f.getCaretPosition(); String current = f.getText(); if (pos < 0 || pos >= current.length()) { setAndCondition(current + c, false); } else { setAndCondition(current.substring(0, pos) + c + current.substring(pos), false); f.setCaretPosition(pos + c.length()); } } andCondition.grabFocus(); } } } }); popup.add(m); } UIUtil.fit(popup); popup.show(label, 0, label.getHeight()); } /** * Creates new row. Fills in foreign key. * * @param parentrow row holding the primary key * @param table the table of the new row * @return new row of table */ private Row createNewRow(Row parentrow, Table table) { Row row = new Row(null, null, new Object[table.getColumns().size()]); if (parentrow != null && association != null) { Map<Column, Column> sToDMap = association.createSourceToDestinationKeyMapping(); for (Map.Entry<Column, Column> e: sToDMap.entrySet()) { int iS = -1; for (int i = 0; i < association.source.getColumns().size(); ++i) { if (e.getKey() == association.source.getColumns().get(i)) { iS = i; break; } } int iD = -1; for (int i = 0; i < association.destination.getColumns().size(); ++i) { if (e.getValue() == association.destination.getColumns().get(i)) { iD = i; break; } } if (iS >= 0 && iD >= 0) { row.values[iD] = parentrow.values[iS]; } } } return row; } protected abstract void navigateTo(Association association, int rowIndex, Row row); protected abstract void onContentChange(List<Row> rows, boolean reloadChildren); protected abstract void onRedraw(); protected abstract void onHide(); protected abstract void beforeReload(); protected abstract QueryBuilderDialog.Relationship createQBRelations(boolean withParents); protected abstract List<QueryBuilderDialog.Relationship> createQBChildrenRelations(RowBrowser tabu, boolean all); protected abstract void addRowToRowLink(Row pRow, Row exRow); protected abstract JFrame getOwner(); protected abstract void findClosure(Row row); protected abstract void findClosure(Row row, Set<Pair<BrowserContentPane, Row>> closure, boolean forward); protected abstract QueryBuilderDialog getQueryBuilderDialog(); protected abstract QueryBuilderPathSelector getQueryBuilderPathSelector(); protected abstract void openSchemaMappingDialog(); protected abstract void openSchemaAnalyzer(); protected abstract DbConnectionDialog getDbConnectionDialog(); protected abstract double getLayoutFactor(); protected abstract List<RowBrowser> getChildBrowsers(); protected abstract RowBrowser getParentBrowser(); protected abstract List<RowBrowser> getTableBrowser(); protected abstract void unhide(); protected abstract void close(); protected abstract void showInNewWindow(); protected abstract void appendLayout(); protected abstract void adjustClosure(BrowserContentPane tabu); protected abstract void reloadDataModel() throws Exception; public interface RunnableWithPriority extends Runnable { int getPriority(); }; protected abstract PriorityBlockingQueue<RunnableWithPriority> getRunnableQueue(); /** * Collect layout of tables in a extraction model. * * @param positions to put positions into */ protected abstract void collectPositions(Map<String, Map<String, double[]>> positions); private void openDetails(final int x, final int y) { JDialog d = new JDialog(getOwner(), (table instanceof SqlStatementTable)? "" : dataModel.getDisplayName(table), true); d.getContentPane().add(new DetailsView(rows, rowsTable.getRowCount(), dataModel, table, 0, rowsTable.getRowSorter(), true, rowIdSupport) { @Override protected void onRowChanged(int row) { setCurrentRowSelection(row); } }); d.pack(); d.setLocation(x, y); d.setSize(400, d.getHeight() + 20); UIUtil.fit(d); d.setVisible(true); setCurrentRowSelection(-1); } private void updateWhereField() { if (association != null) { where.setText(parentRow == null ? (parentRows != null && parentRows.size() > 0? parentRows.get(0).rowId + (parentRows.size() > 1? " or ..." : "") : "") : parentRow.rowId); where.setToolTipText(where.getText()); } } public void convertToRoot() { association = null; parentRow = null; parentRows = null; currentClosureRowIDs.clear(); adjustGui(); reloadRows(); } private static TableContentViewFilter tableContentViewFilter = TableContentViewFilter.create(); private Icon dropDownIcon; { String dir = "/net/sf/jailer/ui/resource"; // load images try { dropDownIcon = new ImageIcon(getClass().getResource(dir + "/dropdown.png")); } catch (Exception e) { e.printStackTrace(); } } }