/******************************************************************************* | |
* Copyright (c) 2020 RBEI and others. | |
* | |
* This program and the accompanying materials | |
* are made available under the terms of the Eclipse Public License v. 2.0 | |
* which accompanies this distribution, and is available at | |
* https://www.eclipse.org/legal/epl-2.0/ | |
* | |
* SPDX-License-Identifier: EPL-2.0 | |
* | |
* Contributors: | |
* Abirami Bhologa Indiran - Initial contribution and API | |
* Adhith Gopal | |
*******************************************************************************/ | |
package org.eclipse.blockchain.ui.views; | |
import java.io.IOException; | |
import java.util.HashSet; | |
import java.util.Set; | |
import org.eclipse.blockchain.core.BlockchainCore; | |
import org.eclipse.blockchain.core.BlockchainViewsRegistry; | |
import org.eclipse.blockchain.core.Web3jHandler; | |
import org.eclipse.blockchain.core.interfaces.IBlockchainView; | |
import org.eclipse.blockchain.core.model.TransactionModel; | |
import org.eclipse.blockchain.ui.Activator; | |
import org.eclipse.fx.ui.workbench3.FXViewPart; | |
import org.eclipse.ui.IViewSite; | |
import org.eclipse.ui.PartInitException; | |
import javafx.event.ActionEvent; | |
import javafx.scene.Scene; | |
import javafx.scene.control.Alert; | |
import javafx.scene.control.Alert.AlertType; | |
import javafx.scene.control.ContextMenu; | |
import javafx.scene.control.Label; | |
import javafx.scene.control.MenuItem; | |
import javafx.scene.control.OverrunStyle; | |
import javafx.scene.control.TableColumn; | |
import javafx.scene.control.TableRow; | |
import javafx.scene.control.TableView; | |
import javafx.scene.control.TextArea; | |
import javafx.scene.control.Tooltip; | |
import javafx.scene.control.cell.PropertyValueFactory; | |
import javafx.scene.layout.GridPane; | |
import javafx.scene.layout.Priority; | |
import javafx.scene.layout.VBox; | |
/** | |
* A Table representation of Transactions | |
*/ | |
public class TransactionHistoryView extends FXViewPart | |
implements | |
IBlockchainView { | |
/** | |
* Important Note - This view is added to the list of registered blockchain | |
* views in the Activator. Because this class won't be initialized until | |
* user manually activate's the view. To avoid data loss this view is added | |
* in Activator | |
*/ | |
/** | |
* These 2 are maintained as a static instance because FXViewPart creates | |
* one instance and we create another instance inactivator. This small | |
* glitch can be handled later | |
*/ | |
private static TableView<TransactionModel> table; | |
/** | |
* A temporary model object | |
*/ | |
private static Set<TransactionModel> tableContents = new HashSet<>(); | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public void init(final IViewSite site) throws PartInitException { | |
super.init(site); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
protected Scene createFxScene() { | |
TransactionHistoryView.table = new TableView<>(); | |
// Create the VBox | |
VBox root = new VBox(TransactionHistoryView.table); | |
TransactionHistoryView.table.setVisible(true); | |
TransactionHistoryView.table | |
.setPlaceholder(new Label("No transactions to display")); | |
TransactionHistoryView.table.setRowFactory(tv -> { | |
TableRow<TransactionModel> row = new TableRow<>(); | |
row.setTooltip(new Tooltip("Double click to view details")); | |
row.setTextOverrun(OverrunStyle.ELLIPSIS); | |
row.setOnMouseClicked(event -> { | |
if ((event.getClickCount() == 2) && !row.isEmpty()) { | |
TransactionModel transaction = row.getItem(); | |
String transactionDetailsMsg = "blockHash : " | |
+ transaction.getBlockHash() | |
+ System.lineSeparator() + "blockNumber : " | |
+ transaction.getBlockNumber() | |
+ System.lineSeparator() + "from : " | |
+ transaction.getFromAddress() | |
+ System.lineSeparator() + "gas : " | |
+ transaction.getGas() + System.lineSeparator() | |
+ "gasPrice : " + transaction.getGasPrice() | |
+ System.lineSeparator() + "hash : " | |
+ transaction.getTransactionHash() | |
+ System.lineSeparator() + "input : " | |
+ transaction.getInput() + System.lineSeparator() | |
+ "nonce : " + transaction.getNonce() | |
+ System.lineSeparator() + "r : " | |
+ transaction.getR() + System.lineSeparator() | |
+ "s : " + transaction.getS() | |
+ System.lineSeparator() + "to : " | |
+ transaction.getToAddress() | |
+ System.lineSeparator() + "transactionIndex : " | |
+ transaction.getTransactionIndex() | |
+ System.lineSeparator() + "v : " | |
+ transaction.getV() + System.lineSeparator() | |
+ "value : " + transaction.getValue() | |
+ System.lineSeparator(); | |
showAlert(transactionDetailsMsg, "Transaction details"); | |
} | |
}); | |
return row; | |
}); | |
TableColumn<TransactionModel, String> blockHashColumn = new TableColumn<>( | |
"Block Hash"); | |
blockHashColumn | |
.setCellValueFactory(new PropertyValueFactory<>("blockHash")); | |
TableColumn<TransactionModel, String> blockNumberColumn = new TableColumn<>( | |
"block Number"); | |
blockNumberColumn | |
.setCellValueFactory(new PropertyValueFactory<>("blockNumber")); | |
TableColumn<TransactionModel, String> fromAddressColumn = new TableColumn<>( | |
"From"); | |
fromAddressColumn | |
.setCellValueFactory(new PropertyValueFactory<>("fromAddress")); | |
TableColumn<TransactionModel, String> gasColumn = new TableColumn<>( | |
"Gas"); | |
gasColumn.setCellValueFactory(new PropertyValueFactory<>("gas")); | |
TableColumn<TransactionModel, String> gasPriceColumn = new TableColumn<>( | |
"Gas Price"); | |
gasPriceColumn | |
.setCellValueFactory(new PropertyValueFactory<>("gasPrice")); | |
TableColumn<TransactionModel, String> transactionHashColumn = new TableColumn<>( | |
"Hash"); | |
transactionHashColumn.setCellValueFactory( | |
new PropertyValueFactory<>("transactionHash")); | |
TableColumn<TransactionModel, String> inputColumn = new TableColumn<>( | |
"Input"); | |
inputColumn.setCellValueFactory(new PropertyValueFactory<>("input")); | |
TableColumn<TransactionModel, String> nonceColumn = new TableColumn<>( | |
"Nonce"); | |
nonceColumn.setCellValueFactory(new PropertyValueFactory<>("nonce")); | |
TableColumn<TransactionModel, String> rColumn = new TableColumn<>("R"); | |
rColumn.setCellValueFactory(new PropertyValueFactory<>("r")); | |
TableColumn<TransactionModel, String> sColumn = new TableColumn<>("S"); | |
sColumn.setCellValueFactory(new PropertyValueFactory<>("s")); | |
TableColumn<TransactionModel, String> toAddressColumn = new TableColumn<>( | |
"To"); | |
toAddressColumn | |
.setCellValueFactory(new PropertyValueFactory<>("toAddress")); | |
TableColumn<TransactionModel, String> transactionIndexColumn = new TableColumn<>( | |
"Transaction Index"); | |
transactionIndexColumn.setCellValueFactory( | |
new PropertyValueFactory<>("transactionIndex")); | |
TransactionHistoryView.table.getColumns().add(blockHashColumn); | |
TransactionHistoryView.table.getColumns().add(blockNumberColumn); | |
TransactionHistoryView.table.getColumns().add(fromAddressColumn); | |
TransactionHistoryView.table.getColumns().add(gasPriceColumn); | |
TransactionHistoryView.table.getColumns().add(transactionHashColumn); | |
TransactionHistoryView.table.getColumns().add(inputColumn); | |
TransactionHistoryView.table.getColumns().add(nonceColumn); | |
TransactionHistoryView.table.getColumns().add(rColumn); | |
TransactionHistoryView.table.getColumns().add(sColumn); | |
TransactionHistoryView.table.getColumns().add(toAddressColumn); | |
TransactionHistoryView.table.getColumns().add(transactionIndexColumn); | |
TransactionHistoryView.table.setTableMenuButtonVisible(true); | |
ContextMenu contextMenu = new ContextMenu(); | |
MenuItem debugItem = new MenuItem("Debug"); | |
MenuItem exportMenu = new MenuItem("Export"); | |
MenuItem transactionReceiptMenu = new MenuItem("TX Receipt"); | |
contextMenu.getItems().add(debugItem); | |
contextMenu.getItems().add(exportMenu); | |
contextMenu.getItems().add(transactionReceiptMenu); | |
transactionReceiptMenu.setOnAction((final ActionEvent event) -> { | |
try { | |
TransactionModel selectedItem = TransactionHistoryView.table | |
.getSelectionModel().getSelectedItem(); | |
showAlert( | |
Web3jHandler.getInstance() | |
.getTransactionReceiptAsString( | |
selectedItem.getTransactionHash()), | |
"Transaction receipt"); | |
} catch (IOException e) { | |
BlockchainCore.getInstance().logException(Activator.PLUGIN_ID, | |
"Error during transaction receipt generation", e); | |
} | |
}); | |
TransactionHistoryView.table.setContextMenu(contextMenu); | |
TransactionHistoryView.table.autosize(); | |
if (!tableContents.isEmpty()) { | |
TransactionHistoryView.table.getItems().addAll(tableContents); | |
} | |
// Create the Scene | |
Scene scene = new Scene(root); | |
scene.getStylesheets() | |
.add(getClass().getResource("Views.css").toExternalForm()); | |
return scene; | |
} | |
private void showAlert(final String transactionDetailsMsg, | |
final String title) { | |
Alert transactionDetailsAlert = new Alert(AlertType.INFORMATION); | |
transactionDetailsAlert.setTitle(title); | |
TextArea detailsArea = new TextArea(transactionDetailsMsg); | |
detailsArea.setEditable(false); | |
detailsArea.setWrapText(true); | |
GridPane.setVgrow(detailsArea, Priority.ALWAYS); | |
GridPane.setHgrow(detailsArea, Priority.ALWAYS); | |
transactionDetailsAlert.getDialogPane() | |
.setExpandableContent(detailsArea); | |
transactionDetailsAlert.getDialogPane().setExpanded(true); | |
transactionDetailsAlert.showAndWait(); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
protected void setFxFocus() { | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public void updateView(final TransactionModel transactionModel) { | |
tableContents.add(transactionModel); | |
if (TransactionHistoryView.table == null) { | |
return;// We are not supposed to manually create this let the | |
// framework handle the creation. data loss wont happen | |
// because we store the transaction in a set. | |
} | |
TransactionHistoryView.table.getItems().add(transactionModel); | |
TransactionHistoryView.table.refresh(); | |
} | |
/** | |
* {@inheritDoc} | |
*/ | |
@Override | |
public void dispose() { | |
super.dispose(); | |
BlockchainViewsRegistry.dereisterBlockchainView(this); | |
} | |
} |