org.bidib.wizard.mvc.netdebug.view.NetDebugView Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-client Show documentation
Show all versions of bidibwizard-client Show documentation
jBiDiB BiDiB Wizard Client Application POM
package org.bidib.wizard.mvc.netdebug.view;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.wizard.api.locale.Resources;
import org.bidib.wizard.client.common.component.TextLineNumber;
import org.bidib.wizard.client.common.converter.StringConverter;
import org.bidib.wizard.client.common.text.InputValidationDocument;
import org.bidib.wizard.client.common.text.WizardComponentFactory;
import org.bidib.wizard.client.common.view.BasicPopupMenu;
import org.bidib.wizard.client.common.view.DockKeys;
import org.bidib.wizard.common.model.settings.WizardSettingsInterface;
import org.bidib.wizard.common.service.SettingsService;
import org.bidib.wizard.common.utils.ImageUtils;
import org.bidib.wizard.core.dialog.FileDialog;
import org.bidib.wizard.mvc.common.view.text.ChangeDocumentListener;
import org.bidib.wizard.mvc.common.view.text.CopyAllAction;
import org.bidib.wizard.mvc.common.view.text.HistoryModel;
import org.bidib.wizard.mvc.common.view.text.HistoryTextField;
import org.bidib.wizard.mvc.netdebug.controller.listener.NetDebugControllerListener;
import org.bidib.wizard.mvc.netdebug.model.NetDebugModel;
import org.bidib.wizard.mvc.netdebug.view.listener.NetDebugViewListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jgoodies.binding.adapter.Bindings;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.beans.PropertyConnector;
import com.jgoodies.binding.value.ConverterValueModel;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.builder.FormBuilder;
import com.jgoodies.forms.debug.FormDebugPanel;
import com.jgoodies.forms.factories.Paddings;
import com.jidesoft.swing.DefaultOverlayable;
import com.jidesoft.swing.JideScrollPane;
import com.jidesoft.swing.StyledLabelBuilder;
import com.vlsolutions.swing.docking.DockKey;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockableState;
import com.vlsolutions.swing.docking.DockingDesktop;
import com.vlsolutions.swing.docking.event.DockableStateChangeEvent;
import com.vlsolutions.swing.docking.event.DockableStateChangeListener;
public class NetDebugView implements Dockable {
private static final Logger LOGGER = LoggerFactory.getLogger(NetDebugView.class);
private static final String ENCODED_DIALOG_COLUMN_SPECS =
"pref, 3dlu, max(80dlu;pref), 3dlu, pref, 3dlu, pref, 3dlu, pref, 3dlu, fill:50dlu:grow, 3dlu, pref";
private static final String ENCODED_DIALOG_ROW_SPECS = "p, 3dlu, p, 3dlu, fill:50dlu:grow, 3dlu, p, 3dlu, p";
private final SettingsService settingsService;
private final NetDebugModel netDebugModel;
private final Collection listeners = new LinkedList<>();
private DockableStateChangeListener dockableStateChangeListener;
private final JComponent contentPanel;
private final JButton connectButton = new JButton(Resources.getString(getClass(), "connect"));
private final JButton disconnectButton = new JButton(Resources.getString(getClass(), "disconnect"));
private ValueModel hostValueModel;
private ValueModel portValueModel;
private JTextField host;
private JTextField port;
private HistoryTextField sendText;
private ValueModel sendTextValueModel;
private final JButton transmitButton = new JButton(Resources.getString(getClass(), "transmit"));
private TextLineNumber tln;
private final JTextArea logsArea = new JTextArea();
public NetDebugView(final DockingDesktop desktop, final NetDebugControllerListener listener,
final NetDebugModel netDebugModel, final SettingsService settingsService) {
this.settingsService = settingsService;
this.netDebugModel = netDebugModel;
DockKeys.DOCKKEY_NET_DEBUG_VIEW.setName(Resources.getString(getClass(), "title"));
DockKeys.DOCKKEY_NET_DEBUG_VIEW.setFloatEnabled(true);
DockKeys.DOCKKEY_NET_DEBUG_VIEW.setAutoHideEnabled(false);
this.dockableStateChangeListener = new DockableStateChangeListener() {
@Override
public void dockableStateChanged(DockableStateChangeEvent event) {
LOGGER
.info("The state has changed, newState: {}, prevState: {}", event.getNewState(),
event.getPreviousState());
DockableState newState = event.getNewState();
if (newState.getDockable().equals(NetDebugView.this) && newState.isClosed()) {
LOGGER.info("The NetDebugView is closed.");
// we are closed
try {
desktop.removeDockableStateChangeListener(dockableStateChangeListener);
}
catch (Exception ex) {
LOGGER
.warn("Remove dockableStateChangeListener from desktop failed: "
+ dockableStateChangeListener, ex);
}
finally {
dockableStateChangeListener = null;
}
if (listener != null) {
LOGGER.info("Close the view.");
listener.viewClosed();
}
}
}
};
desktop.addDockableStateChangeListener(dockableStateChangeListener);
LOGGER.info("Create new NetDebugView");
// create form builder
FormBuilder dialogBuilder = null;
boolean debugDialog = false;
if (debugDialog) {
JPanel panel = new FormDebugPanel();
dialogBuilder =
FormBuilder.create().columns(ENCODED_DIALOG_COLUMN_SPECS).rows(ENCODED_DIALOG_ROW_SPECS).panel(panel);
}
else {
JPanel panel = new JPanel(new BorderLayout());
dialogBuilder =
FormBuilder.create().columns(ENCODED_DIALOG_COLUMN_SPECS).rows(ENCODED_DIALOG_ROW_SPECS).panel(panel);
}
dialogBuilder.border(Paddings.DIALOG);
// host
hostValueModel =
new PropertyAdapter(this.netDebugModel, NetDebugModel.PROPERTY_SELECTED_HOST, true);
host = WizardComponentFactory.createTextField(hostValueModel, false);
dialogBuilder.add(Resources.getString(getClass(), "selectedHost")).xy(1, 1);
dialogBuilder.add(host).xy(3, 1);
// port
portValueModel =
new PropertyAdapter(this.netDebugModel, NetDebugModel.PROPERTY_SELECTED_PORT, true);
final ValueModel portConverterModel =
new ConverterValueModel(portValueModel, new StringConverter(new DecimalFormat("#")));
// create the textfield for the port
port = new JTextField();
InputValidationDocument portDocument = new InputValidationDocument(5, InputValidationDocument.NUMERIC);
port.setDocument(portDocument);
port.setColumns(5);
// bind manually because we changed the document of the textfield
Bindings.bind(port, portConverterModel, false);
dialogBuilder.add(Resources.getString(getClass(), "selectedPort")).xy(5, 1);
dialogBuilder.add(port).xy(7, 1);
// prepare the connect and disconnect button
JPanel debugInterfaceActionButtons =
new ButtonBarBuilder().addButton(connectButton).addRelatedGap().addButton(disconnectButton).build();
dialogBuilder.add(debugInterfaceActionButtons).xy(9, 1);
// add the log area
logsArea.setEditable(false);
logsArea.setFont(new Font("Monospaced", Font.PLAIN, 13));
JScrollPane logsPane = new JScrollPane(logsArea);
tln = new TextLineNumber(logsArea);
logsPane.setRowHeaderView(tln);
logsPane.setAutoscrolls(true);
dialogBuilder.add(logsPane).xyw(1, 5, 13);
// create the textfield for send message to debug
sendTextValueModel =
new PropertyAdapter(this.netDebugModel, NetDebugModel.PROPERTY_SEND_TEXT, true);
// sendText = BasicComponentFactory.createTextField(sendTextValueModel, false);
// set default 20 items in history
HistoryModel.setMax(20);
sendText = new HistoryTextField("sendText", false, true);
Bindings.bind(sendText, sendTextValueModel, false);
sendText.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireTransmit();
}
});
final DefaultOverlayable sendTextOverlayable = new DefaultOverlayable(sendText);
sendTextOverlayable
.addOverlayComponent(
StyledLabelBuilder
.createStyledLabel("{" + Resources.getString(getClass(), "transmitText.prompt") + ":f:gray}"),
SwingConstants.WEST);
sendTextOverlayable.setOverlayLocationInsets(new Insets(0, -5, 0, 5));
final Document textDoc = sendText.getDocument();
textDoc
.addDocumentListener(
new ChangeDocumentListener(doc -> sendTextOverlayable.setOverlayVisible(doc.getLength() < 1)));
sendTextOverlayable.setOverlayVisible(textDoc.getLength() < 1);
dialogBuilder.add(Resources.getString(getClass(), "transmitText")).xy(1, 7);
dialogBuilder.add(sendTextOverlayable).xyw(3, 7, 9);
dialogBuilder.add(transmitButton).xy(13, 7);
transmitButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
sendText.addCurrentToHistory();
fireTransmit();
}
});
transmitButton.setEnabled(false);
// add bindings for enable/disable the send button
PropertyConnector
.connect(this.netDebugModel, NetDebugModel.PROPERTY_TRANSMIT_ENABLED, transmitButton, "enabled");
// PropertyConnector
// .connect(netDebugModel, NetDebugModel.PROPERTY_TRANSMIT_ENABLED, transmitFileButton, "enabled");
PropertyConnector.connect(this.netDebugModel, NetDebugModel.PROPERTY_DISCONNECTED, connectButton, "enabled");
PropertyConnector.connect(this.netDebugModel, NetDebugModel.PROPERTY_CONNECTED, disconnectButton, "enabled");
JPanel contentPanelTemp = dialogBuilder.build();
JideScrollPane scrollPane = new JideScrollPane(contentPanelTemp);
contentPanel = scrollPane;
connectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireConnect();
}
});
disconnectButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireDisconnect();
}
});
disconnectButton.setEnabled(false);
JPopupMenu popupMenu = new BasicPopupMenu();
JMenuItem clearConsole = new JMenuItem(Resources.getString(getClass(), "clear_console"));
clearConsole.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireClearConsole();
}
});
popupMenu.add(clearConsole);
JMenuItem copyAllToClipboard = new JMenuItem(Resources.getString(getClass(), "copyAllToClipboard"));
copyAllToClipboard.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireCopyAllToClipboard();
}
});
popupMenu.add(copyAllToClipboard);
JMenuItem saveToFile =
new JMenuItem(Resources.getString(getClass(), "save_to_file"),
ImageUtils.createImageIcon(getClass(), "/icons/savetofile.png"));
saveToFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireSaveToFile(false);
}
});
popupMenu.add(saveToFile);
JMenuItem saveSelectedToFile =
new JMenuItem(Resources.getString(getClass(), "save_selected_to_file"),
ImageUtils.createImageIcon(getClass(), "/icons/saveselectedtofile.png"));
saveSelectedToFile.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireSaveToFile(true);
}
});
popupMenu.add(saveSelectedToFile);
logsArea.setComponentPopupMenu(popupMenu);
}
@Override
public DockKey getDockKey() {
return DockKeys.DOCKKEY_NET_DEBUG_VIEW;
}
@Override
public Component getComponent() {
return contentPanel;
}
public void addNetDebugViewListener(NetDebugViewListener listener) {
listeners.add(listener);
}
private void fireConnect() {
for (NetDebugViewListener listener : listeners) {
listener.openConnection();
}
}
private void fireDisconnect() {
for (NetDebugViewListener listener : listeners) {
listener.closeConnection();
}
}
private void fireTransmit() {
for (NetDebugViewListener listener : listeners) {
listener.transmit();
}
// clear the text field
sendText.setText(null);
}
private void fireClearConsole() {
LOGGER.info("clear the console.");
logsArea.setText(null);
}
private void fireCopyAllToClipboard() {
LOGGER.info("Copy all content to clipboard.");
ActionEvent copyAll = new ActionEvent(logsArea, 0, CopyAllAction.copyAllAction);
CopyAllAction action = new CopyAllAction();
action.actionPerformed(copyAll);
}
private static FileFilter logfileFilter;
private static final String LOGFILE_EXTENSION = "log";
private static final String WORKING_DIR_NET_DEBUG_LOG_KEY = "netDebugLog";
// description, suffix for node files
private String savedLogFilesDescription;
private void fireSaveToFile(final boolean selectedOnly) {
LOGGER.info("Save the console content to file.");
savedLogFilesDescription = Resources.getString(getClass(), "savedLogFilesDescription");
logfileFilter = new FileNameExtensionFilter(savedLogFilesDescription, LOGFILE_EXTENSION);
final WizardSettingsInterface wizardSettings = settingsService.getWizardSettings();
String storedWorkingDirectory = wizardSettings.getWorkingDirectory(WORKING_DIR_NET_DEBUG_LOG_KEY);
FileDialog dialog = new FileDialog(logsArea, FileDialog.SAVE, storedWorkingDirectory, null, logfileFilter) {
@Override
public void approve(final String fileName) {
try {
// setWaitCursor();
LOGGER.info("Start saving logfile, fileName: {}", fileName);
File file = new File(fileName);
if (selectedOnly) {
FileUtils.write(file, logsArea.getSelectedText(), Charset.forName("UTF-8"));
}
else {
FileUtils.write(file, logsArea.getText(), Charset.forName("UTF-8"));
}
final String workingDir = Paths.get(fileName).getParent().toString();
LOGGER.info("Save current workingDir: {}", workingDir);
wizardSettings.setWorkingDirectory(WORKING_DIR_NET_DEBUG_LOG_KEY, workingDir);
}
catch (Exception ex) {
LOGGER.warn("Save logfile failed.", ex);
throw new RuntimeException("Save logfile failed.");
}
finally {
// setDefaultCursor();
}
}
};
dialog.showDialog();
}
public void addLog(final String logMessage) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
addLogMessage(logMessage);
}
});
}
private final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
public static final String LOGGER_PANE_NAME = "DebugInterfacePane";
private static final Logger LOGGER_PANE = LoggerFactory.getLogger(LOGGER_PANE_NAME);
StringBuilder sbLogger = new StringBuilder();
// int lenOfTimestamp = 0;
private int currentLineLen;
private boolean timestampsEnabled;
private void addLogMessage(final String logMessage) {
try {
int lines = logsArea.getLineCount();
if (lines > 500) {
// remove the first 50 lines
int end = logsArea.getLineEndOffset(/* lines - */50);
logsArea.getDocument().remove(0, end);
}
}
catch (BadLocationException ex) {
LOGGER.warn("Remove some lines from logsArea failed.", ex);
}
// ...........\n........
int beginIndex = 0;
int index = logMessage.indexOf('\n');
int lenOfTimestamp = 0;
if (index > -1) {
// found line terminator
while (index > -1) {
// print the whole line
String part = logMessage.substring(beginIndex, index);
// add text to scroll pane
if (timestampsEnabled && currentLineLen == 0) {
// add the timestamp to the line
sbLogger.append(sdf.format(new Date()));
sbLogger.append(" - ");
lenOfTimestamp = 12;
}
sbLogger.append(part);
LOGGER.debug("1. Added part: {}, currentLineLen: {}, sbLogger: {}", part, currentLineLen, sbLogger);
// the line is complete
logsArea.append(sbLogger.substring(currentLineLen));
logsArea.append("\n"/* System.lineSeparator() */);
LOGGER_PANE.info(sbLogger.toString().replace("\r", ""));
lenOfTimestamp = 0;
sbLogger.setLength(0);
currentLineLen = 0;
beginIndex = index + 1;
index = logMessage.indexOf('\n', beginIndex);
}
lenOfTimestamp = 0;
if (beginIndex < logMessage.length()) {
// add text to scroll pane
String part = logMessage.substring(beginIndex);
if (timestampsEnabled) {
sbLogger.append(sdf.format(new Date()));
sbLogger.append(" - ");
lenOfTimestamp = 12;
}
sbLogger.append(part);
LOGGER.debug("2. Added part: {}, currentLineLen: {}, sbLogger: {}", part, currentLineLen, sbLogger);
logsArea.append(sbLogger.substring(currentLineLen));
currentLineLen = part.length() + lenOfTimestamp;
lenOfTimestamp = 0;
LOGGER.debug("Added last part: {}, currentLineLen: {}", part, currentLineLen);
}
}
else {
// add text to scroll pane
if (timestampsEnabled && currentLineLen == 0) {
LOGGER.debug("Add timestamp, currentLineLen: {}", currentLineLen);
sbLogger.append(sdf.format(new Date()));
sbLogger.append(" - ");
lenOfTimestamp = 12;
// currentLineLen += lenOfTimestamp;
}
String toLog = logMessage;
// split the received data if necessary
if (logMessage.length() > (120 - currentLineLen)) {
int start = 0;
int end = 0;
String part = null;
toLog = null;
end = start + (120 - currentLineLen);
while (end < logMessage.length()) {
// end = start + (120 - currentLineLen);
part = StringUtils.substring(logMessage, start, end);
LOGGER.debug("Fetched part, currentLineLen {}, part: '{}'", currentLineLen, part);
sbLogger.append(part);
logsArea.append(sbLogger.substring(currentLineLen));
logsArea.append("\n");
currentLineLen = 120;
LOGGER.debug("Added logMessage: {}, currentLineLen: {}", logMessage, currentLineLen);
currentLineLen = 0;
sbLogger.setLength(0);
start += 120;
end = start + (120 - currentLineLen);
}
if (start < logMessage.length()) {
LOGGER.debug("Keep the remaining part from logMessage, currentLineLen: {}", currentLineLen, start);
toLog = StringUtils.substring(logMessage, start);
}
}
if (StringUtils.isNotBlank(toLog)) {
sbLogger.append(toLog);
LOGGER
.debug("3. Added logMessage: {}, currentLineLen: {}, sbLogger: {}", toLog, currentLineLen,
sbLogger);
logsArea.append(sbLogger.substring(currentLineLen));
currentLineLen += toLog.length() + lenOfTimestamp;
LOGGER.debug("Added logMessage: {}, currentLineLen: {}", logMessage, currentLineLen);
}
}
// Update and scroll pane to the bottom
if (currentLineLen > 120) {
LOGGER.debug("Append new line to logsArea, currentLineLen: {}", currentLineLen);
// break the line
// logsArea.append(sb.toString());
logsArea.append("\n" /* System.lineSeparator() */);
LOGGER_PANE.info(sbLogger.toString().replace("\r", ""));
lenOfTimestamp = 0;
sbLogger.setLength(0);
currentLineLen = 0;
}
logsArea.invalidate();
}
}