package com.dgex.offspring.ui;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import nxt.Block;
import nxt.Constants;
import nxt.Nxt;
import nxt.util.Convert;
import org.apache.log4j.Logger;
import org.eclipse.e4.ui.di.UISynchronize;
import org.eclipse.e4.ui.services.IStylingEngine;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;
import org.json.simple.JSONObject;
import com.dgex.offspring.config.IContactsService;
import com.dgex.offspring.config.JSonWriter;
import com.dgex.offspring.nxtCore.service.INxtService;
import com.dgex.offspring.user.service.IUserService;
public class InspectBlockDialog extends TitleAreaDialog {
static final Logger logger = Logger.getLogger(InspectBlockDialog.class);
private final List<Long> history = new ArrayList<Long>();
private int historyCursor = 0;
private Long blockId;
private Composite container;
private Button previousButton;
private Button nextButton;
private Label timeLabel;
private Link generatorLink;
private Label heightLabel;
private Label amountLabel;
private Label feeLabel;
private Text hashText;
private Text jsonText;
private Label baseTargetLabel;
private Label difficultyLabel;
private Text generationSignatureText;
private Text blockSignatureText;
private BlockTransactionViewer viewer;
private TabFolder tabFolder;
private TabItem jsonTab;
private TabItem transactionsTab;
private final IStylingEngine engine;
private final INxtService nxt;
private final IUserService userService;
private final UISynchronize sync;
private final IContactsService contactsService;
static InspectBlockDialog INSTANCE = null;
static final SimpleDateFormat dateFormat = new SimpleDateFormat(
"dd MMM yy H:mm:ss");
public InspectBlockDialog(Shell shell, Long blockId, INxtService nxt,
IStylingEngine engine, IUserService userService, UISynchronize sync,
IContactsService contactsService) {
super(shell);
this.blockId = blockId;
this.engine = engine;
this.nxt = nxt;
this.userService = userService;
this.sync = sync;
this.contactsService = contactsService;
}
@Override
protected void setShellStyle(int newShellStyle) {
super.setShellStyle(SWT.CLOSE | SWT.MODELESS | SWT.BORDER | SWT.TITLE
| SWT.RESIZE);
setBlockOnOpen(false);
}
@Override
public void create() {
super.create();
setTitle("Block " + Convert.toUnsignedLong(blockId));
setMessage("");
}
/**
* Static method that opens a new dialog or switches the existing dialog to
* another block id. The dialog shows back and forward buttons to navigate
* between accounts inspected.
*
* @param blockId
* @return
*/
public static void show(final Long blockId, final INxtService nxt,
final IStylingEngine engine, final IUserService userService,
final UISynchronize sync, final IContactsService contactsService) {
sync.syncExec(new Runnable() {
@Override
public void run() {
Shell shell = Display.getCurrent().getActiveShell();
if (shell != null) {
while (shell.getParent() != null) {
shell = shell.getParent().getShell();
}
}
if (INSTANCE == null) {
INSTANCE = new InspectBlockDialog(shell, blockId, nxt, engine,
userService, sync, contactsService);
INSTANCE.history.add(blockId);
INSTANCE.historyCursor = 0;
INSTANCE.open();
}
else {
INSTANCE.history.add(blockId);
INSTANCE.historyCursor = INSTANCE.history.size() - 1;
INSTANCE.setBlockId(blockId);
INSTANCE.getShell().forceActive();
}
}
});
}
@Override
public boolean close() {
INSTANCE = null;
return super.close();
}
public void setBlockId(Long blockId) {
this.blockId = blockId;
Block block = Nxt.getBlockchain().getBlock(blockId);
if (block == null) {
logger.warn("Cannot open non-existing block");
return;
}
setTitle("Block " + Convert.toUnsignedLong(blockId));
setMessage("Contains " + block.getTransactionIds().size()
+ " transactions (v" + block.getVersion() + ")");
updateNavigateButtons();
timeLabel.setText(dateFormat.format(convertTimestamp(block.getTimestamp()))
+ " (" + block.getTimestamp() + ")");
generatorLink.setText("<A>"
+ Convert.toUnsignedLong(block.getGeneratorId()) + "</A>");
baseTargetLabel.setText(Long.toString(block.getBaseTarget()));
heightLabel.setText(Integer.toString(block.getHeight()));
difficultyLabel.setText(block.getCumulativeDifficulty().toString());
amountLabel.setText(Integer.toString(block.getTotalAmount()));
feeLabel.setText(Integer.toString(block.getTotalFee()));
hashText.setText(Convert.toHexString(block.getPayloadHash()));
generationSignatureText.setText(Convert.toHexString(block
.getGenerationSignature()));
blockSignatureText.setText(Convert.toHexString(block.getBlockSignature()));
viewer.setInput(blockId);
jsonText.setText(prettyPrint(block.getJSONObject()));
timeLabel.pack();
generatorLink.pack();
baseTargetLabel.pack();
heightLabel.pack();
difficultyLabel.pack();
amountLabel.pack();
feeLabel.pack();
hashText.pack();
generationSignatureText.pack();
blockSignatureText.pack();
container.layout();
}
private void updateNavigateButtons() {
previousButton.setEnabled(historyCursor > 0);
nextButton.setEnabled(historyCursor < (history.size() - 1));
}
private Date convertTimestamp(int timestamp) {
return new Date((timestamp * 1000l) + (Constants.EPOCH_BEGINNING - 500L));
}
private void labelData(Control control) {
GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER)
.applyTo(control);
}
private void fieldData(Control control) {
GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.CENTER)
.grab(true, false).applyTo(control);
}
@Override
protected Control createDialogArea(Composite parent) {
initializeDialogUnits(parent);
Composite outerContainer = (Composite) super.createDialogArea(parent);
GridLayout layout = new GridLayout(4, false);
layout.horizontalSpacing = 15;
layout.marginTop = 10;
layout.marginLeft = 10;
GridData gd = new GridData(GridData.FILL, GridData.FILL, true, true);
gd.widthHint = Math
.round((convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH) / 2) * 3);
container = new Composite(outerContainer, SWT.NONE);
container.setLayoutData(gd);
container.setLayout(layout);
Label label = new Label(container, SWT.NONE);
label.setText("Time");
labelData(label);
timeLabel = new Label(container, SWT.NONE);
fieldData(timeLabel);
Composite navigationComposite = new Composite(container, SWT.NONE);
GridDataFactory.fillDefaults().span(2, 1).grab(true, false)
.align(SWT.END, SWT.CENTER).applyTo(navigationComposite);
navigationComposite.setLayout(new FillLayout());
previousButton = new Button(navigationComposite, SWT.PUSH);
previousButton.setEnabled(false);
previousButton.setText("<");
previousButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
historyCursor = Math.max(0, historyCursor - 1);
setBlockId(history.get(historyCursor));
}
});
nextButton = new Button(navigationComposite, SWT.PUSH);
nextButton.setEnabled(false);
nextButton.setText(">");
nextButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
historyCursor = Math.min(history.size() - 1, historyCursor + 1);
setBlockId(history.get(historyCursor));
}
});
// -----
label = new Label(container, SWT.NONE);
label.setText("Generator");
labelData(label);
generatorLink = new Link(container, SWT.NONE);
fieldData(generatorLink);
generatorLink.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
Block block = Nxt.getBlockchain().getBlock(blockId);
if (block != null) {
InspectAccountDialog.show(block.getGeneratorId(), nxt, engine,
userService, sync, contactsService);
}
}
});
label = new Label(container, SWT.NONE);
label.setText("Base Target");
labelData(label);
baseTargetLabel = new Label(container, SWT.NONE);
fieldData(baseTargetLabel);
// -----
label = new Label(container, SWT.NONE);
label.setText("Height");
labelData(label);
heightLabel = new Label(container, SWT.NONE);
fieldData(heightLabel);
label = new Label(container, SWT.NONE);
label.setText("Difficulty");
labelData(label);
difficultyLabel = new Label(container, SWT.NONE);
fieldData(difficultyLabel);
// -----
label = new Label(container, SWT.NONE);
label.setText("Amount");
labelData(label);
amountLabel = new Label(container, SWT.NONE);
fieldData(amountLabel);
label = new Label(container, SWT.NONE);
label.setText("Fee");
labelData(label);
feeLabel = new Label(container, SWT.NONE);
fieldData(feeLabel);
// -----
// payload hash
label = new Label(container, SWT.NONE);
label.setText("Hash");
labelData(label);
hashText = new Text(container, SWT.BORDER);
GridDataFactory.swtDefaults().span(3, 1).grab(true, false)
.align(SWT.FILL, SWT.CENTER).applyTo(hashText);
// -----
// generation signature
label = new Label(container, SWT.NONE);
label.setText("Generation Signature");
labelData(label);
generationSignatureText = new Text(container, SWT.BORDER);
GridDataFactory.swtDefaults().span(3, 1).grab(true, false)
.align(SWT.FILL, SWT.CENTER).applyTo(generationSignatureText);
// -----
// generation signature
label = new Label(container, SWT.NONE);
label.setText("Block Signature");
labelData(label);
blockSignatureText = new Text(container, SWT.BORDER);
GridDataFactory.swtDefaults().span(3, 1).grab(true, false)
.align(SWT.FILL, SWT.CENTER).applyTo(blockSignatureText);
// -----
tabFolder = new TabFolder(container, SWT.NONE);
transactionsTab = new TabItem(tabFolder, SWT.NONE);
transactionsTab.setText("Transactions");
jsonTab = new TabItem(tabFolder, SWT.NONE);
jsonTab.setText("JSON");
viewer = new BlockTransactionViewer(tabFolder, 0l, contactsService, nxt,
engine, userService, sync);
transactionsTab.setControl(viewer.getControl());
jsonText = new Text(tabFolder, SWT.MULTI | SWT.BORDER | SWT.WRAP
| SWT.V_SCROLL | SWT.H_SCROLL);
jsonTab.setControl(jsonText);
Point size = jsonText.computeSize(SWT.DEFAULT, SWT.DEFAULT);
GridDataFactory.swtDefaults().span(4, 1).grab(true, true)
.align(SWT.BEGINNING, SWT.CENTER).applyTo(tabFolder);
((GridData) tabFolder.getLayoutData()).minimumHeight = 220;
sync.asyncExec(new Runnable() {
@Override
public void run() {
setBlockId(blockId);
}
});
return outerContainer;
}
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.CLOSE_LABEL,
true);
}
@Override
protected boolean isResizable() {
return true;
}
private String prettyPrint(JSONObject object) {
JSonWriter writer = new JSonWriter();
try {
object.writeJSONString(writer);
return writer.toString();
}
catch (IOException e) {
logger.error("Could not serialize JSON", e);
}
finally {
try {
writer.close();
}
catch (IOException e) {}
}
return "ERROR";
}
}