From 7ac2a022795985da82be5f2f2969d4851c97f144 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 1 Oct 2018 11:39:45 -0500 Subject: [PATCH 1/5] Fix startHeight, handle errors --- .../main/java/bisq/core/dao/node/full/FullNode.java | 7 +++++-- .../main/java/bisq/core/dao/node/full/RpcService.java | 11 ++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/node/full/FullNode.java b/core/src/main/java/bisq/core/dao/node/full/FullNode.java index 46ba64302c4..db3e676eb0d 100644 --- a/core/src/main/java/bisq/core/dao/node/full/FullNode.java +++ b/core/src/main/java/bisq/core/dao/node/full/FullNode.java @@ -158,7 +158,7 @@ private void parseBlocksIfNewBlockAvailable(int chainHeadHeight) { if (newChainHeadHeight > chainHeadHeight) { log.info("During parsing new blocks have arrived. We parse again with those missing blocks." + "ChainHeadHeight={}, newChainHeadHeight={}", chainHeadHeight, newChainHeadHeight); - parseBlocksOnHeadHeight(chainHeadHeight, newChainHeadHeight); + parseBlocksOnHeadHeight(chainHeadHeight + 1, newChainHeadHeight); } else { log.info("parseBlocksIfNewBlockAvailable did not result in a new block, so we complete."); onParseBlockChainComplete(); @@ -239,9 +239,12 @@ private void parseBlock(int blockHeight, int chainHeadHeight, } private void handleError(Throwable throwable) { - String errorMessage = "Initializing FullNode failed: Error=" + throwable.toString(); + String errorMessage = "An error occurred: Error=" + throwable.toString(); log.error(errorMessage); + if (throwable instanceof BlockNotConnectingException) + startReOrgFromLastSnapshot(); + if (errorMessageHandler != null) errorMessageHandler.handleErrorMessage(errorMessage); } diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcService.java b/core/src/main/java/bisq/core/dao/node/full/RpcService.java index fa69b0dceb4..3806d646106 100644 --- a/core/src/main/java/bisq/core/dao/node/full/RpcService.java +++ b/core/src/main/java/bisq/core/dao/node/full/RpcService.java @@ -162,6 +162,15 @@ void addNewBtcBlockHandler(Consumer btcBlockHandler, daemon.addBlockListener(new BlockListener() { @Override public void blockDetected(com.neemre.btcdcli4j.core.domain.RawBlock rawBtcBlock) { + if (rawBtcBlock.getHeight() == null || rawBtcBlock.getHeight() == 0) { + // TODO throw exceptions at BlockNotificationWorker.getRelatedEntity + log.warn("We received a RawBlock with no data. " + + "That can happen if we run multiple networks (regtest + testnet) and receive a blockNotify " + + "from the other network or if our BTC node is not completed with processing a new arrived " + + "block. We ignore that notification."); + return; + } + try { log.info("New block received: height={}, id={}", rawBtcBlock.getHeight(), rawBtcBlock.getHash()); List txList = rawBtcBlock.getTx().stream() @@ -204,7 +213,7 @@ void requestBtcBlock(int blockHeight, List txList = rawBtcBlock.getTx().stream() .map(e -> getTxFromRawTransaction(e, rawBtcBlock)) .collect(Collectors.toList()); - log.info("requestBtcBlock with all txs took {} ms at blockHeight {}; txList.size={}", + log.debug("requestBtcBlock with all txs took {} ms at blockHeight {}; txList.size={}", System.currentTimeMillis() - startTs, blockHeight, txList.size()); return new RawBlock(rawBtcBlock.getHeight(), rawBtcBlock.getTime() * 1000, // rawBtcBlock.getTime() is in sec but we want ms From b981737775fe790585866442ac6f663caf36d688 Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 1 Oct 2018 12:17:28 -0500 Subject: [PATCH 2/5] Change log msg with update to new btcd-cli4j --- core/src/main/java/bisq/core/dao/node/full/RpcService.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/node/full/RpcService.java b/core/src/main/java/bisq/core/dao/node/full/RpcService.java index 3806d646106..f971c1fcae3 100644 --- a/core/src/main/java/bisq/core/dao/node/full/RpcService.java +++ b/core/src/main/java/bisq/core/dao/node/full/RpcService.java @@ -163,11 +163,7 @@ void addNewBtcBlockHandler(Consumer btcBlockHandler, @Override public void blockDetected(com.neemre.btcdcli4j.core.domain.RawBlock rawBtcBlock) { if (rawBtcBlock.getHeight() == null || rawBtcBlock.getHeight() == 0) { - // TODO throw exceptions at BlockNotificationWorker.getRelatedEntity - log.warn("We received a RawBlock with no data. " + - "That can happen if we run multiple networks (regtest + testnet) and receive a blockNotify " + - "from the other network or if our BTC node is not completed with processing a new arrived " + - "block. We ignore that notification."); + log.warn("We received a RawBlock with no data. blockHash={}", rawBtcBlock.getHash()); return; } From 030dc0209b661354755b88b5f129945d4face14e Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 1 Oct 2018 12:17:40 -0500 Subject: [PATCH 3/5] Change btcd-cli4j to 3864e1c4 --- core/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/build.gradle b/core/build.gradle index b42100a483d..e01ec39b558 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -24,7 +24,7 @@ dependencies { compile project(':assets') compile project(':p2p') compile 'net.sf.jopt-simple:jopt-simple:5.0.3' - compile('network.bisq.btcd-cli4j:btcd-cli4j-core:1763d7ef') { + compile('network.bisq.btcd-cli4j:btcd-cli4j-core:3864e1c4') { exclude(module: 'slf4j-api') exclude(module: 'httpclient') exclude(module: 'commons-lang3') @@ -32,7 +32,7 @@ dependencies { exclude(module: 'jackson-annotations') exclude(module: 'jackson-databind') } - compile('network.bisq.btcd-cli4j:btcd-cli4j-daemon:1763d7ef') { + compile('network.bisq.btcd-cli4j:btcd-cli4j-daemon:3864e1c4') { exclude(module: 'slf4j-api') exclude(module: 'httpclient') exclude(module: 'commons-lang3') From 2de9c7282ef4d539b8aa34a7ad03431a576583bf Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 1 Oct 2018 21:16:02 -0500 Subject: [PATCH 4/5] Make DaoFacade implement DaoSetupService - Fixes a bug that the phaseProperty was not set in DaoFacade as the class was instantiated on demand and missed updates. --- core/src/main/java/bisq/core/dao/DaoFacade.java | 15 ++++++++++++++- core/src/main/java/bisq/core/dao/DaoSetup.java | 5 +++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/dao/DaoFacade.java b/core/src/main/java/bisq/core/dao/DaoFacade.java index 3bbbf1255c1..23cac2c828a 100644 --- a/core/src/main/java/bisq/core/dao/DaoFacade.java +++ b/core/src/main/java/bisq/core/dao/DaoFacade.java @@ -82,7 +82,7 @@ * Provides a facade to interact with the Dao domain. Hides complexity and domain details to clients (e.g. UI or APIs) * by providing a reduced API and/or aggregating subroutines. */ -public class DaoFacade { +public class DaoFacade implements DaoSetupService { private final ProposalListPresentation proposalListPresentation; private final BallotListService ballotListService; private final BallotListPresentation ballotListPresentation; @@ -135,7 +135,19 @@ public DaoFacade(MyProposalListService myProposalListService, this.lockupService = lockupService; this.unlockService = unlockService; this.proposalConsensus = proposalConsensus; + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // DaoSetupService + /////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void addListeners() { + } + @Override + public void start() { bsqStateService.addBsqStateListener(new BsqStateListener() { @Override public void onNewBlockHeight(int blockHeight) { @@ -153,6 +165,7 @@ public void onParseBlockChainComplete() { }); } + public void addBsqStateListener(BsqStateListener listener) { bsqStateService.addBsqStateListener(listener); } diff --git a/core/src/main/java/bisq/core/dao/DaoSetup.java b/core/src/main/java/bisq/core/dao/DaoSetup.java index 5c4740bb2c9..e5fd684f707 100644 --- a/core/src/main/java/bisq/core/dao/DaoSetup.java +++ b/core/src/main/java/bisq/core/dao/DaoSetup.java @@ -47,6 +47,7 @@ public class DaoSetup { private final VoteRevealService voteRevealService; private final VoteResultService voteResultService; private final BsqNode bsqNode; + private final DaoFacade daoFacade; private final ExportJsonFilesService exportJsonFilesService; @Inject @@ -59,6 +60,7 @@ public DaoSetup(BsqNodeProvider bsqNodeProvider, MyBlindVoteListService myBlindVoteListService, VoteRevealService voteRevealService, VoteResultService voteResultService, + DaoFacade daoFacade, ExportJsonFilesService exportJsonFilesService) { this.bsqStateService = bsqStateService; this.cycleService = cycleService; @@ -68,6 +70,7 @@ public DaoSetup(BsqNodeProvider bsqNodeProvider, this.myBlindVoteListService = myBlindVoteListService; this.voteRevealService = voteRevealService; this.voteResultService = voteResultService; + this.daoFacade = daoFacade; this.exportJsonFilesService = exportJsonFilesService; bsqNode = bsqNodeProvider.getBsqNode(); @@ -85,6 +88,7 @@ public void onAllServicesInitialized(ErrorMessageHandler errorMessageHandler) { voteRevealService.addListeners(); voteResultService.addListeners(); exportJsonFilesService.addListeners(); + daoFacade.addListeners(); bsqStateService.start(); cycleService.start(); @@ -95,6 +99,7 @@ public void onAllServicesInitialized(ErrorMessageHandler errorMessageHandler) { voteRevealService.start(); voteResultService.start(); exportJsonFilesService.start(); + daoFacade.start(); bsqNode.setErrorMessageHandler(errorMessageHandler); bsqNode.start(); From d68f9b5474ff3644aa2e79477c4e3a4965c659db Mon Sep 17 00:00:00 2001 From: Manfred Karrer Date: Mon, 1 Oct 2018 21:19:12 -0500 Subject: [PATCH 5/5] Fix wrong validation checks on proposals and ballots - We cannot validate the txType at startup as the blockchain is nto ready at that moment. So we cannot apply that validation when we receive the payloads. We do the validation instead at the access to the lists. - We fix a glitch with missing cycle start time in ResultsOfCycle - Cleanup in VoteResultView --- .../ballot/BallotListPresentation.java | 4 +- .../governance/ballot/BallotListService.java | 14 +-- .../blindvote/BlindVoteConsensus.java | 2 +- .../governance/proposal/ProposalService.java | 27 ++++-- .../proposal/ProposalValidator.java | 2 + .../voteresult/VoteResultService.java | 2 +- .../dao/governance/result/ResultsOfCycle.java | 25 ++++- .../dao/governance/result/VoteResultView.java | 97 ++++++++----------- 8 files changed, 91 insertions(+), 82 deletions(-) diff --git a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java index bd0278688d0..809af63ec87 100644 --- a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java +++ b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListPresentation.java @@ -75,13 +75,13 @@ public void onNewBlockHeight(int blockHeight) { @Override public void onParseTxsComplete(Block block) { - onListChanged(ballotListService.getBallotList().getList()); + onListChanged(ballotListService.getValidatedBallotList()); } @Override public void onParseBlockChainComplete() { // As we update the list in ProposalService.onParseBlockChainComplete we need to update here as well. - onListChanged(ballotListService.getBallotList().getList()); + onListChanged(ballotListService.getValidatedBallotList()); } diff --git a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java index 401b3b9af42..e0081c0f383 100644 --- a/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java +++ b/core/src/main/java/bisq/core/dao/governance/ballot/BallotListService.java @@ -82,7 +82,6 @@ public void addListeners() { .map(ProposalPayload::getProposal) .filter(proposal -> ballotList.stream() .noneMatch(ballot -> ballot.getProposal().equals(proposal))) - .filter(proposalValidator::isTxTypeValid) .forEach(proposal -> { Ballot ballot = new Ballot(proposal); // vote is null log.info("We create a new ballot with a proposal and add it to our list. " + @@ -110,9 +109,7 @@ public void readPersisted() { BallotList persisted = storage.initAndGetPersisted(ballotList, 100); if (persisted != null) { ballotList.clear(); - persisted.getList().stream() - .filter(ballot -> proposalValidator.isTxTypeValid(ballot.getProposal())) - .forEach(ballotList::add); + ballotList.addAll(persisted.getList()); listeners.forEach(l -> l.onListChanged(ballotList.getList())); } } @@ -132,12 +129,15 @@ public void addListener(BallotListChangeListener listener) { listeners.add(listener); } - public BallotList getBallotList() { - return ballotList; + public List getValidatedBallotList() { + return ballotList.stream() + .filter(ballot -> proposalValidator.isTxTypeValid(ballot.getProposal())) + .collect(Collectors.toList()); } - public List getBallotsOfCycle() { + public List getValidBallotsOfCycle() { return ballotList.stream() + .filter(ballot -> proposalValidator.isTxTypeValid(ballot.getProposal())) .filter(ballot -> periodService.isTxInCorrectCycle(ballot.getTxId(), periodService.getChainHeight())) .collect(Collectors.toList()); } diff --git a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java index f8a337a82d0..e38a5ed150f 100644 --- a/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java +++ b/core/src/main/java/bisq/core/dao/governance/blindvote/BlindVoteConsensus.java @@ -56,7 +56,7 @@ public static boolean hasOpReturnDataValidLength(byte[] opReturnData) { } public static BallotList getSortedBallotList(BallotListService ballotListService) { - List ballotList = ballotListService.getBallotsOfCycle().stream() + List ballotList = ballotListService.getValidBallotsOfCycle().stream() .sorted(Comparator.comparing(Ballot::getTxId)) .collect(Collectors.toList()); log.info("Sorted ballotList: " + ballotList); diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java index 47d316445fd..86bc7ea65b1 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalService.java @@ -45,6 +45,9 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import java.util.List; +import java.util.stream.Collectors; + import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -174,6 +177,19 @@ public void onParseBlockChainComplete() { } + /////////////////////////////////////////////////////////////////////////////////////////// + // Getter + /////////////////////////////////////////////////////////////////////////////////////////// + + + public List getValidatedProposals() { + return proposalPayloads.stream() + .map(proposalPayload -> proposalPayload.getProposal()) + .filter(proposal -> proposalValidator.isTxTypeValid(proposal)) + .collect(Collectors.toList()); + } + + /////////////////////////////////////////////////////////////////////////////////////////// // Private /////////////////////////////////////////////////////////////////////////////////////////// @@ -243,14 +259,9 @@ private void onAppendOnlyDataAdded(PersistableNetworkPayload persistableNetworkP if (!proposalPayloads.contains(proposalPayload)) { Proposal proposal = proposalPayload.getProposal(); if (proposalValidator.areDataFieldsValid(proposal)) { - if (proposalValidator.isTxTypeValid(proposal)) { - proposalPayloads.add(proposalPayload); - log.info("We received a ProposalPayload and store it to our appendOnlyStoreList. proposalTxId={}", - proposal.getTxId()); - } else { - log.warn("We received a proposal with an invalid txId. Proposal.txId={}", - proposal.getTxId()); - } + proposalPayloads.add(proposalPayload); + log.info("We received a ProposalPayload and store it to our appendOnlyStoreList. proposalTxId={}", + proposal.getTxId()); } else { log.warn("We received a invalid append-only proposal from the P2P network. " + "Proposal.txId={}, blockHeight={}", diff --git a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java index 8d6f2da7b0d..26b2dbf510e 100644 --- a/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java +++ b/core/src/main/java/bisq/core/dao/governance/proposal/ProposalValidator.java @@ -79,6 +79,8 @@ public boolean isTxTypeValid(Proposal proposal) { } Optional optionalTxType = bsqStateService.getOptionalTxType(txId); boolean present = optionalTxType.filter(txType -> txType == proposal.getTxType()).isPresent(); + if (!present) + log.debug("optionalTxType not present for proposal {}" + proposal); return present; } diff --git a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java index fc35c794fe2..44ab17d2c42 100644 --- a/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java +++ b/core/src/main/java/bisq/core/dao/governance/voteresult/VoteResultService.java @@ -310,7 +310,7 @@ private BallotList createBallotList(VoteWithProposalTxIdList voteWithProposalTxI .collect(Collectors.toMap(VoteWithProposalTxId::getProposalTxId, VoteWithProposalTxId::getVote)); // We make a map with proposalTxId as key and the ballot as value out of our stored ballot list - Map ballotByTxIdMap = ballotListService.getBallotList().stream() + Map ballotByTxIdMap = ballotListService.getValidatedBallotList().stream() .collect(Collectors.toMap(Ballot::getTxId, ballot -> ballot)); List missingBallots = new ArrayList<>(); diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java index 98af6dc2e4c..d15443596d9 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/ResultsOfCycle.java @@ -20,8 +20,9 @@ import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMerits; import bisq.core.dao.governance.voteresult.EvaluatedProposal; +import bisq.core.dao.state.BsqStateService; +import bisq.core.dao.state.blockchain.Block; import bisq.core.dao.state.period.Cycle; -import bisq.core.util.BsqFormatter; import java.util.List; @@ -33,12 +34,14 @@ class ResultsOfCycle { private final Cycle cycle; private final int cycleIndex; - private final int numVotes, numAcceptedVotes, numRejectedVotes; - private BsqFormatter bsqFormatter; + private final int numVotes; + private final int numAcceptedVotes; + private final int numRejectedVotes; + private final BsqStateService bsqStateService; private long cycleStartTime; // All available proposals of cycle - private List proposals; + private final List proposals; // Proposals which ended up in voting private final List evaluatedProposals; @@ -50,7 +53,8 @@ class ResultsOfCycle { long cycleStartTime, List proposals, List evaluatedProposals, - List decryptedVotesForCycle) { + List decryptedVotesForCycle, + BsqStateService bsqStateService) { this.cycle = cycle; this.cycleIndex = cycleIndex; this.cycleStartTime = cycleStartTime; @@ -67,5 +71,16 @@ class ResultsOfCycle { numRejectedVotes = evaluatedProposals.stream() .mapToInt(e -> e.getProposalVoteResult().getNumRejectedVotes()) .sum(); + this.bsqStateService = bsqStateService; + } + + public long getCycleStartTime() { + // At a new cycle we have cycleStartTime 0 as the block is not processed yet. + // To display a correct value we access again from the bsqStateService + if (cycleStartTime == 0) + cycleStartTime = bsqStateService.getBlockAtHeight(cycle.getHeightOfFirstBlock()) + .map(Block::getTime) + .orElse(0L); + return cycleStartTime; } } diff --git a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java index 240166b3018..cd4b81981c7 100644 --- a/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java +++ b/desktop/src/main/java/bisq/desktop/main/dao/governance/result/VoteResultView.java @@ -33,7 +33,6 @@ import bisq.core.dao.governance.ballot.Ballot; import bisq.core.dao.governance.proposal.Proposal; import bisq.core.dao.governance.proposal.ProposalService; -import bisq.core.dao.governance.proposal.storage.appendonly.ProposalPayload; import bisq.core.dao.governance.voteresult.DecryptedBallotsWithMerits; import bisq.core.dao.governance.voteresult.EvaluatedProposal; import bisq.core.dao.governance.voteresult.VoteResultService; @@ -242,8 +241,7 @@ private void onSelectProposalResultListItem(ProposalListItem item) { private void fillCycleList() { cycleListItemList.clear(); bsqStateService.getCycles().forEach(cycle -> { - List proposalsForCycle = proposalService.getProposalPayloads().stream() - .map(ProposalPayload::getProposal) + List proposalsForCycle = proposalService.getValidatedProposals().stream() .filter(proposal -> cycleService.isTxInCycle(cycle, proposal.getTxId())) .collect(Collectors.toList()); @@ -265,7 +263,8 @@ private void fillCycleList() { cycleStartTime, proposalsForCycle, evaluatedProposalsForCycle, - decryptedVotesForCycle); + decryptedVotesForCycle, + bsqStateService); CycleListItem cycleListItem = new CycleListItem(resultsOfCycle, bsqStateService, bsqFormatter); cycleListItemList.add(cycleListItem); }); @@ -421,12 +420,11 @@ private void createCycleColumns(TableView votesTableView) { column.setMinWidth(160); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -446,12 +444,11 @@ public void updateItem(final CycleListItem item, boolean empty) { column.setMaxWidth(90); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -471,12 +468,11 @@ public void updateItem(final CycleListItem item, boolean empty) { column.setMaxWidth(70); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -495,12 +491,11 @@ public void updateItem(final CycleListItem item, boolean empty) { column.setMinWidth(70); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -519,12 +514,11 @@ public void updateItem(final CycleListItem item, boolean empty) { column.setMinWidth(70); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final CycleListItem item, boolean empty) { super.updateItem(item, empty); @@ -553,12 +547,11 @@ private void createProposalsColumns(TableView votesTableView) column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { super.updateItem(item, empty); @@ -580,12 +573,11 @@ public void updateItem(final ProposalListItem item, boolean empty) { column.setMinWidth(80); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { @@ -609,13 +601,12 @@ public void updateItem(final ProposalListItem item, boolean empty) { column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { private HyperlinkWithIcon field; @Override @@ -644,12 +635,11 @@ public void updateItem(final ProposalListItem item, boolean empty) { column.setMinWidth(150); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { super.updateItem(item, empty); @@ -669,12 +659,11 @@ public void updateItem(final ProposalListItem item, boolean empty) { column.setMinWidth(180); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final ProposalListItem item, boolean empty) { super.updateItem(item, empty); @@ -694,13 +683,12 @@ public void updateItem(final ProposalListItem item, boolean empty) { column.setMinWidth(70); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { Label myVoteIcon; @Override @@ -728,12 +716,11 @@ public void updateItem(final ProposalListItem item, boolean empty) { column.setMinWidth(90); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { Label icon; @Override @@ -771,12 +758,11 @@ private void createColumns(TableView votesTableView) { column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { private Label icon; @Override @@ -803,12 +789,11 @@ public void updateItem(final VoteListItem item, boolean empty) { column.setMinWidth(100); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final VoteListItem item, boolean empty) { super.updateItem(item, empty); @@ -826,12 +811,11 @@ public void updateItem(final VoteListItem item, boolean empty) { column.setMinWidth(100); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final VoteListItem item, boolean empty) { super.updateItem(item, empty); @@ -850,12 +834,11 @@ public void updateItem(final VoteListItem item, boolean empty) { column.setMinWidth(100); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); column.setCellFactory( - new Callback, TableCell>() { + new Callback<>() { @Override public TableCell call( TableColumn column) { - return new TableCell() { + return new TableCell<>() { @Override public void updateItem(final VoteListItem item, boolean empty) { super.updateItem(item, empty); @@ -874,12 +857,11 @@ public void updateItem(final VoteListItem item, boolean empty) { column.setMinWidth(130); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { private HyperlinkWithIcon hyperlinkWithIcon; @Override @@ -909,12 +891,11 @@ public void updateItem(final VoteListItem item, boolean empty) { column.setMinWidth(140); column.setMaxWidth(column.getMinWidth()); column.setCellValueFactory((item) -> new ReadOnlyObjectWrapper<>(item.getValue())); - column.setCellFactory(new Callback, - TableCell>() { + column.setCellFactory(new Callback<>() { @Override public TableCell call(TableColumn column) { - return new TableCell() { + return new TableCell<>() { private HyperlinkWithIcon hyperlinkWithIcon; @Override