/*
* 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.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedList;
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 java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.DefaultDesktopManager;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.InternalFrameListener;
import net.sf.jailer.ExecutionContext;
import net.sf.jailer.database.Session;
import net.sf.jailer.datamodel.Association;
import net.sf.jailer.datamodel.DataModel;
import net.sf.jailer.datamodel.Table;
import net.sf.jailer.ui.CommandLineInstance;
import net.sf.jailer.ui.ConditionEditor;
import net.sf.jailer.ui.DbConnectionDialog;
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.BrowserContentPane.RunnableWithPriority;
import net.sf.jailer.ui.databrowser.TreeLayoutOptimizer.Node;
import net.sf.jailer.util.CancellationException;
import net.sf.jailer.util.CsvFile;
import net.sf.jailer.util.CsvFile.Line;
import net.sf.jailer.util.Pair;
import net.sf.jailer.util.SqlUtil;
import prefuse.util.GraphicsLib;
/**
* Desktop holding row-browsers as {@link JInternalFrame}s.
*
* @author Ralf Wisser
*/
@SuppressWarnings("serial")
public abstract class Desktop extends JDesktopPane {
/**
* The {@link DataModel}.
*/
private final Reference<DataModel> datamodel;
/**
* Icon for the row-browser frames.
*/
private final Icon jailerIcon;
/**
* Default width of a row-browser frame.
*/
public static final int BROWSERTABLE_DEFAULT_WIDTH = 476;
private final int BROWSERTABLE_DEFAULT_MIN = 0, BROWSERTABLE_DEFAULT_HEIGHT = 460, BROWSERTABLE_DEFAULT_DISTANCE = 64;
/**
* <code>true</code> while the desktop is visible.
*/
private boolean running;
/**
* <code>false</code> if links must not be rendered (if a frame is
* maximized).
*/
private boolean renderLinks;
/**
* Schema mapping.
*/
public final Map<String, String> schemaMapping = new TreeMap<String, String>();
/**
* DB session.
*/
public Session session;
DbConnectionDialog dbConnectionDialog;
/**
* The execution context.
*/
private final ExecutionContext executionContext;
private Set<Pair<BrowserContentPane, Row>> currentClosure = new HashSet<Pair<BrowserContentPane, Row>>();
private Set<Pair<BrowserContentPane, String>> currentClosureRowIDs = new HashSet<Pair<BrowserContentPane, String>>();
private final QueryBuilderDialog queryBuilderDialog;
private final QueryBuilderPathSelector queryBuilderPathSelector;
private Method getPreciseWheelRotation;
/**
* Constructor.
*
* @param datamodel
* the {@link DataModel}
* @param jailerIcon
* icon for the frames
* @param session
* DB-session
*/
public Desktop(Reference<DataModel> datamodel, Icon jailerIcon, Session session, DataBrowser parentFrame, DbConnectionDialog dbConnectionDialog) {
this.executionContext = CommandLineInstance.getExecutionContext();
this.parentFrame = parentFrame;
this.datamodel = datamodel;
this.jailerIcon = jailerIcon;
this.queryBuilderDialog = new QueryBuilderDialog(parentFrame);
this.queryBuilderPathSelector = new QueryBuilderPathSelector(parentFrame, true);
this.dbConnectionDialog = dbConnectionDialog;
this.queryBuilderDialog.sqlEditButton.setVisible(true);
this.queryBuilderDialog.sqlEditButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
addTableBrowser(null, 0, null, null, queryBuilderDialog.getSQL(), null, null, true);
queryBuilderDialog.setVisible(false);
}
});
try {
getPreciseWheelRotation = MouseWheelEvent.class.getMethod("getPreciseWheelRotation");
} catch (Exception exc) {
// ignored
}
try {
this.session = session;
setAutoscrolls(true);
manager = new MDIDesktopManager(this);
setDesktopManager(manager);
synchronized (this) {
running = true;
}
Thread updateUIThread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
synchronized (Desktop.this) {
if (!running) {
return;
}
}
try {
Thread.sleep(300);
SwingUtilities.invokeAndWait(new Runnable() {
@Override
public void run() {
boolean cl = calculateLinks();
if (cl) {
repaintDesktop();
}
}
});
} catch (InterruptedException e) {
// ignore
} catch (InvocationTargetException e) {
// ignore
}
}
}
});
updateUIThread.setDaemon(true);
updateUIThread.start();
} catch (Exception e) {
UIUtil.showException(null, "Error", e, session);
}
desktops.add(this);
updateMenu();
}
public class RowToRowLink {
/**
* The rows.
*/
public Row parentRow, childRow;
/**
* Index of parent row in the parent's row browser.
*/
public int parentRowIndex = -1;
/**
* Index of child row.
*/
public int childRowIndex = -1;
/**
* Coordinates of the link render.
*/
public int x1 = -1, y1, x2, y2;
/**
* The link's color.
*/
public Color color;
}
/**
* Renders a set of {@link Row}s.
*/
public class RowBrowser {
/**
* Frame holding a {@link BrowserContentPane}.
*/
public JInternalFrame internalFrame;
/**
* UI for row-browsing.
*/
public BrowserContentPane browserContentPane;
/**
* Parent browser.
*/
public RowBrowser parent;
/**
* Association with parent.
*/
public Association association;
/**
* Index of parent row in the parent's row browser.
*/
public int rowIndex;
/**
* Coordinates of the link render.
*/
public int x1, y1, x2, y2;
/**
* The link's color.
*/
public Color color;
/**
* Row-to-row links.
*/
public List<RowToRowLink> rowToRowLinks = new ArrayList<RowToRowLink>();
public void convertToRoot() {
rowIndex = -1;
association = null;
parent = null;
browserContentPane.convertToRoot();
}
/**
* Is this RowBrowser hidden?
*/
private boolean hidden;
/**
* Hides/unhides RowBrowser.
*/
public void setHidden(boolean hidden) {
if (hidden == this.hidden) {
return;
}
rbSourceToLinks = null;
if (hidden) {
internalFrame.setVisible(false);
} else {
internalFrame.setVisible(true);
Rectangle r = layout(rowIndex < 0, parent, association, browserContentPane, new ArrayList<RowBrowser>(), 0, -1);
internalFrame.setBounds(r);
scrollRectToVisible(internalFrame.getBounds());
try {
internalFrame.setSelected(true);
} catch (PropertyVetoException e) {
// ignore
}
internalFrame.grabFocus();
}
this.hidden = hidden;
checkDesktopSize();
updateMenu();
}
/**
* Is this RowBrowser hidden?
*/
public boolean isHidden() {
return hidden;
}
};
/**
* All row-browsers.
*/
private List<RowBrowser> tableBrowsers = new ArrayList<RowBrowser>();
/**
* Opens a new row-browser.
*
* @param parent
* parent browser
* @param parentRowIndex
* index of parent row in the parent's row browser, -1 for all
* rows
* @param table
* to read rows from. Open SQL browser if table is
* <code>null</code>.
* @param association
* to navigate, or <code>null</code>
* @param condition
* @param selectDistinct
* @param limit
* @return new row-browser
*/
public synchronized RowBrowser addTableBrowser(final RowBrowser parent, int parentRowIndex, final Table table, final Association association,
String condition, Integer limit, Boolean selectDistinct, boolean reload) {
Set<String> titles = new HashSet<String>();
for (RowBrowser rb : tableBrowsers) {
titles.add(rb.internalFrame.getTitle());
}
demaximize();
String title = null;
if (table != null) {
title = datamodel.get().getDisplayName(table);
if (titles.contains(title)) {
for (int i = 2;; ++i) {
String titelPlusI = title + " (" + i + ")";
if (!titles.contains(titelPlusI)) {
title = titelPlusI;
break;
}
}
}
}
final RowBrowser tableBrowser = new RowBrowser();
final JInternalFrame jInternalFrame = new JInternalFrame(table == null ? "SQL" : title);
jInternalFrame.setClosable(true);
jInternalFrame.setIconifiable(true);
jInternalFrame.setMaximizable(true);
jInternalFrame.setVisible(true);
jInternalFrame.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
onMouseWheelMoved(evt);
onMouseWheelMoved(evt, parentFrame.getDesktopScrollPane());
}
});
javax.swing.GroupLayout jInternalFrame1Layout = new javax.swing.GroupLayout(jInternalFrame.getContentPane());
jInternalFrame.getContentPane().setLayout(jInternalFrame1Layout);
jInternalFrame1Layout.setHorizontalGroup(jInternalFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 162,
Short.MAX_VALUE));
jInternalFrame1Layout.setVerticalGroup(jInternalFrame1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING).addGap(0, 102,
Short.MAX_VALUE));
jInternalFrame.setResizable(true);
if (jailerIcon != null) {
jInternalFrame.setFrameIcon(jailerIcon);
}
add(jInternalFrame, javax.swing.JLayeredPane.DEFAULT_LAYER);
jInternalFrame.addPropertyChangeListener(JInternalFrame.IS_MAXIMUM_PROPERTY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
manager.resizeDesktop();
}
});
jInternalFrame.addPropertyChangeListener(JInternalFrame.IS_ICON_PROPERTY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (jInternalFrame.isIcon()) {
demaximize();
tableBrowser.setHidden(true);
try {
jInternalFrame.setIcon(false);
} catch (PropertyVetoException e) {
// ignore
}
}
}
});
jInternalFrame.addPropertyChangeListener(JInternalFrame.IS_SELECTED_PROPERTY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (Boolean.TRUE.equals(evt.getNewValue())) {
updateMenu();
}
}
});
jInternalFrame.addComponentListener(new ComponentListener() {
@Override
public void componentShown(ComponentEvent e) {
repaintDesktop();
}
@Override
public void componentResized(ComponentEvent e) {
repaintDesktop();
}
@Override
public void componentMoved(ComponentEvent e) {
repaintDesktop();
}
@Override
public void componentHidden(ComponentEvent e) {
repaintDesktop();
}
});
final BrowserContentPane browserContentPane = new BrowserContentPane(datamodel.get(), table, condition, session, parent == null || parentRowIndex < 0 ? null
: parent.browserContentPane.rows.get(parentRowIndex), parent == null || parentRowIndex >= 0 ? null : parent.browserContentPane.rows,
association, parentFrame, currentClosure, currentClosureRowIDs, limit, selectDistinct, reload, executionContext) {
@Override
protected void reloadDataModel() throws Exception {
Desktop.this.reloadDataModel(schemaMapping);
}
@Override
protected QueryBuilderDialog getQueryBuilderDialog() {
return queryBuilderDialog;
}
@Override
protected QueryBuilderPathSelector getQueryBuilderPathSelector() {
return queryBuilderPathSelector;
}
@Override
protected void navigateTo(Association association, int rowIndex, Row row) {
addTableBrowser(tableBrowser, rowIndex, association.destination, association, "", null, null, true);
}
@Override
protected void onContentChange(List<Row> rows, boolean reloadChildren) {
updateChildren(tableBrowser, rows);
for (RowBrowser rb : tableBrowsers) {
if (rb.parent == tableBrowser) {
updateChildren(rb, rb.browserContentPane.rows);
if (reloadChildren && rb.browserContentPane.parentRow == null) {
rb.browserContentPane.reloadRows();
}
}
}
}
@Override
protected void onRedraw() {
repaintDesktop();
}
@Override
protected JFrame getOwner() {
return parentFrame;
}
@Override
protected void addRowToRowLink(Row parentRow, Row childRow) {
synchronized (Desktop.this) {
RowToRowLink rowToRowLink = new RowToRowLink();
rowToRowLink.parentRow = parentRow;
rowToRowLink.childRow = childRow;
rowToRowLink.color = getAssociationColor(association);
tableBrowser.rowToRowLinks.add(rowToRowLink);
}
}
@Override
protected void beforeReload() {
synchronized (Desktop.this) {
tableBrowser.rowToRowLinks.clear();
}
}
@Override
protected void findClosure(Row row) {
Set<Pair<BrowserContentPane, Row>> rows = new HashSet<Pair<BrowserContentPane, Row>>();
findClosure(row, rows, false);
currentClosure.addAll(rows);
rows = new HashSet<Pair<BrowserContentPane, Row>>();
findClosure(row, rows, true);
currentClosure.addAll(rows);
}
@Override
protected void findClosure(Row row, Set<Pair<BrowserContentPane, Row>> closure, boolean forward) {
Pair<BrowserContentPane, Row> thisRow = new Pair<BrowserContentPane, Row>(this, row);
if (!closure.contains(thisRow)) {
closure.add(thisRow);
if (forward) {
for (RowBrowser child : tableBrowsers) {
if (child.parent == tableBrowser) {
if (child.browserContentPane.parentRow != null) {
if (row.rowId.equals(child.browserContentPane.parentRow.rowId)) {
for (Row r : child.browserContentPane.rows) {
child.browserContentPane.findClosure(r, closure, forward);
}
}
}
for (RowToRowLink rowToRowLink : child.rowToRowLinks) {
if (row.rowId.equals(rowToRowLink.parentRow.rowId)) {
child.browserContentPane.findClosure(rowToRowLink.childRow, closure, forward);
}
}
}
}
} else {
if (tableBrowser.parent != null) {
if (tableBrowser.browserContentPane.parentRow != null) {
tableBrowser.parent.browserContentPane.findClosure(tableBrowser.browserContentPane.parentRow, closure, forward);
}
for (RowToRowLink rowToRowLink : tableBrowser.rowToRowLinks) {
if (row.rowId.equals(rowToRowLink.childRow.rowId)) {
tableBrowser.parent.browserContentPane.findClosure(rowToRowLink.parentRow, closure, forward);
}
}
}
}
}
}
private void createAnchorSQL(RowBrowser rb, StringBuilder rowIds, boolean indent) {
boolean f = true;
for (Row row : rb.browserContentPane.rows) {
if (!f) {
rowIds.append(indent ? " or\n " : " or\n");
}
f = false;
rowIds.append(SqlUtil.replaceAliases(row.rowId, "A", "A"));
}
rowIds.append("");
}
@Override
protected QueryBuilderDialog.Relationship createQBRelations(boolean withParents) {
QueryBuilderDialog.Relationship root = new QueryBuilderDialog.Relationship();
root.whereClause = ConditionEditor.toMultiLine(getAndConditionText().trim()).replaceAll("(\r|\n)+", " ");
if (root.whereClause.length() == 0) {
root.whereClause = null;
}
StringBuilder rowIds = new StringBuilder("");
createAnchorSQL(tableBrowser, rowIds, withParents);
root.anchorWhereClause = rowIds.length() == 0 ? null : rowIds.toString();
root.children.addAll(createQBChildrenRelations(null, !withParents));
Association a = association;
QueryBuilderDialog.Relationship r = root;
RowBrowser childRB = tableBrowser;
for (RowBrowser rb = tableBrowser.parent; rb != null && a != null; rb = rb.parent) {
if (!withParents) {
root.needsAnchor = true;
break;
}
QueryBuilderDialog.Relationship child = new QueryBuilderDialog.Relationship();
child.children.addAll(rb.browserContentPane.createQBChildrenRelations(childRB, false));
child.parent = r;
r.children.add(0, child);
child.whereClause = ConditionEditor.toMultiLine(rb.browserContentPane.getAndConditionText().trim()).replaceAll("(\r|\n)+", " ");
if (child.whereClause.length() == 0) {
child.whereClause = null;
}
child.association = a.reversalAssociation;
r.anchor = child.association;
a = rb.association;
rowIds = new StringBuilder("");
createAnchorSQL(rb, rowIds, true);
child.anchorWhereClause = rowIds.length() == 0 ? null : rowIds.toString();
r.originalParent = child;
if (childRB.rowIndex >= 0
&& !(childRB.rowIndex == 0 && childRB.parent != null && childRB.parent.browserContentPane != null
&& childRB.parent.browserContentPane.rows != null && childRB.parent.browserContentPane.rows.size() == 1)) {
String w = childRB.browserContentPane.parentRow.rowId;
child.whereClause = null;
r.whereClause = w; // (r.whereClause == null ||
// r.whereClause.length() == 0)? w :
// "(" + w + ") and (" +
// r.whereClause + ")";
break;
}
r = child;
childRB = rb;
}
return root;
}
@Override
protected List<Relationship> createQBChildrenRelations(RowBrowser tabu, boolean all) {
List<QueryBuilderDialog.Relationship> result = new ArrayList<QueryBuilderDialog.Relationship>();
for (RowBrowser rb : tableBrowsers) {
if (rb.parent == tableBrowser && rb != tabu) {
boolean singleRowParent = rb.rowIndex >= 0
&& !(rb.rowIndex == 0 && rb.parent != null && rb.parent.browserContentPane != null && rb.parent.browserContentPane.rows != null && rb.parent.browserContentPane.rows
.size() == 1);
if (true) { // all || !singleRowParent) {
QueryBuilderDialog.Relationship child = new QueryBuilderDialog.Relationship();
child.whereClause = ConditionEditor.toMultiLine(rb.browserContentPane.getAndConditionText().trim()).replaceAll("(\r|\n)+", " ");
child.joinOperator = QueryBuilderDialog.JoinOperator.LeftJoin;
if (child.whereClause.length() == 0) {
child.whereClause = null;
}
if (singleRowParent) {
String andIsParent = rb.browserContentPane.parentRow.rowId;
if (child.whereClause == null) {
child.whereClause = andIsParent;
} else {
child.whereClause = "(" + child.whereClause + ") and (" + andIsParent + ")";
}
}
child.association = rb.association;
if (child.association != null) {
child.children.addAll(rb.browserContentPane.createQBChildrenRelations(tabu, all));
result.add(child);
}
}
}
}
return result;
}
@Override
protected void openSchemaMappingDialog() {
Desktop.this.openSchemaMappingDialog(false);
}
@Override
protected void openSchemaAnalyzer() {
Desktop.this.openSchemaAnalyzer();
}
@Override
protected DbConnectionDialog getDbConnectionDialog() {
return dbConnectionDialog;
}
@Override
protected double getLayoutFactor() {
return layoutMode.factor;
}
@Override
protected List<RowBrowser> getChildBrowsers() {
return Desktop.this.getChildBrowsers(tableBrowser, false);
}
@Override
protected RowBrowser getParentBrowser() {
return tableBrowser.parent;
}
@Override
protected List<RowBrowser> getTableBrowser() {
return new ArrayList<Desktop.RowBrowser>(Desktop.this.tableBrowsers);
}
@Override
protected void onHide() {
demaximize();
tableBrowser.setHidden(true);
}
@Override
protected void unhide() {
tableBrowser.setHidden(false);
}
@Override
protected void adjustClosure(BrowserContentPane tabu) {
Desktop.this.adjustClosure(tabu);
}
@Override
protected void close() {
closeAll(Collections.singleton(tableBrowser));
}
@Override
protected void showInNewWindow() {
Desktop.this.showInNewWindow(tableBrowser);
}
@Override
protected void appendLayout() {
Desktop.this.restoreSession(tableBrowser);
}
@Override
protected PriorityBlockingQueue<RunnableWithPriority> getRunnableQueue() {
return runnableQueue;
}
@Override
protected void collectPositions(Map<String, Map<String, double[]>> positions) {
Desktop.this.collectPositions(tableBrowser, positions);
}
};
Rectangle r = layout(parentRowIndex < 0, parent, association, browserContentPane, new ArrayList<RowBrowser>(), 0, -1);
browserContentPane.rowsTableScrollPane.addMouseWheelListener(new java.awt.event.MouseWheelListener() {
public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) {
onMouseWheelMoved(evt);
onMouseWheelMoved(evt, browserContentPane.rowsTableScrollPane);
}
});
jInternalFrame.setBounds(r);
tableBrowser.internalFrame = jInternalFrame;
tableBrowser.browserContentPane = browserContentPane;
tableBrowser.rowIndex = parentRowIndex;
tableBrowser.parent = parent;
tableBrowser.association = association;
if (association != null) {
tableBrowser.color = getAssociationColor(association);
}
tableBrowsers.add(tableBrowser);
initIFrame(jInternalFrame, browserContentPane);
jInternalFrame.addInternalFrameListener(new InternalFrameListener() {
@Override
public void internalFrameOpened(InternalFrameEvent e) {
}
@Override
public void internalFrameIconified(InternalFrameEvent e) {
repaintDesktop();
}
@Override
public void internalFrameDeiconified(InternalFrameEvent e) {
repaintDesktop();
}
@Override
public void internalFrameDeactivated(InternalFrameEvent e) {
}
@Override
public void internalFrameClosing(InternalFrameEvent e) {
}
@Override
public void internalFrameClosed(InternalFrameEvent e) {
close(tableBrowser, true);
}
@Override
public void internalFrameActivated(InternalFrameEvent e) {
}
});
checkDesktopSize();
this.scrollToCenter(jInternalFrame);
try {
jInternalFrame.setSelected(true);
} catch (PropertyVetoException e1) {
// ignore
}
if (browserContentPane.sqlBrowserContentPane != null) {
if (this.layoutMode == LayoutMode.THUMBNAIL || this.layoutMode == LayoutMode.TINY) {
try {
jInternalFrame.setMaximum(true);
} catch (PropertyVetoException e1) {
// ignore
}
}
browserContentPane.sqlBrowserContentPane.sqlEditorPane.grabFocus();
} else {
browserContentPane.andCondition.grabFocus();
}
updateMenu();
return tableBrowser;
}
/**
* Demaximizes all internal frames.
*/
private void demaximize() {
for (RowBrowser rb : tableBrowsers) {
try {
rb.internalFrame.setMaximum(false);
} catch (PropertyVetoException e) {
// ignore
}
}
}
private void initIFrame(final JInternalFrame jInternalFrame, final BrowserContentPane browserContentPane) {
final JPanel thumbnail = new JPanel();
final JPanel thumbnailInner = new JPanel();
thumbnail.setLayout(new GridBagLayout());
GridBagConstraints gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 1;
gridBagConstraints.gridheight = 1;
gridBagConstraints.weightx = 1;
gridBagConstraints.weighty = 1;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.insets = new Insets(8, 8, 8, 8);
thumbnail.add(thumbnailInner, gridBagConstraints);
thumbnailInner.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0));
String title = jInternalFrame.getTitle();
String suffix = null;
Pattern tPat = Pattern.compile("^(.*)(\\([0-9]+\\))$");
Matcher matcher = tPat.matcher(title);
if (matcher.matches()) {
title = matcher.group(1);
suffix = matcher.group(2);
}
// boolean isEmpty = browserContentPane.get
List<String> labels = new ArrayList<String>();
final List<JLabel> jLabels = new ArrayList<JLabel>();
for (int i = 0; i < title.length(); ++i) {
labels.add(title.substring(i, i + 1));
}
if (suffix != null) {
labels.add(suffix);
}
for (String l: labels) {
JLabel jl = new JLabel(l);
jLabels.add(jl);
thumbnailInner.add(jl);
}
browserContentPane.setOnReloadAction(new Runnable() {
@Override
public void run() {
if (browserContentPane.rows != null) {
if (browserContentPane.rows.size() == 0) {
for (JLabel l: jLabels) {
l.setForeground(Color.GRAY);
}
} else {
for (JLabel l: jLabels) {
l.setForeground(Color.BLACK);
}
}
}
}
});
jInternalFrame.getContentPane().setLayout(new CardLayout());
jInternalFrame.getContentPane().add(browserContentPane, "C");
jInternalFrame.getContentPane().add(thumbnail, "T");
thumbnail.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
JPopupMenu popup = browserContentPane.createPopupMenu(null, -1, 0, 0, false);
JPopupMenu popup2 = browserContentPane.createSqlPopupMenu(null, -1, 0, 0, true);
popup.add(new JSeparator());
for (Component c : popup2.getComponents()) {
popup.add(c);
}
UIUtil.fit(popup);
popup.show(e.getComponent(), e.getX(), e.getY());
}
});
initIFrameContent(jInternalFrame, browserContentPane, thumbnail);
jInternalFrame.addComponentListener(new ComponentListener() {
@Override
public void componentHidden(ComponentEvent e) {
}
@Override
public void componentMoved(ComponentEvent e) {
}
@Override
public void componentResized(ComponentEvent e) {
initIFrameContent(jInternalFrame, browserContentPane, thumbnail);
}
@Override
public void componentShown(ComponentEvent e) {
}
});
}
private void initIFrameContent(final JInternalFrame jInternalFrame, final BrowserContentPane browserContentPane, final JPanel thumbnail) {
if (jInternalFrame.getWidth() < 150 || jInternalFrame.getHeight() < 150) {
((CardLayout) jInternalFrame.getContentPane().getLayout()).show(jInternalFrame.getContentPane(), "T");
} else {
((CardLayout) jInternalFrame.getContentPane().getLayout()).show(jInternalFrame.getContentPane(), "C");
}
}
private Color getAssociationColor(Association association) {
Color color = new java.awt.Color(0, 100, 255);
if (association.isInsertDestinationBeforeSource()) {
color = new java.awt.Color(170, 0, 0);
}
if (association.isInsertSourceBeforeDestination()) {
color = new java.awt.Color(0, 112, 0);
}
if (association.isIgnored()) {
color = new java.awt.Color(153, 153, 153);
}
return color;
}
private Rectangle layout(final boolean fullSize, RowBrowser parent, Association association, BrowserContentPane browserContentPane,
Collection<RowBrowser> ignore, int maxH, int xPosition) {
int x = (int) (BROWSERTABLE_DEFAULT_MIN * layoutMode.factor);
int y = (int) (BROWSERTABLE_DEFAULT_MIN * layoutMode.factor);
while (parent != null && parent.isHidden()) {
parent = parent.parent;
}
if (parent != null) {
x = (int) (parent.internalFrame.getX() + parent.internalFrame.getWidth() + BROWSERTABLE_DEFAULT_DISTANCE * layoutMode.factor);
y = parent.internalFrame.getY();
}
if (maxH > 0) {
y = maxH;
}
if (xPosition >= 0) {
x = (int) (xPosition * (BROWSERTABLE_DEFAULT_WIDTH + BROWSERTABLE_DEFAULT_DISTANCE) * layoutMode.factor);
}
// int h = fullSize || association == null ||
// (association.getCardinality() != Cardinality.MANY_TO_ONE &&
// association.getCardinality() != Cardinality.ONE_TO_ONE)? HEIGHT :
// browserContentPane.getMinimumSize().height + MIN_HEIGHT;
int h = (int) (BROWSERTABLE_DEFAULT_HEIGHT * layoutMode.factor);
Rectangle r = new Rectangle(x, y, (int) (BROWSERTABLE_DEFAULT_WIDTH * layoutMode.factor), h);
for (;;) {
boolean ok = true;
for (RowBrowser tb : tableBrowsers) {
if (!ignore.contains(tb) && !tb.isHidden() && tb.internalFrame.getBounds().intersects(r)) {
ok = false;
break;
}
}
r = new Rectangle(x, y, (int) (BROWSERTABLE_DEFAULT_WIDTH * layoutMode.factor), h);
y += 8 * layoutMode.factor;
if (ok) {
break;
}
}
return r;
}
protected synchronized void updateChildren(RowBrowser tableBrowser, List<Row> rows) {
boolean hasParent = false;
tableBrowser.browserContentPane.highlightedRows.clear();
for (RowBrowser rowBrowser : tableBrowsers) {
if (rowBrowser == tableBrowser.parent) {
hasParent = true;
}
if (rowBrowser.parent == tableBrowser) {
rowBrowser.rowIndex = -1;
for (int i = 0; i < rows.size(); ++i) {
if (rowBrowser.browserContentPane.parentRow != null && rowBrowser.browserContentPane.parentRow.rowId.equals(rows.get(i).rowId)) {
rowBrowser.rowIndex = i;
tableBrowser.browserContentPane.highlightedRows.add(i);
break;
}
}
}
}
if (!hasParent) {
tableBrowser.rowToRowLinks.clear();
} else {
Map<Row, Integer> rowIndex = new IdentityHashMap<Row, Integer>();
Map<String, Integer> rowIDIndex = new HashMap<String, Integer>();
Map<Row, Integer> parentRowIndex = new IdentityHashMap<Row, Integer>();
Map<String, Integer> parentRowIDIndex = new HashMap<String, Integer>();
for (int i = 0; i < rows.size(); ++i) {
Integer iI = i;
Row r = rows.get(i);
rowIndex.put(r, iI);
rowIDIndex.put(r.rowId, iI);
}
List<Row> parentRows = tableBrowser.parent.browserContentPane.rows;
for (int i = 0; i < parentRows.size(); ++i) {
Integer iI = i;
Row r = parentRows.get(i);
parentRowIndex.put(r, iI);
parentRowIDIndex.put(r.rowId, iI);
}
for (RowToRowLink rowToRowLink : tableBrowser.rowToRowLinks) {
rowToRowLink.childRowIndex = -1;
Integer i = rowIndex.get(rowToRowLink.childRow);
if (i != null) {
rowToRowLink.childRowIndex = i;
}
// for (int i = 0; i < rows.size(); ++i) {
// if (rowToRowLink.childRow == rows.get(i)) {
// rowToRowLink.childRowIndex = i;
// break;
// }
// }
if (rowToRowLink.childRowIndex < 0) {
i = rowIDIndex.get(rowToRowLink.childRow.rowId);
if (i != null) {
rowToRowLink.childRowIndex = i;
}
// for (int i = 0; i < rows.size(); ++i) {
// if
// (rowToRowLink.childRow.rowId.equals(rows.get(i).rowId)) {
// rowToRowLink.childRowIndex = i;
// break;
// }
// }
}
rowToRowLink.parentRowIndex = -1;
i = parentRowIndex.get(rowToRowLink.parentRow);
if (i != null) {
rowToRowLink.parentRowIndex = i;
}
// for (int i = 0; i < parentRows.size(); ++i) {
// if (rowToRowLink.parentRow == parentRows.get(i)) {
// rowToRowLink.parentRowIndex = i;
// break;
// }
// }
if (rowToRowLink.parentRowIndex < 0) {
i = parentRowIDIndex.get(rowToRowLink.parentRow.rowId);
if (i != null) {
rowToRowLink.parentRowIndex = i;
}
// for (int i = 0; i < parentRows.size(); ++i) {
// if
// (rowToRowLink.parentRow.rowId.equals(parentRows.get(i).rowId))
// {
// rowToRowLink.parentRowIndex = i;
// break;
// }
// }
}
}
}
}
/**
* Repaints the desktop.
*/
private void repaintDesktop() {
calculateLinks();
JScrollPane scrollPane = getScrollPane();
scrollPane.setSize(scrollPane.getWidth() + 1, scrollPane.getHeight() + 1);
scrollPane.setSize(scrollPane.getWidth() - 1, scrollPane.getHeight() - 1);
scrollPane.invalidate();
scrollPane.validate();
}
/**
* Calculates coordinates of all link-renders.
*
* @return <code>true</code> iff something has changed
*/
private synchronized boolean calculateLinks() {
boolean changed = false;
for (RowBrowser tableBrowser : tableBrowsers) {
JInternalFrame internalFrame = tableBrowser.internalFrame;
if (internalFrame.isMaximum()) {
changed = renderLinks;
renderLinks = false;
if (changed) {
rbSourceToLinks = null;
}
return changed;
}
if (tableBrowser.parent != null) {
int BORDER = 6;
int BOT_H = 32;
int x1 = internalFrame.getX() + internalFrame.getWidth() / 2;
int y1 = internalFrame.getY() + internalFrame.getHeight() / 2;
RowBrowser visParent = tableBrowser.parent;
while (visParent != null && visParent.isHidden()) {
visParent = visParent.parent;
}
if (visParent == null) {
visParent = tableBrowser.parent;
}
int midx = visParent.internalFrame.getX() + visParent.internalFrame.getWidth() / 2;
Rectangle cellRect = new Rectangle();
boolean ignoreScrolling = false;
int i = 0;
if (tableBrowser.rowIndex >= 0) {
i = tableBrowser.parent.browserContentPane.rowsTable.getRowSorter().convertRowIndexToView(tableBrowser.rowIndex);
cellRect = tableBrowser.parent.browserContentPane.rowsTable.getCellRect(i, 0, true);
if (tableBrowser.parent.browserContentPane.rows != null && tableBrowser.parent.browserContentPane.rows.size() == 1) {
cellRect.setBounds(cellRect.x, 0, cellRect.width, Math.min(cellRect.height, 20));
ignoreScrolling = true;
}
}
int x2 = visParent.internalFrame.getX();
int y = cellRect.y;
y = cellRect.height * i;
int y2 = visParent.internalFrame.getY() + y + Math.min(cellRect.height / 2, 100);
// if (midx < x1) {
x2 += visParent.internalFrame.getWidth() - BORDER;
// } else {
// x2 += BORDER;
// }
Container p = visParent.browserContentPane.rowsTable;
if (ignoreScrolling) {
p = p.getParent();
}
while (p != visParent.internalFrame) {
y2 += p.getY();
p = p.getParent();
}
int min = visParent.internalFrame.getY() + Math.min(cellRect.height, 20);
if (y2 < min) {
y2 = min;
}
int max = visParent.internalFrame.getY() + visParent.internalFrame.getHeight() - BOT_H;
if (y2 > max) {
y2 = max;
}
if (tableBrowser.rowIndex < 0) {
y2 = visParent.internalFrame.getY() + visParent.internalFrame.getHeight() / 2;
}
if (x1 != tableBrowser.x1 || y1 != tableBrowser.y1 || x2 != tableBrowser.x2 || y2 != tableBrowser.y2) {
changed = true;
tableBrowser.x1 = x1;
tableBrowser.y1 = y1;
tableBrowser.x2 = x2;
tableBrowser.y2 = y2;
}
for (RowToRowLink rowToRowLink : tableBrowser.rowToRowLinks) {
x1 = y1 = x2 = y2 = -1;
try {
if (rowToRowLink.childRowIndex >= 0 && rowToRowLink.parentRowIndex >= 0) {
cellRect = new Rectangle();
i = 0;
visParent = tableBrowser.parent;
while (visParent != null && visParent.isHidden()) {
visParent = visParent.parent;
}
if (visParent == null) {
visParent = tableBrowser.parent;
}
int dll = Math.abs(visParent.internalFrame.getX() - internalFrame.getX());
int dlr = Math.abs(visParent.internalFrame.getX() - (internalFrame.getX() + internalFrame.getWidth()));
int drl = Math.abs((visParent.internalFrame.getX() + visParent.internalFrame.getWidth()) - internalFrame.getX());
int drr = Math.abs((visParent.internalFrame.getX() + visParent.internalFrame.getWidth())
- (internalFrame.getX() + internalFrame.getWidth()));
boolean r1, r2;
int dmin = Math.min(dll, Math.min(dlr, Math.min(drl, drr)));
r2 = dmin == drl || dmin == drr;
r1 = dmin == dlr || dmin == drr;
ignoreScrolling = false;
if (rowToRowLink.childRowIndex >= 0) {
i = tableBrowser.browserContentPane.rowsTable.getRowSorter().convertRowIndexToView(rowToRowLink.childRowIndex);
cellRect = tableBrowser.browserContentPane.rowsTable.getCellRect(i, 0, true);
if (tableBrowser.browserContentPane.rows != null && tableBrowser.browserContentPane.rows.size() == 1) {
cellRect.setBounds(cellRect.x, 0, cellRect.width, Math.min(cellRect.height, 20));
ignoreScrolling = true;
}
}
x1 = internalFrame.getX();
y = cellRect.height * i;
y1 = internalFrame.getY() + y + cellRect.height / 2;
// if (r1) {
// x1 += internalFrame.getWidth()- BORDER;
// } else {
x1 += BORDER;
// }
p = tableBrowser.browserContentPane.rowsTable;
if (ignoreScrolling) {
p = p.getParent();
}
while (p != internalFrame) {
y1 += p.getY();
p = p.getParent();
}
min = internalFrame.getY() + cellRect.height;
if (y1 < min) {
y1 = min;
}
max = internalFrame.getY() + internalFrame.getHeight() - BOT_H;
if (y1 > max) {
y1 = max;
}
ignoreScrolling = false;
cellRect = new Rectangle();
i = 0;
if (rowToRowLink.parentRowIndex >= 0) {
i = tableBrowser.parent.browserContentPane.rowsTable.getRowSorter().convertRowIndexToView(rowToRowLink.parentRowIndex);
cellRect = tableBrowser.parent.browserContentPane.rowsTable.getCellRect(i, 0, true);
if (tableBrowser.parent.browserContentPane.rows != null && tableBrowser.parent.browserContentPane.rows.size() == 1) {
cellRect.setBounds(cellRect.x, 0, cellRect.width, Math.min(cellRect.height, 20));
ignoreScrolling = true;
}
}
x2 = visParent.internalFrame.getX();
y = cellRect.height * i;
y2 = visParent.internalFrame.getY() + y + cellRect.height / 2;
// if (r2) {
x2 += visParent.internalFrame.getWidth() - BORDER;
// } else {
// x2 += BORDER;
// }
p = visParent.browserContentPane.rowsTable;
if (ignoreScrolling) {
p = p.getParent();
}
while (p != visParent.internalFrame) {
y2 += p.getY();
p = p.getParent();
}
min = visParent.internalFrame.getY() + cellRect.height;
if (y2 < min) {
y2 = min;
}
max = visParent.internalFrame.getY() + visParent.internalFrame.getHeight() - BOT_H;
if (y2 > max) {
y2 = max;
}
}
if (x1 != rowToRowLink.x1 || y1 != rowToRowLink.y1 || x2 != rowToRowLink.x2 || y2 != rowToRowLink.y2) {
changed = true;
rowToRowLink.x1 = x1;
rowToRowLink.y1 = y1;
rowToRowLink.x2 = x2;
rowToRowLink.y2 = y2;
}
} catch (Exception e) {
// ignore
}
}
}
}
if (!renderLinks) {
changed = true;
}
renderLinks = true;
if (lastPTS + 1000 < System.currentTimeMillis()) {
changed = true;
}
if (changed) {
lastPTS = System.currentTimeMillis();
}
if (changed) {
rbSourceToLinks = null;
}
return changed;
}
private long lastPTS = 0;
private static class Link {
public boolean visible = true;
public final RowBrowser from, to;
public final String sourceRowID, destRowID;
public int x1, y1, x2, y2;
public final Color color;
public final boolean dotted, intersect;
public Link(RowBrowser from, RowBrowser to, String sourceRowID, String destRowID, int x1, int y1, int x2, int y2, Color color, boolean dotted,
boolean intersect) {
this.from = from;
this.to = to;
this.sourceRowID = sourceRowID;
this.destRowID = destRowID;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
this.color = color;
this.dotted = dotted;
this.intersect = intersect;
}
};
private Map<RowBrowser, Map<String, List<Link>>> rbSourceToLinks = null;
/**
* Paints all link-renders.
*/
@Override
public synchronized void paint(Graphics graphics) {
super.paint(graphics);
if (graphics instanceof Graphics2D) {
Graphics2D g2d = (Graphics2D) graphics;
if (renderLinks) {
if (rbSourceToLinks == null) {
rbSourceToLinks = new HashMap<RowBrowser, Map<String, List<Link>>>();
final String ALL = "-";
for (RowBrowser tableBrowser : tableBrowsers) {
Map<String, List<Link>> links = new TreeMap<String, List<Link>>();
rbSourceToLinks.put(tableBrowser, links);
if (!tableBrowser.internalFrame.isIcon() && (tableBrowser.parent == null || !tableBrowser.parent.internalFrame.isIcon())) {
Color color = tableBrowser.color;
if (tableBrowser.parent != null && (tableBrowser.rowIndex >= 0 || tableBrowser.rowToRowLinks.isEmpty())) {
String sourceRowID = ALL;
String destRowID = ALL;
if (tableBrowser.browserContentPane.parentRow != null) {
destRowID = tableBrowser.browserContentPane.parentRow.rowId;
}
Link link = new Link(tableBrowser, tableBrowser.parent, sourceRowID, destRowID, tableBrowser.x1, tableBrowser.y1,
tableBrowser.x2, tableBrowser.y2, color, tableBrowser.parent == null || tableBrowser.rowIndex < 0, true);
List<Link> l = links.get(sourceRowID);
if (l == null) {
l = new ArrayList<Link>();
links.put(sourceRowID, l);
}
l.add(link);
}
for (RowToRowLink rowToRowLink : tableBrowser.rowToRowLinks) {
if (rowToRowLink.x1 >= 0) {
String sourceRowID = rowToRowLink.childRow.rowId;
String destRowID = rowToRowLink.parentRow.rowId;
if (!tableBrowser.isHidden() && (tableBrowser.parent == null || !tableBrowser.parent.isHidden())) {
// optimization
sourceRowID = "";
}
Link link = new Link(tableBrowser, tableBrowser.parent, sourceRowID, destRowID, rowToRowLink.x1, rowToRowLink.y1,
rowToRowLink.x2, rowToRowLink.y2, color, false, false);
List<Link> l = links.get(sourceRowID);
if (l == null) {
l = new ArrayList<Link>();
links.put(sourceRowID, l);
}
l.add(link);
}
}
}
}
// join links of hidden browser
List<Link> toJoinList = new ArrayList<Link>();
for (RowBrowser tableBrowser : tableBrowsers) {
if (tableBrowser.parent != null && tableBrowser.parent.isHidden()) {
List<Link> newLinks = new ArrayList<Link>();
Map<String, List<Link>> links = rbSourceToLinks.get(tableBrowser);
for (Map.Entry<String, List<Link>> e : links.entrySet()) {
for (Link link : e.getValue()) {
link.visible = false;
List<Link> ll;
if (link.destRowID == ALL) {
ll = new ArrayList<Desktop.Link>();
for (List<Link> values : rbSourceToLinks.get(link.to).values()) {
for (Link l : values) {
ll.add(l);
}
}
} else {
ll = rbSourceToLinks.get(link.to).get(link.destRowID);
}
toJoinList.clear();
if (ll != null) {
toJoinList.addAll(ll);
}
ll = rbSourceToLinks.get(link.to).get(ALL);
if (ll != null) {
toJoinList.addAll(ll);
}
for (Link toJoin : toJoinList) {
toJoin.visible = false;
Color color = /*
* link.color.equals(toJoin.
* color)? link.color :
*/Color.black;
boolean intersect = link.intersect;
boolean dotted = link.dotted || toJoin.dotted;
newLinks.add(new Link(link.from, toJoin.to, link.sourceRowID, toJoin.destRowID, link.x1, link.y1, toJoin.x2, toJoin.y2,
color, dotted, intersect));
}
}
}
for (Link link : newLinks) {
links.get(link.sourceRowID).add(link);
}
}
}
}
for (boolean pbg : new Boolean[] { true, false }) {
Set<Long> linesHash = new HashSet<Long>(200000);
for (RowBrowser tableBrowser : rbSourceToLinks.keySet()) {
if (!tableBrowser.isHidden()) {
Map<String, List<Link>> links = rbSourceToLinks.get(tableBrowser);
for (Map.Entry<String, List<Link>> e : links.entrySet()) {
for (Link link : e.getValue()) {
if (link.visible && !link.from.isHidden() && !link.to.isHidden()) {
Color color = pbg ? Color.white : link.color;
Point2D start = new Point2D.Double(link.x2, link.y2);
Point2D end = new Point2D.Double(link.x1, link.y1);
paintLink(start, end, color, g2d, tableBrowser, pbg, link.intersect, linesHash, link.dotted);
}
}
}
}
// if (!tableBrowser.internalFrame.isIcon() &&
// (tableBrowser.parent == null ||
// !tableBrowser.parent.internalFrame.isIcon())) {
// Color color = pbg? Color.white : tableBrowser.color;
// if (tableBrowser.parent != null &&
// (tableBrowser.rowIndex >= 0 ||
// tableBrowser.rowToRowLinks.isEmpty())) {
// Point2D start = new Point2D.Double(tableBrowser.x2,
// tableBrowser.y2);
// Point2D end = new Point2D.Double(tableBrowser.x1,
// tableBrowser.y1);
// paintLink(start, end, color, g2d, tableBrowser, pbg,
// true, linesHash, tableBrowser.parent == null ||
// tableBrowser.rowIndex < 0);
// }
// for (RowToRowLink rowToRowLink:
// tableBrowser.rowToRowLinks) {
// if (rowToRowLink.x1 >= 0) {
// paintLink(new Point2D.Double(rowToRowLink.x2,
// rowToRowLink.y2), new Point2D.Double(rowToRowLink.x1,
// rowToRowLink.y1), color, g2d, tableBrowser, pbg,
// false, linesHash, false);
// }
// }
// }
}
}
}
}
}
private void paintLink(Point2D start, Point2D end, Color color, Graphics2D g2d, RowBrowser tableBrowser, boolean pbg, boolean intersect,
Set<Long> lineHashes, boolean dotted) {
g2d.setColor(color);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
BasicStroke stroke = new BasicStroke(!intersect ? (pbg ? 3 : 1) : (pbg ? 5 : 3));
g2d.setStroke(dotted ? new BasicStroke(stroke.getLineWidth(), stroke.getEndCap(), stroke.getLineJoin(), stroke.getMiterLimit(), new float[] { 2f, 6f },
1.0f) : stroke);
AffineTransform t = new AffineTransform();
t.setToRotation(Math.PI / 4);
Point2D p = new Point2D.Double(), shift = new Point2D.Double();
double d = start.distance(end) / 3.0;
p.setLocation((end.getX() - start.getX()) / d, (end.getY() - start.getY()) / d);
t.transform(p, shift);
start.setLocation(start.getX() + shift.getX(), start.getY() + shift.getY());
end.setLocation(end.getX() + shift.getX(), end.getY() + shift.getY());
// compute the intersection with the target bounding box
if (intersect) {
Point2D[] sect = new Point2D[10];
int i = GraphicsLib.intersectLineRectangle(start, end, tableBrowser.internalFrame.getBounds(), sect);
if (i == 0)
return;
end = sect[0];
}
if (start.distance(end) < 2)
return;
long lineHash = ((long) start.hashCode()) + (((long) Integer.MAX_VALUE) + 1) * ((long) end.hashCode());
if (lineHashes.contains(lineHash)) {
return;
}
lineHashes.add(lineHash);
// create the arrow head shape
m_arrowHead = new Polygon();
double ws = 0.5;
double hs = 2.0 / 3.0;
double w = !intersect ? (pbg ? 3 : 3) : (pbg ? 3 : 3), h = w;
m_arrowHead.addPoint(0, 0);
m_arrowHead.addPoint((int) (ws * -w), (int) (hs * (-h)));
// m_arrowHead.addPoint(0, (int) (hs * (-2 * h)));
m_arrowHead.addPoint((int) (ws * w), (int) (hs * (-h)));
m_arrowHead.addPoint(0, 0);
AffineTransform at = getArrowTrans(start, end, 10);
Shape m_curArrow = at.createTransformedShape(m_arrowHead);
Point2D lineEnd = end;
lineEnd.setLocation(0, -2);
at.transform(lineEnd, lineEnd);
g2d.drawLine((int) start.getX(), (int) start.getY(), (int) end.getX(), (int) end.getY());
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(1));
g2d.fill(m_curArrow);
if (pbg) {
g2d.draw(m_curArrow);
}
}
private Polygon m_arrowHead;
/**
* Returns an affine transformation that maps the arrowhead shape to the
* position and orientation specified by the provided line segment end
* points.
*/
protected AffineTransform getArrowTrans(Point2D p1, Point2D p2, double width) {
AffineTransform m_arrowTrans = new AffineTransform();
m_arrowTrans.setToTranslation(p2.getX(), p2.getY());
m_arrowTrans.rotate(-Math.PI / 2.0 + Math.atan2(p2.getY() - p1.getY(), p2.getX() - p1.getX()));
if (width > 1) {
double scalar = width / 2;
m_arrowTrans.scale(scalar, scalar);
}
return m_arrowTrans;
}
private static int FRAME_OFFSET = 20;
private MDIDesktopManager manager;
public void setBounds(int x, int y, int w, int h) {
super.setBounds(x, y, w, h);
checkDesktopSize();
}
public Component add(JInternalFrame frame) {
JInternalFrame[] array = getAllFrames();
Point p;
int w;
int h;
Component retval = super.add(frame);
checkDesktopSize();
if (array.length > 0) {
p = array[0].getLocation();
p.x = p.x + FRAME_OFFSET;
p.y = p.y + FRAME_OFFSET;
} else {
p = new Point(0, 0);
}
frame.setLocation(p.x, p.y);
if (frame.isResizable()) {
w = getWidth() - (getWidth() / 3);
h = getHeight() - (getHeight() / 3);
if (w < frame.getMinimumSize().getWidth())
w = (int) frame.getMinimumSize().getWidth();
if (h < frame.getMinimumSize().getHeight())
h = (int) frame.getMinimumSize().getHeight();
frame.setSize(w, h);
}
moveToFront(frame);
frame.setVisible(true);
try {
frame.setSelected(true);
} catch (PropertyVetoException e) {
frame.toBack();
}
return retval;
}
public void remove(Component c) {
super.remove(c);
checkDesktopSize();
}
/**
* Cascade all internal frames
*/
public void cascadeFrames() {
int x = 0;
int y = 0;
JInternalFrame allFrames[] = getAllFrames();
manager.setNormalSize();
int frameHeight = (getBounds().height - 5) - allFrames.length * FRAME_OFFSET;
int frameWidth = (getBounds().width - 5) - allFrames.length * FRAME_OFFSET;
for (int i = allFrames.length - 1; i >= 0; i--) {
allFrames[i].setSize(frameWidth, frameHeight);
allFrames[i].setLocation(x, y);
x = x + FRAME_OFFSET;
y = y + FRAME_OFFSET;
}
}
/**
* Tile all internal frames
*/
public void tileFrames() {
java.awt.Component allFrames[] = getAllFrames();
manager.setNormalSize();
int frameHeight = getBounds().height / allFrames.length;
int y = 0;
for (int i = 0; i < allFrames.length; i++) {
allFrames[i].setSize(getBounds().width, frameHeight);
allFrames[i].setLocation(0, y);
y = y + frameHeight;
}
}
/**
* Sets all component size properties ( maximum, minimum, preferred) to the
* given dimension.
*/
public void setAllSize(Dimension d) {
setMinimumSize(d);
setMaximumSize(d);
setPreferredSize(d);
}
/**
* Sets all component size properties ( maximum, minimum, preferred) to the
* given width and height.
*/
public void setAllSize(int width, int height) {
setAllSize(new Dimension(width, height));
}
private void checkDesktopSize() {
if (getParent() != null && isVisible())
manager.resizeDesktop();
}
private JScrollPane getScrollPane() {
if (getParent() instanceof JViewport) {
JViewport viewPort = (JViewport) getParent();
if (viewPort.getParent() instanceof JScrollPane)
return (JScrollPane) viewPort.getParent();
}
return null;
}
/**
* Private class used to replace the standard DesktopManager for
* JDesktopPane. Used to provide scrollbar functionality.
*/
class MDIDesktopManager extends DefaultDesktopManager {
private Desktop desktop;
public MDIDesktopManager(Desktop desktop) {
this.desktop = desktop;
}
public void endResizingFrame(JComponent f) {
super.endResizingFrame(f);
resizeDesktop();
}
public void endDraggingFrame(JComponent f) {
super.endDraggingFrame(f);
resizeDesktop();
}
public void setNormalSize() {
JScrollPane scrollPane = getScrollPane();
int x = 0;
int y = 0;
Insets scrollInsets = getScrollPaneInsets();
if (scrollPane != null) {
Dimension d = scrollPane.getVisibleRect().getSize();
if (scrollPane.getBorder() != null) {
d.setSize(d.getWidth() - scrollInsets.left - scrollInsets.right, d.getHeight() - scrollInsets.top - scrollInsets.bottom);
}
d.setSize(d.getWidth() - 20, d.getHeight() - 20);
desktop.setAllSize(x, y);
scrollPane.invalidate();
scrollPane.validate();
}
}
private Insets getScrollPaneInsets() {
JScrollPane scrollPane = getScrollPane();
if (scrollPane == null)
return new Insets(0, 0, 0, 0);
else
return getScrollPane().getBorder().getBorderInsets(scrollPane);
}
private JScrollPane getScrollPane() {
if (desktop.getParent() instanceof JViewport) {
JViewport viewPort = (JViewport) desktop.getParent();
if (viewPort.getParent() instanceof JScrollPane)
return (JScrollPane) viewPort.getParent();
}
return null;
}
public void resizeDesktop() {
int x = 0;
int y = 0;
JScrollPane scrollPane = getScrollPane();
Insets scrollInsets = getScrollPaneInsets();
if (scrollPane != null) {
boolean isMaximized = false;
JInternalFrame allFrames[] = desktop.getAllFrames();
for (int i = 0; i < allFrames.length; i++) {
if (allFrames[i].isVisible()) {
if (allFrames[i].isMaximum()) {
isMaximized = true;
}
if (allFrames[i].getX() + allFrames[i].getWidth() > x) {
x = allFrames[i].getX() + allFrames[i].getWidth();
}
if (allFrames[i].getY() + allFrames[i].getHeight() > y) {
y = allFrames[i].getY() + allFrames[i].getHeight();
}
}
}
Dimension d = scrollPane.getVisibleRect().getSize();
if (scrollPane.getBorder() != null) {
d.setSize(d.getWidth() - scrollInsets.left - scrollInsets.right, d.getHeight() - scrollInsets.top - scrollInsets.bottom);
}
if (x <= d.getWidth() || isMaximized)
x = ((int) d.getWidth()) - 20;
if (y <= d.getHeight() || isMaximized)
y = ((int) d.getHeight()) - 20;
desktop.setAllSize(x, y);
scrollPane.invalidate();
scrollPane.validate();
}
}
}
public synchronized void stop() {
running = false;
desktops.remove(this);
for (RowBrowser rb : tableBrowsers) {
rb.browserContentPane.cancelLoadJob(false);
}
if (session != null) {
new Thread(new Runnable() {
@Override
public void run() {
try {
synchronized (session) {
session.shutDown();
}
} catch (SQLException e) {
// exception already has been logged
}
}
}).start();
}
}
private final DataBrowser parentFrame;
public static enum LayoutMode {
THUMBNAIL(0.22), TINY(0.569), SMALL(0.75), MEDIUM(1.0), LARGE(1.4);
public final double factor;
private LayoutMode(double factor) {
this.factor = factor;
}
}
LayoutMode layoutMode = LayoutMode.MEDIUM;
public void layoutBrowser() {
JInternalFrame selectedFrame = getSelectedFrame();
List<RowBrowser> all = new ArrayList<RowBrowser>(tableBrowsers);
layout(all, 0);
optimizeLayout();
all.clear();
int maxH = 0;
for (RowBrowser rb : tableBrowsers) {
if (rb.browserContentPane.table instanceof BrowserContentPane.SqlStatementTable) {
all.add(rb);
} else {
maxH = Math.max(maxH, rb.internalFrame.getBounds().y + rb.internalFrame.getBounds().height);
}
}
layout(all, maxH + (int) (16 * layoutMode.factor));
checkDesktopSize();
if (selectedFrame != null) {
try {
selectedFrame.setSelected(true);
} catch (PropertyVetoException e) {
// ignore
}
this.scrollToCenter(selectedFrame);
}
}
private void layout(List<RowBrowser> toLayout, int maxH) {
List<RowBrowser> roots = new ArrayList<RowBrowser>();
for (RowBrowser rb : toLayout) {
if (rb.parent == null) {
roots.add(rb);
}
}
while (!roots.isEmpty()) {
List<RowBrowser> nextColumn = new ArrayList<RowBrowser>();
int i = 0;
for (RowBrowser rb : roots) {
try {
rb.internalFrame.setMaximum(false);
} catch (PropertyVetoException e) {
// ignore
}
int xPosition = -1;
if (maxH > 0) {
xPosition = i;
}
rb.internalFrame.setBounds(layout(rb.rowIndex < 0, rb.parent, rb.association, rb.browserContentPane, toLayout, maxH, xPosition));
rb.browserContentPane.adjustRowTableColumnsWidth();
toLayout.remove(rb);
for (RowBrowser rbc : toLayout) {
if (rbc.parent == rb) {
nextColumn.add(rbc);
}
}
++i;
}
roots = nextColumn;
}
}
/**
* Experimental layout optimization.
*/
private void optimizeLayout() {
TreeLayoutOptimizer.Node<RowBrowser> root = new TreeLayoutOptimizer.Node<RowBrowser>(null);
collectChildren(root);
TreeLayoutOptimizer.optimizeTreeLayout(root);
arrangeNodes(root);
}
private void collectChildren(Node<RowBrowser> root) {
List<RowBrowser> children;
if (root.getUserObject() == null) {
children = getRootBrowsers(true);
} else {
children = getChildBrowsers(root.getUserObject(), true);
}
for (RowBrowser rb : children) {
if (rb.browserContentPane.table instanceof BrowserContentPane.SqlStatementTable) {
continue;
}
TreeLayoutOptimizer.Node<RowBrowser> childNode = new TreeLayoutOptimizer.Node<RowBrowser>(rb);
root.addChild(childNode);
collectChildren(childNode);
}
}
private void arrangeNodes(Node<RowBrowser> root) {
if (root.getUserObject() != null) {
JInternalFrame iFrame = root.getUserObject().internalFrame;
int x = (int) (BROWSERTABLE_DEFAULT_MIN * layoutMode.factor);
int y = (int) (BROWSERTABLE_DEFAULT_MIN * layoutMode.factor);
x += (root.getLevel() - 1) * (int) ((BROWSERTABLE_DEFAULT_WIDTH + BROWSERTABLE_DEFAULT_DISTANCE) * layoutMode.factor);
y += (int) (root.getPosition() * (BROWSERTABLE_DEFAULT_HEIGHT + 8) * layoutMode.factor);
int h = (int) (BROWSERTABLE_DEFAULT_HEIGHT * layoutMode.factor);
Rectangle r = new Rectangle(x, y, (int) (BROWSERTABLE_DEFAULT_WIDTH * layoutMode.factor), h);
iFrame.setBounds(r);
}
for (Node<RowBrowser> child : root.getChildren()) {
arrangeNodes(child);
}
}
private Map<Rectangle, double[]> precBounds = new HashMap<Rectangle, double[]>();
private static Collection<Desktop> desktops = new ArrayList<Desktop>();
public void rescaleLayout(LayoutMode layoutMode, Point fixed) {
double scale = layoutMode.factor / this.layoutMode.factor;
if (fixed == null) {
fixed = new Point(getVisibleRect().x + getVisibleRect().width / 2, getVisibleRect().y + getVisibleRect().height / 2);
}
try {
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
this.layoutMode = layoutMode;
Map<Rectangle, double[]> newPrecBounds = new HashMap<Rectangle, double[]>();
for (RowBrowser rb : new ArrayList<RowBrowser>(tableBrowsers)) {
if (rb.internalFrame.isMaximum()) {
try {
rb.internalFrame.setMaximum(false);
} catch (PropertyVetoException e) {
// ignore
}
}
Rectangle bounds = rb.internalFrame.getBounds();
Rectangle newBounds;
double[] pBounds = precBounds.get(bounds);
if (pBounds == null) {
pBounds = new double[] { bounds.x * scale, bounds.y * scale, bounds.width * scale, bounds.height * scale };
} else {
pBounds = new double[] { pBounds[0] * scale, pBounds[1] * scale, pBounds[2] * scale, pBounds[3] * scale };
}
newBounds = new Rectangle((int) pBounds[0], (int) pBounds[1], (int) pBounds[2], (int) pBounds[3]);
rb.internalFrame.setBounds(newBounds);
rb.browserContentPane.adjustRowTableColumnsWidth();
newPrecBounds.put(newBounds, pBounds);
}
precBounds = newPrecBounds;
manager.resizeDesktop();
Rectangle vr = new Rectangle(Math.max(0, (int) (fixed.x * scale - getVisibleRect().width / 2)), Math.max(0,
(int) (fixed.y * scale - getVisibleRect().height / 2)), getVisibleRect().width, getVisibleRect().height);
scrollRectToVisible(vr);
updateMenu(layoutMode);
adjustClosure(null);
} finally {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}
void onMouseWheelMoved(java.awt.event.MouseWheelEvent e, JScrollPane scrollPane) {
if (!e.isControlDown()) {
if ((e.getScrollAmount() != 0) && (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL)) {
JScrollBar toScroll = scrollPane.getVerticalScrollBar();
int direction = 0;
// find which scrollbar to scroll, or return if none
if ((toScroll == null) || !toScroll.isVisible() || ((e.getModifiers() & InputEvent.ALT_MASK) != 0)) {
toScroll = scrollPane.getHorizontalScrollBar();
if ((toScroll == null) || !toScroll.isVisible()) {
return;
}
}
if (e.getWheelRotation() != 0) {
direction = (e.getWheelRotation() < 0) ? (-1) : 1;
}
double f = 1.0;
if (getPreciseWheelRotation != null) {
try {
double pwr = (Double) getPreciseWheelRotation.invoke(e);
direction = pwr == 0? 0 : (pwr < 0) ? (-1) : 1;
f = Math.abs(pwr);
} catch (Exception exc) {
// ignored
}
}
if (direction != 0) {
int oldValue = toScroll.getValue();
int blockIncrement = toScroll.getUnitIncrement(direction);
// allow for partial page overlapping
// blockIncrement -= 10;
int delta = (int) (f * blockIncrement * ((direction > 0) ? +1 : -1));
int newValue = oldValue + delta;
// Check for overflow.
if ((delta > 0) && (newValue < oldValue)) {
newValue = toScroll.getMaximum();
} else if ((delta < 0) && (newValue > oldValue)) {
newValue = toScroll.getMinimum();
}
toScroll.setValue(newValue);
}
}
}
}
void onMouseWheelMoved(java.awt.event.MouseWheelEvent e) {
if (e.isControlDown()) {
int d = 0;
if (e.getWheelRotation() < 0) {
d = -1;
}
if (e.getWheelRotation() > 0) {
d = 1;
}
if (d != 0) {
for (RowBrowser rb : new ArrayList<RowBrowser>(tableBrowsers)) {
if (rb.internalFrame.isMaximum()) {
return;
}
}
d += layoutMode.ordinal();
if (d >= 0 && d < LayoutMode.values().length) {
Point fixed = SwingUtilities.convertPoint(e.getComponent(), e.getPoint().x, e.getPoint().y, Desktop.this);
rescaleLayout(LayoutMode.values()[d], fixed);
}
}
}
}
public void closeAll() {
closeAll(new ArrayList<RowBrowser>(tableBrowsers));
}
public void closeAll(Collection<RowBrowser> toClose) {
for (RowBrowser rb : toClose) {
close(rb, false);
getDesktopManager().closeFrame(rb.internalFrame);
}
updateMenu();
}
private void close(final RowBrowser tableBrowser, boolean convertChildrenToRoots) {
List<RowBrowser> children = new ArrayList<RowBrowser>();
for (RowBrowser tb : tableBrowsers) {
if (tb.parent == tableBrowser) {
tb.parent = null;
children.add(tb);
}
}
tableBrowsers.remove(tableBrowser);
tableBrowser.browserContentPane.cancelLoadJob(true);
if (convertChildrenToRoots) {
for (RowBrowser child : children) {
child.convertToRoot();
}
}
for (RowBrowser rb : tableBrowsers) {
updateChildren(rb, rb.browserContentPane.rows);
}
repaintDesktop();
updateMenu();
}
/**
* Reloads the data model and replaces the tables in all browser windows.
*/
public void reloadDataModel(Map<String, String> schemamapping) throws Exception {
reloadDataModel(schemamapping, true);
}
/**
* Reloads the data model and replaces the tables in all browser windows.
*/
public void reloadDataModel(Map<String, String> schemamapping, boolean forAll) throws Exception {
try {
Component pFrame = SwingUtilities.getWindowAncestor(this);
if (pFrame == null) {
pFrame = this;
}
String filename = ".tempsession-" + System.currentTimeMillis();
storeSession(filename);
DataModel newModel = new DataModel(schemamapping, executionContext, false);
datamodel.set(newModel);
restoreSession(null, pFrame, filename);
File file = new File(filename);
file.delete();
} catch (Throwable e) {
UIUtil.showException(this, "Error", e, session);
}
//
// for (RowBrowser rb : tableBrowsers) {
// if (rb.browserContentPane != null) {
// rb.browserContentPane.dataModel = newModel;
// if (rb.browserContentPane.table != null && datamodel.get() != null) {
// Table oldTable = rb.browserContentPane.table;
// Table newTable = null;
// if (oldTable.getOriginalName() != null) {
// for (Table t : newModel.getTables()) {
// if (oldTable.getOriginalName().equals(t.getOriginalName())) {
// newTable = t;
// break;
// }
// }
// }
// if (newTable == null && oldTable.getName() != null) {
// newTable = newModel.getTableByDisplayName(datamodel.get().getDisplayName(oldTable));
// }
// if (newTable != null) {
// rb.browserContentPane.table = newTable;
// }
// }
// Association association = rb.browserContentPane.association;
// if (association != null && datamodel.get() != null) {
// Association newAssociation = newModel.namedAssociations.get(association.getName());
// if (newAssociation != null) {
// if (association.source.equals(newAssociation.source)) {
// if (association.destination.equals(newAssociation.destination)) {
// rb.browserContentPane.association = newAssociation;
// }
// }
// }
// }
// }
// updateChildren(rb, rb.browserContentPane.rows);
// }
if (forAll) {
for (Desktop desktop : desktops) {
if (desktop != this) {
desktop.reloadDataModel(desktop.schemaMapping, false);
}
}
}
}
/**
* Reloads the rows in all root-table-browsers.
*/
public void reloadRoots() throws Exception {
for (RowBrowser rb : tableBrowsers) {
if (rb.browserContentPane != null) {
if (rb.parent == null) {
rb.browserContentPane.reloadRows();
}
}
}
}
private boolean loadSchemaMapping = true;
public abstract void openSchemaAnalyzer();
public void openSchemaMappingDialog(boolean silent) {
try {
Map<String, String> mapping = schemaMapping;
if (loadSchemaMapping || silent) {
mapping = SchemaMappingDialog.restore(dbConnectionDialog);
loadSchemaMapping = false;
}
if (!silent) {
SchemaMappingDialog schemaMappingDialog = new SchemaMappingDialog(parentFrame, datamodel.get(), dbConnectionDialog, session, mapping);
mapping = schemaMappingDialog.getMapping();
}
if (mapping != null) {
SchemaMappingDialog.store(mapping, dbConnectionDialog);
schemaMapping.clear();
schemaMapping.putAll(mapping);
parentFrame.updateStatusBar();
reloadDataModel(mapping, !silent);
reloadRoots();
}
} catch (Exception e) {
UIUtil.showException(this, "Error", e, session);
}
}
/**
* Lets user chose a table browser and creates an extraction model for it.
*/
public void createExtractionModel(boolean doExport) {
Set<String> titles = new TreeSet<String>();
Map<String, RowBrowser> rowBrowserByTitle = new HashMap<String, Desktop.RowBrowser>();
for (RowBrowser rb : tableBrowsers) {
if (rb.browserContentPane.table != null && !(rb.browserContentPane.table instanceof BrowserContentPane.SqlStatementTable)) {
titles.add(rb.internalFrame.getTitle());
rowBrowserByTitle.put(rb.internalFrame.getTitle(), rb);
}
}
String s = (String) JOptionPane.showInputDialog(this.parentFrame, "Select subject table", "Subject", JOptionPane.QUESTION_MESSAGE, null,
titles.toArray(), null);
if (s != null) {
rowBrowserByTitle.get(s).browserContentPane.openExtractionModelEditor(doExport);
}
}
void updateMenu() {
boolean hasTableBrowser = false;
boolean hasIFrame = false;
for (RowBrowser rb : tableBrowsers) {
hasIFrame = true;
if (!(rb.browserContentPane.table instanceof BrowserContentPane.SqlStatementTable)) {
hasTableBrowser = true;
}
}
updateMenu(hasTableBrowser, hasIFrame);
}
protected void updateMenu(boolean hasTableBrowser, boolean hasIFrame) {
if (!hasIFrame) {
if (!hasTableBrowser) {
currentSessionFileName = null;
}
}
}
protected abstract void updateMenu(LayoutMode layoutMode);
private final String LF = System.getProperty("line.separator", "\n");
private String currentSessionFileName = null;
/**
* Stores browser session.
*/
public void storeSession() {
String fnProp = null;
for (RowBrowser rb : tableBrowsers) {
if (fnProp == null && rb.parent == null && rb.browserContentPane.table != null) {
if (!(rb.browserContentPane.table instanceof BrowserContentPane.SqlStatementTable)) {
fnProp = datamodel.get().getDisplayName(rb.browserContentPane.table).replace(' ', '-').replace('\"', '-').replace('\'', '-')
.replace('(', '-').replace(')', '-').toLowerCase()
+ ".dbl";
}
}
}
if (currentSessionFileName != null) {
fnProp = currentSessionFileName;
}
File startDir = new File("layout");
Component pFrame = SwingUtilities.getWindowAncestor(this);
if (pFrame == null) {
pFrame = this;
}
String sFile = UIUtil.choseFile(fnProp == null ? null : new File(startDir, fnProp), startDir.getPath(), "Store Layout", ".dbl", pFrame, true, false);
if (sFile != null) {
File file = new File(sFile);
if (!file.exists() || JOptionPane.YES_OPTION == JOptionPane.showConfirmDialog(
pFrame,
"The file '" + sFile + " already exists. Do you wont to replace the existing file?", "Store Layout", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE)) {
try {
storeSession(sFile);
} catch (Throwable e) {
UIUtil.showException(this, "Error", e, session);
}
currentSessionFileName = sFile;
}
}
}
/**
* Stores browser session.
*/
private void storeSession(String sFile) throws IOException {
int i = 1;
Map<RowBrowser, Integer> browserNumber = new HashMap<Desktop.RowBrowser, Integer>();
for (RowBrowser rb : tableBrowsers) {
browserNumber.put(rb, i++);
}
FileWriter out = new FileWriter(new File(sFile));
out.write("Layout; " + layoutMode + LF);
for (RowBrowser rb : tableBrowsers) {
if (rb.parent == null) {
storeSession(rb, browserNumber, out);
}
}
out.close();
}
/**
* Recursively stores row-browser session.
*/
private void storeSession(RowBrowser rb, Map<RowBrowser, Integer> browserNumber, FileWriter out) throws IOException {
if (rb.browserContentPane.table != null) {
String csv = browserNumber.get(rb) + "; " + (rb.parent == null ? "" : browserNumber.get(rb.parent)) + "; ";
String where = rb.browserContentPane.getAndConditionText().trim();
if (rb.browserContentPane.parentRow != null) {
if (where.length() > 0) {
where = "(" + where + ") and (" + rb.browserContentPane.parentRow.rowId + ")";
} else {
where = rb.browserContentPane.parentRow.rowId;
}
}
csv += where + "; ";
csv += rb.internalFrame.getLocation().x + "; " + rb.internalFrame.getLocation().y + "; ";
csv += rb.internalFrame.getSize().width + "; " + rb.internalFrame.getSize().height + "; ";
csv += rb.browserContentPane.limitBox.getSelectedItem() + "; " + rb.browserContentPane.selectDistinctCheckBox.isSelected() + "; ";
if (rb.browserContentPane.table instanceof BrowserContentPane.SqlStatementTable) {
csv += "Q; " + CsvFile.encodeCell(rb.browserContentPane.sqlBrowserContentPane.sqlEditorPane.getText()) + "; ";
} else {
csv += "T; " + CsvFile.encodeCell(rb.browserContentPane.table.getName()) + "; "
+ (rb.association == null ? "" : CsvFile.encodeCell(rb.association.getName())) + "; ";
}
csv += rb.isHidden() + "; ";
out.append(csv).append(LF);
for (RowBrowser child : tableBrowsers) {
if (child.parent == rb) {
storeSession(child, browserNumber, out);
}
}
}
}
/**
* Restores browser session.
*/
public void restoreSession(RowBrowser toBeAppended) {
File startDir = new File("layout");
Component pFrame = SwingUtilities.getWindowAncestor(this);
if (pFrame == null) {
pFrame = this;
}
String sFile = UIUtil.choseFile(null, startDir.getPath(), toBeAppended == null ? "Restore Layout" : "Append Layout", ".dbl", pFrame, true, true);
if (sFile != null) {
try {
restoreSession(toBeAppended, pFrame, sFile);
if (toBeAppended == null) {
currentSessionFileName = sFile;
}
} catch (Throwable e) {
UIUtil.showException(this, "Error", e, session);
}
}
}
/**
* Restores browser session.
*/
private void restoreSession(RowBrowser toBeAppended, Component pFrame, String sFile) throws Exception {
String tbaPeerID = null;
Map<String, RowBrowser> rbByID = new HashMap<String, Desktop.RowBrowser>();
List<Line> lines = new CsvFile(new File(sFile)).getLines();
if (toBeAppended == null) {
closeAll();
}
Collection<RowBrowser> toBeLoaded = new ArrayList<Desktop.RowBrowser>();
List<String> unknownTables = new ArrayList<String>();
for (CsvFile.Line l : lines) {
if (l.cells.get(0).equals("Layout")) {
try {
if (toBeAppended == null) {
layoutMode = LayoutMode.valueOf(l.cells.get(1));
updateMenu(layoutMode);
}
} catch (Exception e) {
e.printStackTrace();
}
continue;
}
String id = l.cells.get(0);
String parent = l.cells.get(1);
String where = l.cells.get(2);
Point loc = new Point(Integer.parseInt(l.cells.get(3)), Integer.parseInt(l.cells.get(4)));
Dimension size = new Dimension(Integer.parseInt(l.cells.get(5)), Integer.parseInt(l.cells.get(6)));
int limit = Integer.parseInt(l.cells.get(7));
boolean selectDistinct = Boolean.parseBoolean(l.cells.get(8));
RowBrowser rb = null;
if ("T".equals(l.cells.get(9))) {
Table table = datamodel.get().getTable(l.cells.get(10));
if (table == null) {
unknownTables.add(l.cells.get(10));
} else {
Association association = datamodel.get().namedAssociations.get(l.cells.get(11));
RowBrowser parentRB = rbByID.get(parent);
if (association == null) {
parentRB = null;
}
boolean add = true;
if (toBeAppended != null) {
if (tbaPeerID == null) {
add = false;
if (parent.trim().length() == 0 && table.equals(toBeAppended.browserContentPane.table)) {
tbaPeerID = id;
}
} else {
if (tbaPeerID.equals(parent)) {
parentRB = toBeAppended;
} else if (!rbByID.containsKey(parent)) {
add = false;
}
}
}
if (add) {
rb = addTableBrowser(parentRB, -1, table, parentRB != null ? association : null, where, limit, selectDistinct, false);
if (id.length() > 0) {
rbByID.put(id, rb);
}
if (parentRB == null || parentRB == toBeAppended) {
toBeLoaded.add(rb);
}
}
}
} else {
if (toBeAppended == null) {
rb = addTableBrowser(null, 0, null, null, where, limit, selectDistinct, false);
toBeLoaded.add(rb);
}
}
if (rb != null) {
rb.setHidden(Boolean.parseBoolean(l.cells.get(12)));
if (toBeAppended == null) {
rb.internalFrame.setLocation(loc);
rb.internalFrame.setSize(size);
}
}
}
checkDesktopSize();
makePrimaryRootVisible();
for (RowBrowser rb : toBeLoaded) {
rb.browserContentPane.reloadRows();
}
if (toBeAppended != null && toBeLoaded.isEmpty()) {
JOptionPane.showMessageDialog(pFrame,
"Layout doesn't contain table \"" + datamodel.get().getDisplayName(toBeAppended.browserContentPane.table) + "\" as root.");
} else if (!unknownTables.isEmpty()) {
String pList = "";
for (String ut : unknownTables) {
pList += ut + "\n";
}
JOptionPane.showMessageDialog(pFrame, "Unknown tables:\n\n" + pList + "\n");
}
}
private void makePrimaryRootVisible() {
RowBrowser root = null;
for (RowBrowser rb : getRootBrowsers(true)) {
if (rb.browserContentPane.table != null) {
if (!(rb.browserContentPane.table instanceof BrowserContentPane.SqlStatementTable)) {
root = rb;
break;
}
}
}
if (root != null) {
try {
root.internalFrame.setSelected(true);
} catch (PropertyVetoException e) {
// ignore
}
this.scrollToCenter(root.internalFrame);
} else {
this.scrollRectToVisible(new Rectangle(0, 0, 1, 1));
}
}
public JInternalFrame[] getAllFramesFromTableBrowsers() {
List<JInternalFrame> frames = new ArrayList<JInternalFrame>();
for (RowBrowser rb : tableBrowsers) {
frames.add(rb.internalFrame);
}
return frames.toArray(new JInternalFrame[frames.size()]);
}
public List<RowBrowser> getRootBrowsers(boolean ignoreHidden) {
List<RowBrowser> roots = new ArrayList<Desktop.RowBrowser>();
if (ignoreHidden) {
for (RowBrowser rb : tableBrowsers) {
if (!rb.isHidden()) {
RowBrowser p = rb.parent;
while (p != null && p.isHidden()) {
p = p.parent;
}
if (p == null) {
roots.add(rb);
}
}
}
} else {
for (RowBrowser rb : tableBrowsers) {
if (rb.parent == null) {
roots.add(rb);
}
}
}
return roots;
}
public List<RowBrowser> getBrowsers() {
return new ArrayList<Desktop.RowBrowser>(tableBrowsers);
}
public List<RowBrowser> getChildBrowsers(RowBrowser parent, boolean ignoreHidden) {
List<RowBrowser> roots = new ArrayList<Desktop.RowBrowser>();
if (ignoreHidden) {
for (RowBrowser rb : tableBrowsers) {
if (rb.parent == parent) {
if (rb.isHidden()) {
roots.addAll(getChildBrowsers(rb, true));
} else {
roots.add(rb);
}
}
}
} else {
for (RowBrowser rb : tableBrowsers) {
if (rb.parent == parent) {
roots.add(rb);
}
}
}
return roots;
}
/**
* Adjusts scroll-position of each table browser s.t. rows in closure are
* visible.
*
* @param tabu
* don't adjust this one
*/
protected synchronized void adjustClosure(BrowserContentPane tabu) {
for (RowBrowser rb : tableBrowsers) {
if (rb.browserContentPane == tabu) {
continue;
}
List<Row> rowsOfRB = new ArrayList<Row>();
for (Pair<BrowserContentPane, Row> r : currentClosure) {
if (r.a == rb.browserContentPane) {
rowsOfRB.add(r.b);
}
}
if (!rowsOfRB.isEmpty()) {
Rectangle firstRowPos = null;
Rectangle lastRowPos = null;
Rectangle visibleRect = rb.browserContentPane.rowsTable.getVisibleRect();
for (Row r : rowsOfRB) {
int index = rb.browserContentPane.rows.indexOf(r);
if (index < 0) {
for (int n = 0; n < rb.browserContentPane.rows.size(); ++n) {
if (r.rowId.equals(rb.browserContentPane.rows.get(n).rowId)) {
index = n;
break;
}
}
}
if (index < 0) {
// not visible due to distinct selection
continue;
}
index = rb.browserContentPane.rowsTable.getRowSorter().convertRowIndexToView(index);
Rectangle pos = rb.browserContentPane.rowsTable.getCellRect(index, 0, false);
if (pos.y >= visibleRect.y && pos.y + pos.height < visibleRect.y + visibleRect.height) {
// already a visible row
firstRowPos = null;
lastRowPos = null;
break;
}
if (firstRowPos == null || firstRowPos.y > pos.y) {
firstRowPos = pos;
}
if (lastRowPos == null || lastRowPos.y < pos.y) {
lastRowPos = pos;
}
}
if (lastRowPos != null) {
rb.browserContentPane.rowsTable
.scrollRectToVisible(new Rectangle(visibleRect.x, lastRowPos.y - lastRowPos.height, 1, 3 * lastRowPos.height));
}
if (firstRowPos != null) {
rb.browserContentPane.rowsTable.scrollRectToVisible(new Rectangle(visibleRect.x, firstRowPos.y - firstRowPos.height, 1,
3 * firstRowPos.height));
}
}
}
repaintDesktop();
}
/**
* Opens new Browser and adds complete sub-tree of {@link RowBrowser}.
*
* @param tableBrowser
* the root
*/
private void showInNewWindow(RowBrowser tableBrowser) {
DataBrowser newDataBrowser = openNewDataBrowser();
if (newDataBrowser != null) {
newDataBrowser.desktop.layoutMode = layoutMode;
newDataBrowser.desktop.updateMenu(layoutMode);
StringBuilder cond = new StringBuilder();
Set<String> known = new HashSet<String>();
synchronized (this) {
for (Row r : tableBrowser.browserContentPane.rows) {
if (!known.contains(r.rowId)) {
known.add(r.rowId);
if (cond.length() > 0) {
cond.append(" or \n");
}
cond.append("(" + SqlUtil.replaceAliases(r.rowId, "A", "A") + ")");
}
}
}
RowBrowser root = addTableBrowserSubTree(newDataBrowser, tableBrowser, null, cond.toString());
root.browserContentPane.reloadRows();
newDataBrowser.arrangeLayout();
try {
JInternalFrame iFrame = root.internalFrame;
newDataBrowser.desktop.scrollToCenter(iFrame);
iFrame.setSelected(true);
iFrame.grabFocus();
} catch (PropertyVetoException e1) {
// ignore
}
}
}
private RowBrowser addTableBrowserSubTree(DataBrowser newDataBrowser, RowBrowser tableBrowser, RowBrowser parent, String rootCond) {
Object limitO = tableBrowser.browserContentPane.limitBox.getSelectedItem();
Integer limit = null;
if (limitO instanceof Integer) {
limit = (Integer) limitO;
}
RowBrowser rb;
if (parent == null) {
rb = newDataBrowser.desktop.addTableBrowser(null, -1, tableBrowser.browserContentPane.table, null,
rootCond == null ? tableBrowser.browserContentPane.getAndConditionText() : rootCond, limit,
tableBrowser.browserContentPane.selectDistinctCheckBox.isSelected(), false);
} else {
rb = newDataBrowser.desktop.addTableBrowser(parent, tableBrowser.rowIndex, tableBrowser.browserContentPane.table,
tableBrowser.browserContentPane.association, rootCond == null ? tableBrowser.browserContentPane.getAndConditionText() : rootCond, limit,
tableBrowser.browserContentPane.selectDistinctCheckBox.isSelected(), false);
}
rb.setHidden(tableBrowser.isHidden());
for (RowBrowser child : getChildBrowsers(tableBrowser, false)) {
addTableBrowserSubTree(newDataBrowser, child, rb, null);
}
return rb;
}
protected abstract DataBrowser openNewDataBrowser();
/**
* Scrolls an iFrame to the center of the desktop.
*/
public void scrollToCenter(JInternalFrame iFrame) {
demaximize();
int w = getVisibleRect().width;
int h = getVisibleRect().height;
int x = iFrame.getBounds().x + iFrame.getBounds().width / 2 - getVisibleRect().width / 2;
int y = iFrame.getBounds().y + iFrame.getBounds().height / 2 - getVisibleRect().height / 2;
if (x < 0) {
w += x;
x = 0;
}
if (y < 0) {
h += y;
y = 0;
}
Rectangle r = new Rectangle(x, y, Math.max(1, w), Math.max(1, h));
scrollRectToVisible(r);
}
/**
* Collect layout of tables in a extraction model.
*
* @param positions
* to put positions into
*/
private void collectPositions(RowBrowser root, Map<String, Map<String, double[]>> positions) {
List<Pair<RowBrowser, Pair<Integer, Integer>>> toDo = new LinkedList<Pair<RowBrowser, Pair<Integer, Integer>>>();
toDo.add(new Pair<RowBrowser, Pair<Integer, Integer>>(root, new Pair<Integer, Integer>(1, 1)));
String subject = root.browserContentPane.table.getName(); // datamodel.get().getDisplayName(root.browserContentPane.table);
double scaleX = 0.35 / layoutMode.factor;
double scaleY = 0.3 / layoutMode.factor;
double scher = 2;
while (!toDo.isEmpty()) {
Pair<RowBrowser, Pair<Integer, Integer>> rowBrowser = toDo.remove(0);
int i = 1;
for (RowBrowser child : getChildBrowsers(rowBrowser.a, true)) {
toDo.add(new Pair<RowBrowser, Pair<Integer, Integer>>(child, new Pair<Integer, Integer>(rowBrowser.b.a + 1, i++)));
}
String table = rowBrowser.a.browserContentPane.table.getName(); // datamodel.get().getDisplayName(rowBrowser.a.browserContentPane.table);
Map<String, double[]> tablePos = positions.get(subject);
if (tablePos == null) {
tablePos = new TreeMap<String, double[]>();
positions.put(subject, tablePos);
}
if (!tablePos.containsKey(table)) {
double x = rowBrowser.a.internalFrame.getX();
double y = rowBrowser.a.internalFrame.getY();
tablePos.put(table, new double[] { x * scaleX + scher * (2 * (rowBrowser.b.b % 2) - 1), y * scaleY + scher * (2 * (rowBrowser.b.a % 2) - 1),
1.0 });
// } else {
// double[] pos = tablePos.get(table);
// tablePos.put(table, new double[] { pos[0], pos[1], 0.0 });
}
}
}
/**
* For concurrent reload of rows.
*/
private final PriorityBlockingQueue<RunnableWithPriority> runnableQueue = new PriorityBlockingQueue<RunnableWithPriority>(100,
new Comparator<RunnableWithPriority>() {
@Override
public int compare(RunnableWithPriority o1, RunnableWithPriority o2) {
return o2.getPriority() - o1.getPriority();
}
});
/**
* Maximum number of concurrent DB connections.
*/
private static final int MAX_CONCURRENT_CONNECTIONS = 6;
{
// initialize listeners for #runnableQueue
for (int i = 0; i < MAX_CONCURRENT_CONNECTIONS; ++i) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
for (;;) {
RunnableWithPriority take = null;
try {
take = runnableQueue.take();
take.run();
} catch (InterruptedException e) {
// ignore
} catch (CancellationException e) {
// ignore
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}, "PQueue Worker " + i);
t.setDaemon(true);
t.start();
}
}
}