mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Improve type safety of the fluent interface of Overlay<T>
Refactor all the unchecked casts from Overlay<T> to T into a single private cast() method. Also add a runtime type check to the constructor to prevent creation of window objects of the form "A extends Overlay<B>" for unrelated A & B, as such casts would then subvert the type system.
This commit is contained in:
parent
6b0da3d08b
commit
2f61b025f5
@ -29,14 +29,16 @@ import bisq.desktop.util.Transitions;
|
||||
|
||||
import bisq.core.app.BisqEnvironment;
|
||||
import bisq.core.locale.GlobalSettings;
|
||||
import bisq.core.locale.LanguageUtil;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.user.DontShowAgainLookup;
|
||||
import bisq.core.locale.LanguageUtil;
|
||||
|
||||
import bisq.common.Timer;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.util.Utilities;
|
||||
|
||||
import com.google.common.reflect.TypeToken;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import de.jensd.fx.fontawesome.AwesomeIcon;
|
||||
@ -68,8 +70,8 @@ import javafx.scene.transform.Rotate;
|
||||
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.geometry.NodeOrientation;
|
||||
import javafx.geometry.Pos;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
@ -93,7 +95,7 @@ import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public abstract class Overlay<T extends Overlay> {
|
||||
public abstract class Overlay<T extends Overlay<T>> {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Enum
|
||||
@ -185,12 +187,23 @@ public abstract class Overlay<T extends Overlay> {
|
||||
|
||||
protected int maxChar = 1800;
|
||||
|
||||
private T cast() {
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Public API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public Overlay() {
|
||||
//noinspection UnstableApiUsage
|
||||
TypeToken<T> typeToken = new TypeToken<>(getClass()) {
|
||||
};
|
||||
if (!typeToken.isSupertypeOf(getClass())) {
|
||||
throw new RuntimeException("Subclass of Overlay<T> should be castable to T");
|
||||
}
|
||||
}
|
||||
|
||||
public void show(boolean showAgainChecked) {
|
||||
@ -266,26 +279,22 @@ public abstract class Overlay<T extends Overlay> {
|
||||
|
||||
public T onClose(Runnable closeHandler) {
|
||||
this.closeHandlerOptional = Optional.of(closeHandler);
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T onAction(Runnable actionHandler) {
|
||||
this.actionHandlerOptional = Optional.of(actionHandler);
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T onSecondaryAction(Runnable secondaryActionHandlerOptional) {
|
||||
this.secondaryActionHandlerOptional = Optional.of(secondaryActionHandlerOptional);
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T headLine(String headLine) {
|
||||
this.headLine = headLine;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T notification(String message) {
|
||||
@ -294,8 +303,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.notification");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T instruction(String message) {
|
||||
@ -304,8 +312,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.instruction");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T attention(String message) {
|
||||
@ -314,8 +321,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.attention");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T backgroundInfo(String message) {
|
||||
@ -324,8 +330,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.backgroundInfo");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T feedback(String message) {
|
||||
@ -334,8 +339,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.feedback");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T confirmation(String message) {
|
||||
@ -344,8 +348,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.confirmation");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T information(String message) {
|
||||
@ -354,8 +357,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.information");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T warning(String message) {
|
||||
@ -365,8 +367,7 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.warning");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T error(String message) {
|
||||
@ -377,136 +378,116 @@ public abstract class Overlay<T extends Overlay> {
|
||||
this.headLine = Res.get("popup.headline.error");
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
public T showReportErrorButtons() {
|
||||
this.showReportErrorButtons = true;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T message(String message) {
|
||||
this.message = message;
|
||||
setTruncatedMessage();
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T closeButtonText(String closeButtonText) {
|
||||
this.closeButtonText = closeButtonText;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T useReportBugButton() {
|
||||
this.closeButtonText = Res.get("shared.reportBug");
|
||||
this.closeHandlerOptional = Optional.of(() -> GUIUtil.openWebPage("https://bisq.network/source/bisq/issues"));
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T useIUnderstandButton() {
|
||||
this.closeButtonText = Res.get("shared.iUnderstand");
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T actionButtonTextWithGoTo(String target) {
|
||||
this.actionButtonText = Res.get("shared.goTo", Res.get(target));
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T secondaryActionButtonTextWithGoTo(String target) {
|
||||
this.secondaryActionButtonText = Res.get("shared.goTo", Res.get(target));
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T closeButtonTextWithGoTo(String target) {
|
||||
this.closeButtonText = Res.get("shared.goTo", Res.get(target));
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T actionButtonText(String actionButtonText) {
|
||||
this.actionButtonText = actionButtonText;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T secondaryActionButtonText(String secondaryActionButtonText) {
|
||||
this.secondaryActionButtonText = secondaryActionButtonText;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T useShutDownButton() {
|
||||
this.actionButtonText = Res.get("shared.shutDown");
|
||||
this.actionHandlerOptional = Optional.ofNullable(BisqApp.getShutDownHandler());
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T buttonAlignment(HPos pos) {
|
||||
this.buttonAlignment = pos;
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T width(double width) {
|
||||
this.width = width;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T maxMessageLength(int maxChar) {
|
||||
this.maxChar = maxChar;
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T showBusyAnimation() {
|
||||
this.showBusyAnimation = true;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T dontShowAgainId(String key) {
|
||||
this.dontShowAgainId = key;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T dontShowAgainText(String dontShowAgainText) {
|
||||
this.dontShowAgainText = dontShowAgainText;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T hideCloseButton() {
|
||||
this.hideCloseButton = true;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T useAnimation(boolean useAnimation) {
|
||||
this.useAnimation = useAnimation;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T setHeadlineStyle(String headlineStyle) {
|
||||
this.headlineStyle = headlineStyle;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
public T disableActionButton() {
|
||||
this.disableActionButton = true;
|
||||
//noinspection unchecked
|
||||
return (T) this;
|
||||
return cast();
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,7 +4,7 @@ import com.jfoenix.controls.JFXTabPane;
|
||||
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
public abstract class TabbedOverlay<T extends TabbedOverlay> extends Overlay {
|
||||
public abstract class TabbedOverlay<T extends TabbedOverlay<T>> extends Overlay<T> {
|
||||
|
||||
protected JFXTabPane tabPane;
|
||||
|
||||
|
@ -22,7 +22,8 @@ import bisq.desktop.main.overlays.Overlay;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class Popup<T extends Overlay> extends Overlay<Popup> {
|
||||
// TODO: Type parameter is unused - remove:
|
||||
public class Popup<T> extends Overlay<Popup<T>> {
|
||||
protected final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
public Popup() {
|
||||
@ -41,6 +42,4 @@ public class Popup<T extends Overlay> extends Overlay<Popup> {
|
||||
protected void onHidden() {
|
||||
PopupManager.onHidden(this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.desktop.main.overlays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class OverlayTest {
|
||||
|
||||
@Test
|
||||
public void typeSafeCreation() {
|
||||
new A();
|
||||
new C();
|
||||
new D<>();
|
||||
}
|
||||
|
||||
@Test(expected = RuntimeException.class)
|
||||
public void typeUnsafeCreation() {
|
||||
new B();
|
||||
}
|
||||
|
||||
private static class A extends Overlay<A> {
|
||||
}
|
||||
|
||||
private static class B extends Overlay<A> {
|
||||
}
|
||||
|
||||
private static class C extends TabbedOverlay<C> {
|
||||
}
|
||||
|
||||
private static class D<T> extends Overlay<D<T>> {
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user