diff --git a/.gitignore b/.gitignore index e36257f..0011502 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /.idea/ /LeagueTeamComp.iml /out/ +/logs/ diff --git a/pom.xml b/pom.xml index f609244..febe56f 100644 --- a/pom.xml +++ b/pom.xml @@ -40,18 +40,7 @@ javafx-fxml ${javafx-fxml.version} - - - org.controlsfx - controlsfx - ${controlsfx.version} - - - org.projectlombok - lombok - ${lombok.version} - provided - + org.apache.logging.log4j log4j-api @@ -67,6 +56,18 @@ log4j-slf4j-impl ${log4j.version} + + + org.controlsfx + controlsfx + ${controlsfx.version} + + + org.projectlombok + lombok + ${lombok.version} + provided + com.google.code.gson gson diff --git a/src/main/java/com/st4s1k/leagueteamcomp/LeagueTeamCompApplication.java b/src/main/java/com/st4s1k/leagueteamcomp/LeagueTeamCompApplication.java index 4beb7d7..792f1f4 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/LeagueTeamCompApplication.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/LeagueTeamCompApplication.java @@ -1,58 +1,43 @@ package com.st4s1k.leagueteamcomp; import com.google.gson.Gson; +import com.st4s1k.leagueteamcomp.controller.LTCExceptionController; import com.st4s1k.leagueteamcomp.controller.LeagueTeamCompController; -import com.st4s1k.leagueteamcomp.model.champion.Champions; +import com.st4s1k.leagueteamcomp.exceptions.LTCException; +import com.st4s1k.leagueteamcomp.model.champion.ChampionsDTO; import com.st4s1k.leagueteamcomp.repository.ChampionRepository; +import com.st4s1k.leagueteamcomp.utils.ResizeHelper; import javafx.application.Application; +import javafx.application.Platform; import javafx.fxml.FXMLLoader; +import javafx.geometry.Insets; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.image.Image; +import javafx.scene.layout.Region; +import javafx.scene.paint.Color; +import javafx.stage.Modality; import javafx.stage.Stage; import javafx.stage.StageStyle; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; +import java.io.PrintWriter; +import java.io.StringWriter; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.text.MessageFormat; -import java.util.ResourceBundle; -import java.util.concurrent.CompletableFuture; +import java.util.stream.DoubleStream; +import static com.st4s1k.leagueteamcomp.utils.Resources.*; import static java.net.http.HttpResponse.BodyHandlers; import static java.util.Objects.requireNonNull; @Slf4j public class LeagueTeamCompApplication extends Application { - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Resource path constants * - * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - public static final String ICON_FILE_PATH = "icon.png"; - public static final String FXML_FILE_PATH = "ltc-view.fxml"; - public static final String APP_BUNDLE_PATH = "com.st4s1k.leagueteamcomp.ltc"; - public static final String FXML_BUNDLE_PATH = "com.st4s1k.leagueteamcomp.ltc-view"; - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Window constants * - * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - private static final String WINDOW_TITLE = "League of Legends Team Composition Tool"; - private static final int WINDOW_WIDTH = 800; - private static final int WINDOW_HEIGHT = 600; - private static final boolean WINDOW_IS_RESIZABLE = false; - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Resource bundles * - * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - - private final ResourceBundle ltcProperties = ResourceBundle.getBundle(APP_BUNDLE_PATH); - private final ResourceBundle ltcViewProperties = ResourceBundle.getBundle(FXML_BUNDLE_PATH); - public static void main(String[] args) { launch(); } @@ -60,40 +45,84 @@ public static void main(String[] args) { @Override @SneakyThrows public void start(Stage stage) { - ChampionRepository.init(getChampionsFromUrl()); - FXMLLoader loader = new FXMLLoader(getClass().getResource(FXML_FILE_PATH), ltcViewProperties); + Thread.setDefaultUncaughtExceptionHandler(LeagueTeamCompApplication::showError); + getChampionsFromUrl(); + FXMLLoader loader = new FXMLLoader(getClass().getResource(FXML_FILE_PATH), LTC_VIEW_PROPERTIES); Parent root = loader.load(); LeagueTeamCompController controller = loader.getController(); controller.setStageAndSetupListeners(stage); - controller.setCloseButtonAction(() -> closeProgram(stage, controller)); + controller.setCloseButtonAction(() -> closeProgram(stage, controller)); controller.setMinimizeButtonAction(() -> stage.setIconified(true)); Scene scene = new Scene(root, WINDOW_WIDTH, WINDOW_HEIGHT); + stage.initStyle(StageStyle.TRANSPARENT); + scene.setFill(Color.TRANSPARENT); stage.setScene(scene); stage.getIcons().add(new Image(requireNonNull(getClass().getResourceAsStream(ICON_FILE_PATH)))); stage.setTitle(WINDOW_TITLE); stage.setResizable(WINDOW_IS_RESIZABLE); - stage.initStyle(StageStyle.UNDECORATED); stage.show(); } @SneakyThrows - private CompletableFuture getChampionsFromUrl() { + private void getChampionsFromUrl() { HttpRequest request = HttpRequest.newBuilder() - .uri(new URI(ltcProperties.getString("champions-url"))) + .uri(new URI(CHAMPIONS_URL)) .build(); - return HttpClient.newHttpClient() + HttpClient.newHttpClient() .sendAsync(request, BodyHandlers.ofString()) - .thenApply(this::getChampions); + .thenApply(this::getChampions) + .thenAccept(ChampionRepository::init); } - private Champions getChampions(HttpResponse response) { + private ChampionsDTO getChampions(HttpResponse response) { String json = MessageFormat.format("'{'\"champions\":{0}'}'", response.body()); - Champions champions = new Gson().fromJson(json, Champions.class); + ChampionsDTO champions = new Gson().fromJson(json, ChampionsDTO.class); champions.getChampions().values() .forEach(champion -> champion.setImage(new Image(champion.getIconUrl(), true))); return champions; } + private static void showError(Thread t, Throwable e) { + log.error("***Default exception handler***"); + if (Platform.isFxApplicationThread()) { + showErrorDialog(e); + } else { + log.error("An unexpected error occurred in " + t); + } + } + + @SneakyThrows + private static void showErrorDialog(Throwable e) { + StringWriter errorMsg = new StringWriter(); + e.printStackTrace(new PrintWriter(errorMsg)); + Stage dialog = new Stage(); + dialog.initModality(Modality.APPLICATION_MODAL); + FXMLLoader loader = new FXMLLoader(LeagueTeamCompApplication.class.getResource("ltc-exception.fxml")); + Region root = loader.load(); + LTCExceptionController controller = loader.getController(); + controller.setStageAndSetupListeners(dialog); + int sceneWidth = 600; + int sceneHeight = 400; + if (e instanceof LTCException ltcException) { + sceneWidth = 300; + sceneHeight = 150; + controller.setErrorText(ltcException.getMessage()); + } else { + controller.setErrorText(errorMsg.toString()); + } + Scene scene = new Scene(root, sceneWidth, sceneHeight); + scene.setFill(Color.TRANSPARENT); + dialog.initStyle(StageStyle.TRANSPARENT); + dialog.setScene(scene); + dialog.setMinHeight(sceneHeight); + dialog.setMinWidth(sceneWidth); + Insets padding = root.getPadding(); + double border = DoubleStream.of(padding.getTop(), padding.getRight(), padding.getBottom(), padding.getLeft()) + .min().orElse(4); + ResizeHelper.addResizeListener(dialog, border); + dialog.show(); + } + private void closeProgram(Stage stage, LeagueTeamCompController controller) { log.info("Closing application..."); controller.stop(); diff --git a/src/main/java/com/st4s1k/leagueteamcomp/controller/LTCExceptionController.java b/src/main/java/com/st4s1k/leagueteamcomp/controller/LTCExceptionController.java new file mode 100644 index 0000000..0b0aae7 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/controller/LTCExceptionController.java @@ -0,0 +1,59 @@ +package com.st4s1k.leagueteamcomp.controller; + +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.TextArea; +import javafx.scene.input.MouseButton; +import javafx.scene.layout.HBox; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.ResourceBundle; + +public class LTCExceptionController implements Initializable { + + private static double xOffset = 0; + private static double yOffset = 0; + + @FXML + private Button minimizeButton; + @FXML + private Button closeButton; + @FXML + private Button okButton; + @FXML + private TextArea errorMessage; + @FXML + private HBox windowTitleBar; + + @Override + public void initialize(URL location, ResourceBundle resources) { + errorMessage.setEditable(false); + } + + public void setStageAndSetupListeners(Stage stage) { + windowTitleBar.setOnMousePressed(event -> { + xOffset = stage.getX() - event.getScreenX(); + yOffset = stage.getY() - event.getScreenY(); + }); + windowTitleBar.setOnMouseDragged(event -> { + stage.setX(event.getScreenX() + xOffset); + stage.setY(event.getScreenY() + yOffset); + }); + windowTitleBar.setOnMouseClicked(event -> { + if (event.getButton().equals(MouseButton.PRIMARY)) { + if (event.getClickCount() == 2) { + stage.setMaximized(!stage.isMaximized()); + } + } + }); + okButton.setOnAction(event -> stage.close()); + closeButton.setOnAction(actionEvent -> stage.close()); + minimizeButton.setOnAction(actionEvent -> stage.setIconified(true)); + } + + public void setErrorText(String text) { + errorMessage.setText(text); + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/controller/LeagueTeamCompController.java b/src/main/java/com/st4s1k/leagueteamcomp/controller/LeagueTeamCompController.java index 774f12a..e060dff 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/controller/LeagueTeamCompController.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/controller/LeagueTeamCompController.java @@ -1,12 +1,20 @@ package com.st4s1k.leagueteamcomp.controller; -import com.st4s1k.leagueteamcomp.model.SummonerRole; -import com.st4s1k.leagueteamcomp.model.champion.AttributeRatings; -import com.st4s1k.leagueteamcomp.model.champion.Champion; +import com.st4s1k.leagueteamcomp.exceptions.LTCException; +import com.st4s1k.leagueteamcomp.model.champion.AttributeRatingsDTO; +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; import com.st4s1k.leagueteamcomp.model.champion.select.ChampSelectDTO; import com.st4s1k.leagueteamcomp.model.champion.select.SlotDTO; +import com.st4s1k.leagueteamcomp.model.champion.select.SummonerDTO; import com.st4s1k.leagueteamcomp.model.champion.select.TeamDTO; +import com.st4s1k.leagueteamcomp.model.enums.SummonerRoleEnum; +import com.st4s1k.leagueteamcomp.model.interfaces.ChampionHolder; +import com.st4s1k.leagueteamcomp.model.interfaces.Clearable; +import com.st4s1k.leagueteamcomp.model.interfaces.SlotItem; +import com.st4s1k.leagueteamcomp.service.ChampionSuggestionService; import com.st4s1k.leagueteamcomp.service.LeagueTeamCompService; +import com.st4s1k.leagueteamcomp.service.SummonerRoleListGeneratorService; +import com.st4s1k.leagueteamcomp.utils.Utils; import com.stirante.lolclient.ClientApi; import com.stirante.lolclient.ClientConnectionListener; import com.stirante.lolclient.ClientWebSocket; @@ -14,7 +22,6 @@ import generated.LolChampSelectChampSelectSession; import generated.LolSummonerSummoner; import javafx.application.Platform; -import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.css.PseudoClass; @@ -22,10 +29,8 @@ import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.control.*; -import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; -import javafx.scene.layout.GridPane; import javafx.scene.text.Text; import javafx.scene.text.TextFlow; import javafx.stage.Stage; @@ -35,26 +40,30 @@ import java.net.URL; import java.util.*; import java.util.function.DoubleBinaryOperator; -import java.util.stream.IntStream; -import java.util.stream.Stream; -import static com.st4s1k.leagueteamcomp.model.SummonerRole.*; -import static com.st4s1k.leagueteamcomp.service.SummonerRoleListGeneratorService.getCombinations; +import static com.st4s1k.leagueteamcomp.model.enums.SummonerRoleEnum.*; import static java.util.function.Predicate.not; import static java.util.stream.Collectors.*; -import static javafx.geometry.Pos.CENTER; import static javafx.scene.control.SelectionMode.MULTIPLE; import static org.controlsfx.control.textfield.TextFields.bindAutoCompletion; @Slf4j public class LeagueTeamCompController implements Initializable { - private static double xOffset = 0; - private static double yOffset = 0; + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * App controls * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ - private LeagueTeamCompService service; + @FXML + private TabPane tabPane; + @FXML + private Button minimizeButton; + @FXML + private Button closeButton; - /* Role Compositions */ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Role Compositions * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ private static final boolean DEFAULT_CHECKBOX_STATE = true; @@ -132,7 +141,9 @@ public class LeagueTeamCompController implements Initializable { @FXML private Button resetButton; - /* Champion Suggestions */ + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Champion Suggestions * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ private static final PseudoClass CHAMPION_STAT_GOOD_PSEUDO_CLASS = PseudoClass.getPseudoClass("good"); private static final PseudoClass CHAMPION_STAT_BAD_PSEUDO_CLASS = PseudoClass.getPseudoClass("bad"); @@ -143,58 +154,85 @@ public class LeagueTeamCompController implements Initializable { @FXML private TextField allySearchField; @FXML - private ListView allyListView; + private ListView> allyListView; @FXML - private ListView allyBanListView; + private ListView> allyBanListView; + @FXML + private ListView>> suggestionsListView; @FXML private TextFlow allyTeamResultTextFlow; @FXML private TextField enemySearchField; @FXML - private ListView enemyListView; + private ListView> enemyListView; @FXML - private ListView enemyBanListView; + private ListView> enemyBanListView; @FXML private TextFlow enemyTeamResultTextFlow; @FXML - private GridPane gridPane; - @FXML - private TabPane tabPane; - @FXML - private Button minimizeButton; - @FXML - private Button closeButton; + private ToggleButton manualModeToggle; + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Controller fields * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + private static double xOffset = 0; + private static double yOffset = 0; + + private final LeagueTeamCompService service = LeagueTeamCompService.getInstance(); + private final ChampionSuggestionService suggestionService = ChampionSuggestionService.getInstance(); + private final SummonerRoleListGeneratorService roleListGeneratorService = SummonerRoleListGeneratorService.getInstance(); + private final ChampSelectDTO champSelect = new ChampSelectDTO(); + private final ClientApi api = new ClientApi(); - private ChampSelectDTO champSelect; private ClientWebSocket socket; - private ClientApi api; + + public void setStageAndSetupListeners(Stage stage) { + tabPane.setOnMousePressed(event -> { + xOffset = stage.getX() - event.getScreenX(); + yOffset = stage.getY() - event.getScreenY(); + }); + tabPane.setOnMouseDragged(event -> { + stage.setX(event.getScreenX() + xOffset); + stage.setY(event.getScreenY() + yOffset); + }); + } + + public void setCloseButtonAction(Runnable closeAction) { + closeButton.setOnAction(actionEvent -> closeAction.run()); + } + + public void setMinimizeButtonAction(Runnable minimizeAction) { + minimizeButton.setOnAction(actionEvent -> minimizeAction.run()); + } + + public void stop() { + api.stop(); + } @Override @SneakyThrows public void initialize(URL url, ResourceBundle resourceBundle) { - service = LeagueTeamCompService.getInstance(); - api = new ClientApi(); + registerLeagueClientListeners(); initializeRoleCompositions(); - registerLCUListeners(); initializeChampionSuggestions(); } - public void stop() { - api.stop(); - } - @SneakyThrows - private void registerLCUListeners() { + private void registerLeagueClientListeners() { + log.info("Registering League Client listeners"); api.addClientConnectionListener(new ClientConnectionListener() { @Override public void onClientConnected() { + log.info("League Client connected"); registerSocketListener(); } @Override public void onClientDisconnected() { + log.info("League Client disconnected"); if (socket != null) { socket.close(); } @@ -204,6 +242,7 @@ public void onClientDisconnected() { @SneakyThrows private void registerSocketListener() { + log.info("Registering socket listener"); if (!api.isAuthorized()) { log.warn("Not logged in!"); return; @@ -212,60 +251,73 @@ private void registerSocketListener() { socket.setSocketListener(new ClientWebSocket.SocketListener() { @Override public void onEvent(ClientWebSocket.Event event) { - Platform.runLater(() -> { - if (event.getEventType().equals("Update") && - event.getUri().equals("/lol-champ-select/v1/session") && - event.getData() instanceof LolChampSelectChampSelectSession session) { - updateTeam(session.myTeam, champSelect.getAllyTeam()); - updateBans(session.bans.myTeamBans, champSelect.getAllyBanList()); - updateTeam(session.theirTeam, champSelect.getEnemyTeam()); - updateBans(session.bans.theirTeamBans, champSelect.getEnemyBanList()); - } - }); + if (event.getEventType().equals("Update") && + event.getUri().equals("/lol-champ-select/v1/session") && + event.getData() instanceof LolChampSelectChampSelectSession session) { + Platform.runLater(() -> LeagueTeamCompController.this.onChampSelectUpdate(session)); + } } @Override public void onClose(int code, String reason) { - log.warn("Socket closed, reason: " + reason); + log.warn("Socket closed, code: {}, reason: {}", code, reason); } }); } - private void updateBans(List bans, TeamDTO teamBans) { - System.out.println("team: " + teamBans.getTeam()); - System.out.println("response bans: " + bans); + private void onChampSelectUpdate(LolChampSelectChampSelectSession session) { + if (!manualModeToggle.isSelected()) { + updateTeam(champSelect.getAllyTeam(), session.myTeam, session.bans.myTeamBans); + updateTeam(champSelect.getEnemyTeam(), session.theirTeam, session.bans.theirTeamBans); + } + } + + private void updateTeam( + TeamDTO team, + List playerSelectionList, + List bans + ) { + playerSelectionList.forEach(playerSelection -> service.findChampionDataById(playerSelection.championId) + .ifPresent(champion -> populateSummonerSlot(playerSelection, team, champion))); + updateBans(team, bans); + } + + private void updateBans(TeamDTO team, List bans) { + List bannedChampionIdsBefore = team.getBannedChampionIds(); + boolean shouldLog = Utils.notSame(bannedChampionIdsBefore, bans); + if (shouldLog) { + log.debug("team: {}", team.getTeamSide()); + log.debug("response bans: {}", bans); + } bans.stream() - .filter(championId -> teamBans.getSlots().stream() - .map(SlotDTO::getChampion) - .flatMap(Optional::stream) - .map(Champion::getId) + .filter(championId -> team.getBannedChampions().stream() + .map(ChampionDTO::getId) .noneMatch(championId::equals)) .map(service::findChampionDataById) .flatMap(Optional::stream) - .forEach(champion -> teamBans.getSlots().stream() + .forEach(champion -> team.getBans().stream() .filter(SlotDTO::isChampionNotSelected) .findFirst() .ifPresent(slot -> slot.setChampion(champion))); - System.out.println("teamBans: " + teamBans.getSlots().stream() - .map(SlotDTO::getChampion) - .flatMap(Optional::stream) - .map(Champion::getId) - .toList() + "\n"); + if (shouldLog) { + log.debug("teamBans: {}\n", team.getBannedChampionIds()); + } } - private void updateTeam(List session, TeamDTO team) { - session.forEach(playerSelection -> service.findChampionDataById(playerSelection.championId) - .ifPresent(champion -> { - int slotId = playerSelection.cellId.intValue(); - int slotIndex = slotId % 5; - SlotDTO slot = team.getSlot(slotIndex); - slot.setSlotId(slotId); - String summonerName = getSummonerName(playerSelection.summonerId); - slot.setSummonerName(summonerName); - slot.setChampion(champion); - })); + private void populateSummonerSlot( + LolChampSelectChampSelectPlayerSelection playerSelection, + TeamDTO team, + ChampionDTO champion + ) { + int slotId = playerSelection.cellId.intValue(); + int slotIndex = slotId % 5; + team.getSlot(slotIndex).getItem().ifPresent(summoner -> { + summoner.setSlotId(slotId); + summoner.setSummonerName(getSummonerName(playerSelection.summonerId)); + summoner.setChampion(champion); + }); } @SneakyThrows @@ -278,105 +330,167 @@ private String getSummonerName(Long summonerId) { .orElse(""); } - protected void onGenerateButtonClick() { + private void initializeRoleCompositions() { + log.info("Initializing role compositions"); + textArea.setEditable(false); + resetAllCheckboxes(); + generateButton.setOnAction(event -> onGenerateButtonClick()); + resetButton.setOnAction(event -> onResetButtonClick()); + } + + private void onGenerateButtonClick() { textArea.clear(); - var summonerNames = List.of( - tf1.getText(), - tf2.getText(), - tf3.getText(), - tf4.getText(), - tf5.getText() - ); - var playersToRoles = Stream.of( + Map> playersToRoles = getPlayersToRolesMap(); + + List> combinations = roleListGeneratorService.getCombinations(playersToRoles); + + int nameReservedSize = calculateSummonerNameReservedSize(playersToRoles.keySet()); + List rows = combinations.stream().map(row -> formatPLayerToRoleRow(row, nameReservedSize)).toList(); + List rowsWithSeparator = rows.stream().map(this::appendSeparator).toList(); + + StringBuilder sb = new StringBuilder(); + rowsWithSeparator.forEach(sb::append); + if (!rowsWithSeparator.isEmpty()) { + String lastRow = rows.get(rowsWithSeparator.size() - 1); + sb.append(getSeparator(lastRow)); + } + textArea.setText(sb.toString()); + } + + private Map> getPlayersToRolesMap() { + validateSummonerNames(); + return Map.ofEntries( Map.entry(tf1.getText(), getRoles(cbTop1, cbMid1, cbAdc1, cbSup1, cbJgl1)), Map.entry(tf2.getText(), getRoles(cbTop2, cbMid2, cbAdc2, cbSup2, cbJgl2)), Map.entry(tf3.getText(), getRoles(cbTop3, cbMid3, cbAdc3, cbSup3, cbJgl3)), Map.entry(tf4.getText(), getRoles(cbTop4, cbMid4, cbAdc4, cbSup4, cbJgl4)), Map.entry(tf5.getText(), getRoles(cbTop5, cbMid5, cbAdc5, cbSup5, cbJgl5)) - ).filter(entry -> !entry.getKey().isBlank()) - .filter(entry -> summonerNames.stream().filter(entry.getKey()::equals).count() == 1) + ).entrySet().stream() + .filter(entry -> !entry.getKey().isBlank()) .collect(toMap(Map.Entry::getKey, Map.Entry::getValue)); - var combinations = getCombinations(playersToRoles); - var nameReservedSize = Integer.min(16, IntStream.of( - tf1.getLength(), - tf2.getLength(), - tf3.getLength(), - tf4.getLength(), - tf5.getLength() - ).max().getAsInt()); - var rows = combinations.stream() - .map(playerToRoleRow -> formatPLayerToRoleRow(playerToRoleRow, nameReservedSize)) - .toList(); - var rowsWithSeparator = rows.stream().map(this::appendSeparator).toList(); - rowsWithSeparator.forEach(textArea::appendText); - if (!rowsWithSeparator.isEmpty()) { - var lastRow = rows.get(rowsWithSeparator.size() - 1); - textArea.appendText(getSeparator(lastRow)); + } + + private void validateSummonerNames() { + List summonerNames = List.of( + tf1.getText(), + tf2.getText(), + tf3.getText(), + tf4.getText(), + tf5.getText() + ); + Set summonerNameSet = Set.copyOf(summonerNames); + if (summonerNames.size() != summonerNameSet.size()) { + throw new LTCException("Summoner names must be unique!"); } } - protected void onResetButtonClick() { - textArea.clear(); - resetAllCheckboxes(); + private static List getRoles( + CheckBox cbTop, + CheckBox cbMid, + CheckBox cbAdc, + CheckBox cbSup, + CheckBox cbJgl + ) { + List roles = new ArrayList<>(); + if (cbTop.isSelected()) { + roles.add(TOP); + } + if (cbMid.isSelected()) { + roles.add(MID); + } + if (cbAdc.isSelected()) { + roles.add(ADC); + } + if (cbSup.isSelected()) { + roles.add(SUP); + } + if (cbJgl.isSelected()) { + roles.add(JGL); + } + return roles; } + private int calculateSummonerNameReservedSize(Set summonerNames) { + return Integer.min(16, summonerNames.stream().mapToInt(String::length).max().orElse(16)); + } - public void setStageAndSetupListeners(Stage stage) { - tabPane.setOnMousePressed(event -> { - xOffset = stage.getX() - event.getScreenX(); - yOffset = stage.getY() - event.getScreenY(); - }); - tabPane.setOnMouseDragged(event -> { - stage.setX(event.getScreenX() + xOffset); - stage.setY(event.getScreenY() + yOffset); - }); + private String formatPLayerToRoleRow( + Map playerToRoleRow, + int nameReservedSize + ) { + return playerToRoleRow.entrySet().stream() + .map(playerToRole -> formatPlayerToRoleEntry(playerToRole, nameReservedSize)) + .collect(joining(" ")); } - public void setCloseButtonAction(Runnable closeAction) { - closeButton.setOnAction(actionEvent -> closeAction.run()); + private String formatPlayerToRoleEntry(Map.Entry playerToRole, int nameReservedSize) { + return String.format("%" + nameReservedSize + "s", playerToRole.getKey()) + ": " + playerToRole.getValue().toString(); } - public void setMinimizeButtonAction(Runnable minimizeAction) { - minimizeButton.setOnAction(actionEvent -> minimizeAction.run()); + private String appendSeparator(String playerToRoleRow) { + return getSeparator(playerToRoleRow) + playerToRoleRow + "\n"; } - private void initializeRoleCompositions() { - gridPane.setAlignment(CENTER); - textArea.setEditable(false); + private String getSeparator(String playerToRoleRow) { + return "-".repeat(playerToRoleRow.length()) + "\n"; + } + + private void onResetButtonClick() { + textArea.clear(); resetAllCheckboxes(); - generateButton.setOnAction(actionEvent -> onGenerateButtonClick()); - resetButton.setOnAction(actionEvent -> onResetButtonClick()); } - public void initializeChampionSuggestions() { - champSelect = new ChampSelectDTO(); - ObservableList allyItems = FXCollections.observableArrayList(SlotDTO.extractor()); - ObservableList allyBanItems = FXCollections.observableArrayList(SlotDTO.extractor()); - ObservableList enemyItems = FXCollections.observableArrayList(SlotDTO.extractor()); - ObservableList enemyBanItems = FXCollections.observableArrayList(SlotDTO.extractor()); - allyItems.addAll(champSelect.getAllyTeam().getSlots()); - allyBanItems.addAll(champSelect.getAllyBanList().getSlots()); - enemyItems.addAll(champSelect.getEnemyTeam().getSlots()); - enemyBanItems.addAll(champSelect.getEnemyBanList().getSlots()); - allyListView.setItems(allyItems); - allyBanListView.setItems(allyBanItems); - enemyListView.setItems(enemyItems); - enemyBanListView.setItems(enemyBanItems); - allyListView.setCellFactory(param -> populateListCells(60, 50)); - allyBanListView.setCellFactory(param -> populateListCells(50, 40)); - enemyListView.setCellFactory(param -> populateListCells(60, 50)); - enemyBanListView.setCellFactory(param -> populateListCells(50, 40)); + private void resetAllCheckboxes() { + List.of( + cbTop1, cbMid1, cbAdc1, cbSup1, cbJgl1, + cbTop2, cbMid2, cbAdc2, cbSup2, cbJgl2, + cbTop3, cbMid3, cbAdc3, cbSup3, cbJgl3, + cbTop4, cbMid4, cbAdc4, cbSup4, cbJgl4, + cbTop5, cbMid5, cbAdc5, cbSup5, cbJgl5 + ).forEach(cb -> cb.setSelected(DEFAULT_CHECKBOX_STATE)); + } + public void initializeChampionSuggestions() { + log.info("Initializing champion suggestions"); + Utils.disableInteraction(allyListView); + Utils.disableInteraction(enemyListView); + Utils.disableInteraction(allyBanListView); + Utils.disableInteraction(enemyBanListView); + Utils.disableInteraction(suggestionsListView); + suggestionService.initializeChampionListView(champSelect.getAllyTeam().getSlots(), allyListView, 50); + suggestionService.initializeChampionListView(champSelect.getEnemyTeam().getSlots(), enemyListView, 50); + suggestionService.initializeChampionListView(champSelect.getAllyTeam().getBans(), allyBanListView, 40); + suggestionService.initializeChampionListView(champSelect.getEnemyTeam().getBans(), enemyBanListView, 40); + suggestionService.initializeSuggestionsListView(champSelect, suggestionsListView, 40); + manualModeToggle.setOnAction(event -> { + if (manualModeToggle.isSelected()) { + Utils.enableInteraction(allyListView); + Utils.enableInteraction(enemyListView); + Utils.enableInteraction(allyBanListView); + Utils.enableInteraction(enemyBanListView); + Utils.enableInteraction(suggestionsListView); + } else { + Utils.disableInteraction(allyListView); + Utils.disableInteraction(enemyListView); + Utils.disableInteraction(allyBanListView); + Utils.disableInteraction(enemyBanListView); + Utils.disableInteraction(suggestionsListView); + allyListView.getSelectionModel().clearSelection(); + enemyListView.getSelectionModel().clearSelection(); + allyBanListView.getSelectionModel().clearSelection(); + enemyBanListView.getSelectionModel().clearSelection(); + suggestionsListView.getItems().forEach(item -> item.getSelectionModel().clearSelection()); + } + }); initializeTemporaryTeamStats(); } private void initializeTemporaryTeamStats() { allyListView.getSelectionModel().setSelectionMode(MULTIPLE); enemyListView.getSelectionModel().setSelectionMode(MULTIPLE); - allyBanListView.setMouseTransparent(true); - allyBanListView.setFocusTraversable(false); - enemyBanListView.setMouseTransparent(true); - enemyBanListView.setFocusTraversable(false); + allyBanListView.getSelectionModel().setSelectionMode(MULTIPLE); + enemyBanListView.getSelectionModel().setSelectionMode(MULTIPLE); + suggestionsListView.getItems().forEach(item -> item.getSelectionModel().setSelectionMode(MULTIPLE)); allySearchField.setOnAction(actionEvent -> onSearchAction(allyListView, allySearchField.getText())); enemySearchField.setOnAction(actionEvent -> onSearchAction(enemyListView, enemySearchField.getText())); @@ -386,13 +500,85 @@ private void initializeTemporaryTeamStats() { allyListView.setOnKeyPressed(event -> handleDeleteButton(event, allyListView)); enemyListView.setOnKeyPressed(event -> handleDeleteButton(event, enemyListView)); + allyBanListView.setOnKeyPressed(event -> handleDeleteButton(event, allyBanListView)); + enemyBanListView.setOnKeyPressed(event -> handleDeleteButton(event, enemyBanListView)); + suggestionsListView.getItems().forEach(item -> item.setOnKeyPressed(event -> handleDeleteButton(event, item))); getChampionListChangeListener().onChanged(null); allyListView.getItems().addListener(getChampionListChangeListener()); enemyListView.getItems().addListener(getChampionListChangeListener()); } - private ListChangeListener getChampionListChangeListener() { + private void onSearchAction( + ListView> championListView, + String searchFieldText + ) { + if (isValidSearchFieldText(championListView.getItems(), searchFieldText)) { + log.debug("Search field text \"{}\" is valid", searchFieldText); + service.findChampionDataByName(searchFieldText) + .ifPresentOrElse( + champion -> getSearchActionItems(championListView).stream() + .filter(ChampionHolder::isChampionNotSelected) + .findFirst() + .ifPresentOrElse( + slot -> { + log.debug("Setting champion {} in slot {}", champion.getName(), slot); + slot.setChampion(champion); + log.debug("Champion {} set in slot {}", champion.getName(), slot); + }, + () -> log.debug("No empty slots found") + ), + () -> log.debug("Champion not found for name: {}", searchFieldText)); + } else { + log.debug("Search field text is not valid: {}", searchFieldText); + } + } + + private boolean isValidSearchFieldText(List> slots, String searchFieldText) { + List championKeys = slots.stream() + .map(ChampionHolder::getChampion) + .flatMap(Optional::stream) + .map(ChampionDTO::getKey) + .toList(); + long filledSlots = slots.stream().filter(ChampionHolder::isChampionSelected).count(); + return filledSlots < 5 + && !championKeys.contains(searchFieldText) + && service.existsChampionDataByName(searchFieldText) + && !service.findChampionDataByName(searchFieldText) + .map(champSelect.getChampionPool()::contains) + .orElse(false); + } + + private ObservableList> getSearchActionItems(ListView> championListView) { + ObservableList> selectedItems = championListView.getSelectionModel().getSelectedItems(); + return selectedItems.isEmpty() || selectedItems.stream().noneMatch(ChampionHolder::isChampionNotSelected) + ? championListView.getItems() + : selectedItems; + } + + private List searchFieldAutoCompletion(String userText) { + List championPoolKeys = champSelect.getChampionPool().stream() + .map(ChampionDTO::getKey) + .toList(); + return service.getAllChampionKeys().stream() + .filter(not(championPoolKeys::contains)) + .map(service::findChampionDataByKey) + .flatMap(Optional::stream) + .map(ChampionDTO::getName) + .filter(championName -> championName.toLowerCase().startsWith(userText.toLowerCase())) + .collect(toList()); + } + + public void handleDeleteButton(KeyEvent event, ListView> listView) { + if (event.getCode() == KeyCode.DELETE) { + ObservableList> selectedItems = listView.getSelectionModel().getSelectedItems(); + if (!selectedItems.isEmpty()) { + selectedItems.forEach(Clearable::clear); + } + } + } + + private ListChangeListener> getChampionListChangeListener() { return change -> { setTeamAttributeRatings(champSelect.getAllyTeam()); setTeamAttributeRatings(champSelect.getEnemyTeam()); @@ -418,16 +604,24 @@ private ListChangeListener getChampionListChangeListener() { }; } - private long getBadStatCount(TextFlow textFlow) { - return textFlow.getChildren().stream() - .map(Node::getPseudoClassStates) - .filter(pseudoClasses -> pseudoClasses.contains(CHAMPION_STAT_BAD_PSEUDO_CLASS)) - .count(); + private void setTeamAttributeRatings(TeamDTO team) { + AttributeRatingsDTO enemyTeamStats = team.getAttributeRatings(); + List enemyChampionKeys = team.getChampions().stream().map(ChampionDTO::getKey).toList(); + enemyTeamStats.setDamage(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getDamage, 3)); + enemyTeamStats.setAttack(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getAttack, 10)); + enemyTeamStats.setDefense(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getDefense, 10)); + enemyTeamStats.setMagic(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getMagic, 10)); + enemyTeamStats.setDifficulty(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getDifficulty, 3)); + enemyTeamStats.setControl(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getControl, 3)); + enemyTeamStats.setToughness(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getToughness, 3)); + enemyTeamStats.setMobility(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getMobility, 3)); + enemyTeamStats.setUtility(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getUtility, 3)); + enemyTeamStats.setAbilityReliance(service.getChampionStatValue(enemyChampionKeys, AttributeRatingsDTO::getAbilityReliance, 100)); } private void calculateTeamStats( - AttributeRatings stats, - AttributeRatings opponentStats, + AttributeRatingsDTO stats, + AttributeRatingsDTO opponentStats, TextFlow textFlow ) { Text damageText = getStatText("Damage:", stats.getDamage()); @@ -465,43 +659,6 @@ private void calculateTeamStats( textFlow.getChildren().add(abilityRelianceText); } - private void setTeamAttributeRatings(TeamDTO champSelect) { - AttributeRatings enemyTeamStats = champSelect.getAttributeRatings(); - List enemyChampionKeys = champSelect.getSlots().stream() - .map(SlotDTO::getChampion) - .flatMap(Optional::stream) - .map(Champion::getKey) - .toList(); - enemyTeamStats.setDamage(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getDamage, 3)); - enemyTeamStats.setAttack(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getAttack, 10)); - enemyTeamStats.setDefense(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getDefense, 10)); - enemyTeamStats.setMagic(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getMagic, 10)); - enemyTeamStats.setDifficulty(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getDifficulty, 3)); - enemyTeamStats.setControl(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getControl, 3)); - enemyTeamStats.setToughness(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getToughness, 3)); - enemyTeamStats.setMobility(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getMobility, 3)); - enemyTeamStats.setUtility(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getUtility, 3)); - enemyTeamStats.setAbilityReliance(service.getChampionInfoValue(enemyChampionKeys, AttributeRatings::getAbilityReliance, 100)); - } - - private void colorStatText( - Text statText, - double value, - double opponentValue, - DoubleBinaryOperator operation - ) { - if (value == opponentValue) { - statText.pseudoClassStateChanged(CHAMPION_STAT_BAD_PSEUDO_CLASS, false); - statText.pseudoClassStateChanged(CHAMPION_STAT_GOOD_PSEUDO_CLASS, false); - } else if (operation.applyAsDouble(value, opponentValue) == value) { - statText.pseudoClassStateChanged(CHAMPION_STAT_BAD_PSEUDO_CLASS, false); - statText.pseudoClassStateChanged(CHAMPION_STAT_GOOD_PSEUDO_CLASS, true); - } else { - statText.pseudoClassStateChanged(CHAMPION_STAT_BAD_PSEUDO_CLASS, true); - statText.pseudoClassStateChanged(CHAMPION_STAT_GOOD_PSEUDO_CLASS, false); - } - } - private Text getStatText(String label, double value) { return new Text(getStatString(label, value)); } @@ -518,161 +675,28 @@ private String formatStatValue(double value) { return String.format("%4s / 10", String.format("%1$,.1f", value)); } - private ListCell populateListCells(int emptySlotCellPrefSize, final int imageSize) { - return new ListCell<>() { - private final ImageView imageView = new ImageView(); - - @Override - public void updateItem(SlotDTO slot, boolean empty) { - super.updateItem(slot, empty); - if (empty || slot == null) { - setPrefHeight(emptySlotCellPrefSize); - setPrefWidth(emptySlotCellPrefSize); - setGraphic(null); - } else { - imageView.setFitWidth(imageSize); - imageView.setFitHeight(imageSize); - imageView.setImage(slot.getImage()); - setGraphic(imageView); - } - } - }; - } - - public void handleDeleteButton(KeyEvent event, ListView listView) { - if (event.getCode() == KeyCode.DELETE) { - var selectedItems = listView.getSelectionModel().getSelectedItems(); - if (!selectedItems.isEmpty()) { - selectedItems.forEach(SlotDTO::clear); - } - } - } - - private String formatPLayerToRoleRow(List> playerToRoleRow, - int nameReservedSize) { - return playerToRoleRow.stream() - .map(playerToRole -> formatPlayerToRoleEntry(playerToRole, nameReservedSize)) - .collect(joining(" ")); - } - - private String formatPlayerToRoleEntry(Map.Entry playerToRole, int nameReservedSize) { - return String.format("%" + nameReservedSize + "s", playerToRole.getKey()) + ": " + playerToRole.getValue().toString(); - } - - private String appendSeparator(String playerToRoleRow) { - return getSeparator(playerToRoleRow) + playerToRoleRow + "\n"; - } - - private String getSeparator(String playerToRoleRow) { - return "-".repeat(playerToRoleRow.length()) + "\n"; - } - - private void onSearchAction( - ListView championListView, - String searchFieldText - ) { - - if (isValidSearchFieldText(championListView.getItems(), searchFieldText)) { - service.findChampionDataByName(searchFieldText) - .ifPresent(champion -> getSearchActionItems(championListView).stream() - .filter(SlotDTO::isChampionNotSelected) - .findFirst() - .ifPresent(slot -> slot.setChampion(champion))); - } - } - - private ObservableList getSearchActionItems(ListView championListView) { - ObservableList selectedItems = championListView.getSelectionModel().getSelectedItems(); - return selectedItems.isEmpty() || selectedItems.stream() - .map(SlotDTO::getChampion) - .noneMatch(Optional::isEmpty) - ? championListView.getItems() - : selectedItems; - } - - private boolean isValidSearchFieldText(List slots, String searchFieldText) { - List championKeys = slots.stream() - .map(SlotDTO::getChampion) - .flatMap(Optional::stream) - .map(Champion::getKey) - .toList(); - long filledSlots = slots.stream().filter(SlotDTO::isChampionSelected).count(); - return filledSlots < 5 - && !championKeys.contains(searchFieldText) - && service.existsChampionDataByName(searchFieldText) - && !service.findChampionDataByName(searchFieldText) - .map(champSelect.getChampionPool()::contains) - .orElse(false); - } - - private List searchFieldAutoCompletion(String userText) { - List championPoolKeys = champSelect.getChampionPool().stream() - .map(Champion::getKey) - .toList(); - return service.getAllChampionKeys().stream() - .filter(not(championPoolKeys::contains)) - .map(service::findChampionDataByKey) - .flatMap(Optional::stream) - .map(Champion::getName) - .filter(championName -> championName.toLowerCase().startsWith(userText.toLowerCase())) - .collect(toList()); - } - - private static List getRoles( - CheckBox cbTop, - CheckBox cbMid, - CheckBox cbAdc, - CheckBox cbSup, - CheckBox cbJgl + private void colorStatText( + Text statText, + double value, + double opponentValue, + DoubleBinaryOperator operation ) { - var roles = new ArrayList(); - if (cbTop.isSelected()) { - roles.add(TOP); - } - if (cbMid.isSelected()) { - roles.add(MID); - } - if (cbAdc.isSelected()) { - roles.add(ADC); - } - if (cbSup.isSelected()) { - roles.add(SUP); - } - if (cbJgl.isSelected()) { - roles.add(JGL); + if (value == opponentValue) { + statText.pseudoClassStateChanged(CHAMPION_STAT_BAD_PSEUDO_CLASS, false); + statText.pseudoClassStateChanged(CHAMPION_STAT_GOOD_PSEUDO_CLASS, false); + } else if (operation.applyAsDouble(value, opponentValue) == value) { + statText.pseudoClassStateChanged(CHAMPION_STAT_BAD_PSEUDO_CLASS, false); + statText.pseudoClassStateChanged(CHAMPION_STAT_GOOD_PSEUDO_CLASS, true); + } else { + statText.pseudoClassStateChanged(CHAMPION_STAT_BAD_PSEUDO_CLASS, true); + statText.pseudoClassStateChanged(CHAMPION_STAT_GOOD_PSEUDO_CLASS, false); } - return roles; } - private void resetAllCheckboxes() { - cbTop1.setSelected(DEFAULT_CHECKBOX_STATE); - cbMid1.setSelected(DEFAULT_CHECKBOX_STATE); - cbAdc1.setSelected(DEFAULT_CHECKBOX_STATE); - cbSup1.setSelected(DEFAULT_CHECKBOX_STATE); - cbJgl1.setSelected(DEFAULT_CHECKBOX_STATE); - - cbTop2.setSelected(DEFAULT_CHECKBOX_STATE); - cbMid2.setSelected(DEFAULT_CHECKBOX_STATE); - cbAdc2.setSelected(DEFAULT_CHECKBOX_STATE); - cbSup2.setSelected(DEFAULT_CHECKBOX_STATE); - cbJgl2.setSelected(DEFAULT_CHECKBOX_STATE); - - cbTop3.setSelected(DEFAULT_CHECKBOX_STATE); - cbMid3.setSelected(DEFAULT_CHECKBOX_STATE); - cbAdc3.setSelected(DEFAULT_CHECKBOX_STATE); - cbSup3.setSelected(DEFAULT_CHECKBOX_STATE); - cbJgl3.setSelected(DEFAULT_CHECKBOX_STATE); - - cbTop4.setSelected(DEFAULT_CHECKBOX_STATE); - cbMid4.setSelected(DEFAULT_CHECKBOX_STATE); - cbAdc4.setSelected(DEFAULT_CHECKBOX_STATE); - cbSup4.setSelected(DEFAULT_CHECKBOX_STATE); - cbJgl4.setSelected(DEFAULT_CHECKBOX_STATE); - - cbTop5.setSelected(DEFAULT_CHECKBOX_STATE); - cbMid5.setSelected(DEFAULT_CHECKBOX_STATE); - cbAdc5.setSelected(DEFAULT_CHECKBOX_STATE); - cbSup5.setSelected(DEFAULT_CHECKBOX_STATE); - cbJgl5.setSelected(DEFAULT_CHECKBOX_STATE); + private long getBadStatCount(TextFlow textFlow) { + return textFlow.getChildren().stream() + .map(Node::getPseudoClassStates) + .filter(pseudoClasses -> pseudoClasses.contains(CHAMPION_STAT_BAD_PSEUDO_CLASS)) + .count(); } } \ No newline at end of file diff --git a/src/main/java/com/st4s1k/leagueteamcomp/exceptions/LTCException.java b/src/main/java/com/st4s1k/leagueteamcomp/exceptions/LTCException.java new file mode 100644 index 0000000..5a3e225 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/exceptions/LTCException.java @@ -0,0 +1,7 @@ +package com.st4s1k.leagueteamcomp.exceptions; + +public class LTCException extends RuntimeException { + public LTCException(String message) { + super(message); + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/AttributeRatings.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/AttributeRatingsDTO.java similarity index 95% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/AttributeRatings.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/champion/AttributeRatingsDTO.java index 07f2cbb..fcecd5f 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/AttributeRatings.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/AttributeRatingsDTO.java @@ -4,7 +4,7 @@ import lombok.Data; @Data -public class AttributeRatings { +public class AttributeRatingsDTO { @SerializedName("damage") private Double damage; @SerializedName("toughness") diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Champion.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Champion.java deleted file mode 100644 index 5d0a33f..0000000 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Champion.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.st4s1k.leagueteamcomp.model.champion; - -import com.google.gson.annotations.SerializedName; -import com.st4s1k.leagueteamcomp.model.champion.enums.AdaptiveType; -import com.st4s1k.leagueteamcomp.model.champion.enums.AttackType; -import com.st4s1k.leagueteamcomp.model.champion.enums.Resource; -import com.st4s1k.leagueteamcomp.model.champion.enums.Role; -import javafx.scene.image.Image; -import lombok.Data; - -import java.util.List; - -@Data -public class Champion { - - @SerializedName("id") - private Integer id; - @SerializedName("key") - private String key; - @SerializedName("name") - private String name; - @SerializedName("attributeRatings") - private AttributeRatings attributeRatings; - @SerializedName("roles") - private List roles; - @SerializedName("resource") - private Resource resource; - @SerializedName("attackType") - private AttackType attackType; - @SerializedName("adaptiveType") - private AdaptiveType adaptiveType; - @SerializedName("stats") - private Stats stats; - @SerializedName("icon") - private String iconUrl; - - private transient Image image; -} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/ChampionDTO.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/ChampionDTO.java new file mode 100644 index 0000000..5507452 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/ChampionDTO.java @@ -0,0 +1,41 @@ +package com.st4s1k.leagueteamcomp.model.champion; + +import com.google.gson.annotations.SerializedName; +import com.st4s1k.leagueteamcomp.model.enums.AdaptiveTypeEnum; +import com.st4s1k.leagueteamcomp.model.enums.AttackTypeEnum; +import com.st4s1k.leagueteamcomp.model.enums.ResourceEnum; +import com.st4s1k.leagueteamcomp.model.enums.RoleEnum; +import com.st4s1k.leagueteamcomp.model.interfaces.SlotItem; +import javafx.scene.image.Image; +import lombok.Data; +import lombok.ToString; + +import java.util.List; + +@Data +@ToString(onlyExplicitlyIncluded = true) +public class ChampionDTO implements SlotItem { + @SerializedName("id") + private Integer id; + @SerializedName("key") + private String key; + @ToString.Include + @SerializedName("name") + private String name; + @SerializedName("attributeRatings") + private AttributeRatingsDTO attributeRatings; + @SerializedName("roles") + private List roles; + @SerializedName("resource") + private ResourceEnum resource; + @SerializedName("attackType") + private AttackTypeEnum attackType; + @SerializedName("adaptiveType") + private AdaptiveTypeEnum adaptiveType; + @SerializedName("stats") + private StatsDTO stats; + @SerializedName("icon") + private String iconUrl; + + private transient Image image; +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Champions.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/ChampionsDTO.java similarity index 71% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/Champions.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/champion/ChampionsDTO.java index bf6bbdc..02daff5 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Champions.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/ChampionsDTO.java @@ -6,8 +6,7 @@ import java.util.Map; @Data -public class Champions { - +public class ChampionsDTO { @SerializedName("champions") - private Map champions; + private Map champions; } diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Stat.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/StatDTO.java similarity index 94% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/Stat.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/champion/StatDTO.java index 364f6bd..56697a0 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Stat.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/StatDTO.java @@ -4,7 +4,7 @@ import lombok.Data; @Data -public class Stat { +public class StatDTO { @SerializedName("flat") private double flat; @SerializedName("percent") diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Stats.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/StatsDTO.java similarity index 53% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/Stats.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/champion/StatsDTO.java index 7df0878..e4a452f 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/Stats.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/StatsDTO.java @@ -4,61 +4,61 @@ import lombok.Data; @Data -public class Stats { +public class StatsDTO { @SerializedName("health") - private Stat health; + private StatDTO health; @SerializedName("healthRegen") - private Stat healthRegen; + private StatDTO healthRegen; @SerializedName("mana") - private Stat mana; + private StatDTO mana; @SerializedName("manaRegen") - private Stat manaRegen; + private StatDTO manaRegen; @SerializedName("armor") - private Stat armor; + private StatDTO armor; @SerializedName("magicResistance") - private Stat magicResistance; + private StatDTO magicResistance; @SerializedName("attackDamage") - private Stat attackDamage; + private StatDTO attackDamage; @SerializedName("movespeed") - private Stat moveSpeed; + private StatDTO moveSpeed; @SerializedName("acquisitionRadius") - private Stat acquisitionRadius; + private StatDTO acquisitionRadius; @SerializedName("selectionRadius") - private Stat selectionRadius; + private StatDTO selectionRadius; @SerializedName("pathingRadius") - private Stat pathingRadius; + private StatDTO pathingRadius; @SerializedName("gameplayRadius") - private Stat gameplayRadius; + private StatDTO gameplayRadius; @SerializedName("criticalStrikeDamage") - private Stat criticalStrikeDamage; + private StatDTO criticalStrikeDamage; @SerializedName("criticalStrikeDamageModifier") - private Stat criticalStrikeDamageModifier; + private StatDTO criticalStrikeDamageModifier; @SerializedName("attackSpeed") - private Stat attackSpeed; + private StatDTO attackSpeed; @SerializedName("attackSpeedRatio") - private Stat attackSpeedRatio; + private StatDTO attackSpeedRatio; @SerializedName("attackCastTime") - private Stat attackCastTime; + private StatDTO attackCastTime; @SerializedName("attackTotalTime") - private Stat attackTotalTime; + private StatDTO attackTotalTime; @SerializedName("attackDelayOffset") - private Stat attackDelayOffset; + private StatDTO attackDelayOffset; @SerializedName("attackRange") - private Stat attackRange; + private StatDTO attackRange; @SerializedName("aramDamageTaken") - private Stat aramDamageTaken; + private StatDTO aramDamageTaken; @SerializedName("aramDamageDealt") - private Stat aramDamageDealt; + private StatDTO aramDamageDealt; @SerializedName("aramHealing") - private Stat aramHealing; + private StatDTO aramHealing; @SerializedName("aramShielding") - private Stat aramShielding; + private StatDTO aramShielding; @SerializedName("urfDamageTaken") - private Stat urfDamageTaken; + private StatDTO urfDamageTaken; @SerializedName("urfDamageDealt") - private Stat urfDamageDealt; + private StatDTO urfDamageDealt; @SerializedName("urfHealing") - private Stat urfHealing; + private StatDTO urfHealing; @SerializedName("urfShielding") - private Stat urfShielding; + private StatDTO urfShielding; } diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/ChampSelectDTO.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/ChampSelectDTO.java index dfa5f8d..23bd67a 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/ChampSelectDTO.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/ChampSelectDTO.java @@ -1,6 +1,7 @@ package com.st4s1k.leagueteamcomp.model.champion.select; -import com.st4s1k.leagueteamcomp.model.champion.Champion; +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import com.st4s1k.leagueteamcomp.model.interfaces.Clearable; import lombok.Getter; import lombok.NoArgsConstructor; @@ -9,29 +10,40 @@ import java.util.Optional; import java.util.stream.Stream; -import static com.st4s1k.leagueteamcomp.model.champion.select.TeamDTO.Team.ALLY_TEAM; -import static com.st4s1k.leagueteamcomp.model.champion.select.TeamDTO.Team.ENEMY_TEAM; +import static com.st4s1k.leagueteamcomp.model.enums.TeamSideEnum.ALLY_TEAM; +import static com.st4s1k.leagueteamcomp.model.enums.TeamSideEnum.ENEMY_TEAM; @Getter @NoArgsConstructor -public class ChampSelectDTO { +public class ChampSelectDTO implements Clearable { private final TeamDTO allyTeam = new TeamDTO(ALLY_TEAM); - private final TeamDTO allyBanList = new TeamDTO(ALLY_TEAM); - private final TeamDTO enemyTeam = new TeamDTO(ENEMY_TEAM); - private final TeamDTO enemyBanList = new TeamDTO(ENEMY_TEAM); - public List getChampionPool() { + public List getChampionPool() { return Stream.of( - allyTeam.getSlots(), - allyBanList.getSlots(), - enemyTeam.getSlots(), - enemyBanList.getSlots() - ) - .flatMap(Collection::stream) - .map(SlotDTO::getChampion) - .flatMap(Optional::stream) + allyTeam.getSlots().stream() + .map(SlotDTO::getChampion) + .flatMap(Optional::stream) + .toList(), + enemyTeam.getSlots().stream() + .map(SlotDTO::getChampion) + .flatMap(Optional::stream) + .toList(), + allyTeam.getBans().stream() + .map(SlotDTO::getChampion) + .flatMap(Optional::stream) + .toList(), + enemyTeam.getBans().stream() + .map(SlotDTO::getChampion) + .flatMap(Optional::stream) + .toList() + ).flatMap(Collection::stream) .toList(); } + + public void clear() { + allyTeam.clear(); + enemyTeam.clear(); + } } diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/ChampionSlotDTO.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/ChampionSlotDTO.java new file mode 100644 index 0000000..9e067aa --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/ChampionSlotDTO.java @@ -0,0 +1,31 @@ +package com.st4s1k.leagueteamcomp.model.champion.select; + +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Function; + +@ToString(callSuper = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true) +public class ChampionSlotDTO extends SlotDTO { + + private ChampionSlotDTO( + Function> championGetter, + BiConsumer> championSetter + ) { + super(championGetter, championSetter); + } + + public static ChampionSlotDTO newSlot() { + return new ChampionSlotDTO(Optional::ofNullable, (item, slot) -> slot.setItem(item)); + } + + public static ChampionSlotDTO of(ChampionDTO champion) { + ChampionSlotDTO championSlot = new ChampionSlotDTO(Optional::ofNullable, (item, slot) -> slot.setItem(item)); + championSlot.setChampion(champion); + return championSlot; + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SlotDTO.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SlotDTO.java index b6ea896..cdf85ab 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SlotDTO.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SlotDTO.java @@ -1,58 +1,77 @@ package com.st4s1k.leagueteamcomp.model.champion.select; -import com.st4s1k.leagueteamcomp.LeagueTeamCompApplication; -import com.st4s1k.leagueteamcomp.model.champion.Champion; +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import com.st4s1k.leagueteamcomp.model.interfaces.ChampionHolder; +import com.st4s1k.leagueteamcomp.model.interfaces.Clearable; +import com.st4s1k.leagueteamcomp.model.interfaces.ObservablesProvider; +import com.st4s1k.leagueteamcomp.model.interfaces.SlotItem; +import com.st4s1k.leagueteamcomp.utils.Resources; import javafx.beans.Observable; import javafx.beans.property.SimpleObjectProperty; import javafx.scene.image.Image; -import javafx.util.Callback; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Function; -import static java.util.Objects.requireNonNull; +import static lombok.AccessLevel.PACKAGE; @Data +@RequiredArgsConstructor(access = PACKAGE) +@ToString(onlyExplicitlyIncluded = true) @EqualsAndHashCode(onlyExplicitlyIncluded = true) -public class SlotDTO { - private static final String DEFAULT_IMAGE_URL = "helmet_bro.png"; - private static final Image EMPTY_SLOT_IMAGE = - new Image(requireNonNull(LeagueTeamCompApplication.class.getResourceAsStream(DEFAULT_IMAGE_URL))); +public class SlotDTO implements SlotItem, ChampionHolder { - private int slotId; + private final SimpleObjectProperty itemProperty = new SimpleObjectProperty<>(); + private final Function> championGetter; + private final BiConsumer> championSetter; + @ToString.Include @EqualsAndHashCode.Include - private SimpleObjectProperty championProperty = new SimpleObjectProperty<>(); - - @EqualsAndHashCode.Include - private String summonerName; - - public Optional getChampion() { - return Optional.ofNullable(championProperty.get()); + public Optional getItem() { + return Optional.ofNullable(itemProperty.get()); } - public void setChampion(Champion champion) { - this.championProperty.set(champion); + public void setItem(T value) { + itemProperty.set(value); } - public Image getImage() { - return getChampion().map(Champion::getImage).orElse(EMPTY_SLOT_IMAGE); + @Override + public void clear() { + getItem().ifPresent(Clearable::clear); } - public boolean isChampionSelected() { - return getChampion().isPresent(); + @Override + public Observable[] getObservables() { + List observables = new ArrayList<>(); + observables.add(itemProperty); + getItem() + .map(ObservablesProvider::getObservables) + .map(Arrays::asList) + .ifPresent(observables::addAll); + return observables.toArray(Observable[]::new); } - public boolean isChampionNotSelected() { - return getChampion().isEmpty(); + @Override + public Image getImage() { + return getItem().map(SlotItem::getImage).orElse(Resources.EMPTY_SLOT_IMAGE); } - public void clear() { - championProperty.set(null); + @Override + @EqualsAndHashCode.Include + public Optional getChampion() { + return getItem().flatMap(championGetter); } - public static Callback extractor() { - return param -> new Observable[]{param.championProperty, param.getImage().progressProperty()}; + @Override + public void setChampion(ChampionDTO champion) { + championSetter.accept(champion, this); } } diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SummonerDTO.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SummonerDTO.java new file mode 100644 index 0000000..16d692c --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SummonerDTO.java @@ -0,0 +1,65 @@ +package com.st4s1k.leagueteamcomp.model.champion.select; + +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import com.st4s1k.leagueteamcomp.model.interfaces.ChampionHolder; +import com.st4s1k.leagueteamcomp.model.interfaces.SlotItem; +import com.st4s1k.leagueteamcomp.utils.Resources; +import javafx.beans.Observable; +import javafx.beans.property.SimpleObjectProperty; +import javafx.scene.image.Image; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.List; +import java.util.Optional; + +@Data +@ToString(onlyExplicitlyIncluded = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +public class SummonerDTO implements SlotItem, ChampionHolder { + + @ToString.Include + @EqualsAndHashCode.Include + private String summonerName; + + private int slotId = -1; + + private final List> championSuggestions = List.of( + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot() + ); + + private SimpleObjectProperty selectedChampionProperty = new SimpleObjectProperty<>(); + + @Override + public Observable[] getObservables() { + return new Observable[]{selectedChampionProperty}; + } + + @Override + public Image getImage() { + return getChampion().map(ChampionDTO::getImage).orElse(Resources.EMPTY_SLOT_IMAGE); + } + + @Override + @ToString.Include + @EqualsAndHashCode.Include + public Optional getChampion() { + return Optional.ofNullable(selectedChampionProperty.get()); + } + + @Override + public void setChampion(ChampionDTO champion) { + selectedChampionProperty.set(champion); + } + + @Override + public void clear() { + slotId = -1; + selectedChampionProperty.set(null); + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SummonerSlotDTO.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SummonerSlotDTO.java new file mode 100644 index 0000000..d7ee07d --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/SummonerSlotDTO.java @@ -0,0 +1,33 @@ +package com.st4s1k.leagueteamcomp.model.champion.select; + +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Function; + +@ToString(callSuper = true) +@EqualsAndHashCode(onlyExplicitlyIncluded = true, callSuper = true) +public class SummonerSlotDTO extends SlotDTO { + private SummonerSlotDTO( + Function> championGetter, + BiConsumer> championSetter + ) { + super(championGetter, championSetter); + } + + public static SummonerSlotDTO newSlot() { + return of(new SummonerDTO()); + } + + public static SummonerSlotDTO of(SummonerDTO summoner) { + SummonerSlotDTO championSlot = new SummonerSlotDTO( + SummonerDTO::getChampion, + (champion, slot) -> slot.getItem().orElseThrow().setChampion(champion) + ); + championSlot.setItem(summoner); + return championSlot; + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/TeamDTO.java b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/TeamDTO.java index 6a87fdd..f90dd58 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/TeamDTO.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/champion/select/TeamDTO.java @@ -1,36 +1,67 @@ package com.st4s1k.leagueteamcomp.model.champion.select; -import com.st4s1k.leagueteamcomp.model.champion.AttributeRatings; +import com.st4s1k.leagueteamcomp.model.champion.AttributeRatingsDTO; +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import com.st4s1k.leagueteamcomp.model.enums.TeamSideEnum; +import com.st4s1k.leagueteamcomp.model.interfaces.Clearable; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; +import java.util.Optional; @Data @NoArgsConstructor -public class TeamDTO { - enum Team { - UNDEFINED, - ALLY_TEAM, - ENEMY_TEAM - } +public class TeamDTO implements Clearable { - private final List slots = List.of( - new SlotDTO(), - new SlotDTO(), - new SlotDTO(), - new SlotDTO(), - new SlotDTO() + private final List> slots = List.of( + SummonerSlotDTO.newSlot(), + SummonerSlotDTO.newSlot(), + SummonerSlotDTO.newSlot(), + SummonerSlotDTO.newSlot(), + SummonerSlotDTO.newSlot() + ); + private final List> bans = List.of( + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot(), + ChampionSlotDTO.newSlot() ); - private Team team = Team.UNDEFINED; - public TeamDTO(Team team) { - this.team = team; + private TeamSideEnum teamSide = TeamSideEnum.UNDEFINED; + + public TeamDTO(TeamSideEnum teamSide) { + this.teamSide = teamSide; } - private final AttributeRatings attributeRatings = new AttributeRatings(); + private final AttributeRatingsDTO attributeRatings = new AttributeRatingsDTO(); - public SlotDTO getSlot(int slotId) { + public SlotDTO getSlot(int slotId) { return slots.get(slotId); } + + public List getChampions() { + return slots.stream() + .map(SlotDTO::getItem) + .flatMap(Optional::stream) + .map(SummonerDTO::getChampion) + .flatMap(Optional::stream) + .toList(); + } + + public List getBannedChampions() { + return bans.stream().map(SlotDTO::getItem).flatMap(Optional::stream).toList(); + } + + public List getBannedChampionIds() { + return bans.stream().map(SlotDTO::getItem).flatMap(Optional::stream).map(ChampionDTO::getId).toList(); + } + + @Override + public void clear() { + slots.forEach(SlotDTO::clear); + bans.forEach(SlotDTO::clear); + teamSide = TeamSideEnum.UNDEFINED; + } } diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/AdaptiveType.java b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/AdaptiveTypeEnum.java similarity index 66% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/AdaptiveType.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/enums/AdaptiveTypeEnum.java index ec7633c..f09f28f 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/AdaptiveType.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/AdaptiveTypeEnum.java @@ -1,8 +1,8 @@ -package com.st4s1k.leagueteamcomp.model.champion.enums; +package com.st4s1k.leagueteamcomp.model.enums; import com.google.gson.annotations.SerializedName; -public enum AdaptiveType { +public enum AdaptiveTypeEnum { @SerializedName("PHYSICAL_DAMAGE") PHYSICAL_DAMAGE, @SerializedName("MAGIC_DAMAGE") diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/AttackType.java b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/AttackTypeEnum.java similarity index 62% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/AttackType.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/enums/AttackTypeEnum.java index 6529791..dac131f 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/AttackType.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/AttackTypeEnum.java @@ -1,8 +1,8 @@ -package com.st4s1k.leagueteamcomp.model.champion.enums; +package com.st4s1k.leagueteamcomp.model.enums; import com.google.gson.annotations.SerializedName; -public enum AttackType { +public enum AttackTypeEnum { @SerializedName("MELEE") MELEE, @SerializedName("RANGED") diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/Resource.java b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/ResourceEnum.java similarity index 89% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/Resource.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/enums/ResourceEnum.java index f6570f5..b4d9bfc 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/Resource.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/ResourceEnum.java @@ -1,8 +1,8 @@ -package com.st4s1k.leagueteamcomp.model.champion.enums; +package com.st4s1k.leagueteamcomp.model.enums; import com.google.gson.annotations.SerializedName; -public enum Resource { +public enum ResourceEnum { @SerializedName("BLOOD_WELL") BLOOD_WELL, @SerializedName("BLOODTHIRST") diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/Role.java b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/RoleEnum.java similarity index 91% rename from src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/Role.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/enums/RoleEnum.java index 2292ba7..6d864d3 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/champion/enums/Role.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/RoleEnum.java @@ -1,8 +1,8 @@ -package com.st4s1k.leagueteamcomp.model.champion.enums; +package com.st4s1k.leagueteamcomp.model.enums; import com.google.gson.annotations.SerializedName; -public enum Role { +public enum RoleEnum { @SerializedName("ARTILLERY") ARTILLERY, @SerializedName("ASSASSIN") diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/SummonerRole.java b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/SummonerRoleEnum.java similarity index 62% rename from src/main/java/com/st4s1k/leagueteamcomp/model/SummonerRole.java rename to src/main/java/com/st4s1k/leagueteamcomp/model/enums/SummonerRoleEnum.java index 2f3eec6..cfb3ba8 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/model/SummonerRole.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/SummonerRoleEnum.java @@ -1,6 +1,6 @@ -package com.st4s1k.leagueteamcomp.model; +package com.st4s1k.leagueteamcomp.model.enums; -public enum SummonerRole { +public enum SummonerRoleEnum { TOP, JGL, MID, diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/enums/TeamSideEnum.java b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/TeamSideEnum.java new file mode 100644 index 0000000..04ba46d --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/enums/TeamSideEnum.java @@ -0,0 +1,7 @@ +package com.st4s1k.leagueteamcomp.model.enums; + +public enum TeamSideEnum { + UNDEFINED, + ALLY_TEAM, + ENEMY_TEAM +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ChampionHolder.java b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ChampionHolder.java new file mode 100644 index 0000000..4a08119 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ChampionHolder.java @@ -0,0 +1,20 @@ +package com.st4s1k.leagueteamcomp.model.interfaces; + +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; + +import java.util.Optional; + +public interface ChampionHolder { + + Optional getChampion(); + + void setChampion(ChampionDTO champion); + + default boolean isChampionSelected() { + return getChampion().isPresent(); + } + + default boolean isChampionNotSelected() { + return getChampion().isEmpty(); + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/Clearable.java b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/Clearable.java new file mode 100644 index 0000000..971937f --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/Clearable.java @@ -0,0 +1,6 @@ +package com.st4s1k.leagueteamcomp.model.interfaces; + +public interface Clearable { + default void clear() { + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ImageProvider.java b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ImageProvider.java new file mode 100644 index 0000000..dcd6342 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ImageProvider.java @@ -0,0 +1,8 @@ +package com.st4s1k.leagueteamcomp.model.interfaces; + +import javafx.scene.image.Image; + +public interface ImageProvider { + + Image getImage(); +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ObservablesProvider.java b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ObservablesProvider.java new file mode 100644 index 0000000..ae1bdc7 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/ObservablesProvider.java @@ -0,0 +1,10 @@ +package com.st4s1k.leagueteamcomp.model.interfaces; + +import javafx.beans.Observable; + +public interface ObservablesProvider { + + default Observable[] getObservables() { + return new Observable[0]; + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/SlotItem.java b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/SlotItem.java new file mode 100644 index 0000000..d788a0c --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/interfaces/SlotItem.java @@ -0,0 +1,4 @@ +package com.st4s1k.leagueteamcomp.model.interfaces; + +public interface SlotItem extends ObservablesProvider, ImageProvider, Clearable { +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/javafx/LTCChampionListCell.java b/src/main/java/com/st4s1k/leagueteamcomp/model/javafx/LTCChampionListCell.java new file mode 100644 index 0000000..d6f844d --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/javafx/LTCChampionListCell.java @@ -0,0 +1,31 @@ +package com.st4s1k.leagueteamcomp.model.javafx; + +import com.st4s1k.leagueteamcomp.model.champion.select.SlotDTO; +import com.st4s1k.leagueteamcomp.model.interfaces.SlotItem; +import javafx.scene.control.ListCell; +import javafx.scene.image.ImageView; + +public class LTCChampionListCell extends ListCell> { + + private final ImageView imageView = new ImageView(); + private final double imageSize; + + public LTCChampionListCell(double imageSize) { + this.imageSize = imageSize; + } + + @Override + public void updateItem(SlotDTO item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setGraphic(null); + } else { + imageView.setSmooth(true); + imageView.setCache(true); + imageView.setFitWidth(imageSize); + imageView.setFitHeight(imageSize); + imageView.setImage(item.getImage()); + setGraphic(imageView); + } + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/model/javafx/LTCChampionSuggestionListCell.java b/src/main/java/com/st4s1k/leagueteamcomp/model/javafx/LTCChampionSuggestionListCell.java new file mode 100644 index 0000000..587bafc --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/model/javafx/LTCChampionSuggestionListCell.java @@ -0,0 +1,19 @@ +package com.st4s1k.leagueteamcomp.model.javafx; + +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import com.st4s1k.leagueteamcomp.model.champion.select.SlotDTO; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; + +public class LTCChampionSuggestionListCell extends ListCell>> { + + @Override + public void updateItem(ListView> item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setGraphic(null); + } else { + setGraphic(item); + } + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/repository/ChampionRepository.java b/src/main/java/com/st4s1k/leagueteamcomp/repository/ChampionRepository.java index 2eff4e8..df87ed0 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/repository/ChampionRepository.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/repository/ChampionRepository.java @@ -1,58 +1,67 @@ package com.st4s1k.leagueteamcomp.repository; -import com.st4s1k.leagueteamcomp.model.champion.Champion; -import com.st4s1k.leagueteamcomp.model.champion.Champions; +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import com.st4s1k.leagueteamcomp.model.champion.ChampionsDTO; +import lombok.NoArgsConstructor; +import java.util.Collection; +import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import static java.util.Collections.emptySet; +import static java.util.Collections.emptyMap; +import static lombok.AccessLevel.PRIVATE; +@NoArgsConstructor(access = PRIVATE) public class ChampionRepository { private static ChampionRepository INSTANCE; - private final CompletableFuture champions; + private static ChampionsDTO champions; + + public static void init(ChampionsDTO champions) { + if (ChampionRepository.champions == null) { + ChampionRepository.champions = champions; + } + } public static ChampionRepository getInstance() { + if (INSTANCE == null) { + INSTANCE = new ChampionRepository(); + } return INSTANCE; } - private ChampionRepository(CompletableFuture champions) { - this.champions = champions; + private Map getChampionsMap() { + return Optional.ofNullable(champions) + .map(ChampionsDTO::getChampions) + .orElse(emptyMap()); } - public static void init(CompletableFuture champions) { - if (INSTANCE == null) { - INSTANCE = new ChampionRepository(champions); - } + public Collection getAllChampions() { + return getChampionsMap().values(); } public Set getAllChampionKeys() { - return champions.thenApply(champions -> champions.getChampions().keySet()) - .getNow(emptySet()); + return getChampionsMap().keySet(); } - public Optional findChampionDataById(Integer championId) { - return champions.thenApply(champions -> champions.getChampions().values().stream() - .filter(championData -> championData.getId().equals(championId)) - .findFirst()) - .getNow(Optional.empty()); + public Optional findChampionById(Integer championId) { + return getAllChampions().stream() + .filter(championData -> championData.getId().equals(championId)) + .findFirst(); } - public Optional findChampionDataByKey(String championKey) { - return champions.thenApply(champions -> Optional.ofNullable(champions.getChampions().get(championKey))) - .getNow(Optional.empty()); + public Optional findChampionByKey(String championKey) { + return Optional.ofNullable(getChampionsMap().get(championKey)); } - public Optional findChampionDataByName(String championName) { - return champions.thenApply(champions -> champions.getChampions().values().stream() - .filter(championData -> championData.getName().equals(championName)) - .findFirst()) - .getNow(Optional.empty()); + public Optional findChampionByName(String championName) { + return getAllChampions().stream() + .filter(championData -> championData.getName().equals(championName)) + .findFirst(); } - public boolean existsChampionDataByName(String championName) { - return findChampionDataByName(championName).isPresent(); + public boolean existsChampionByName(String championName) { + return findChampionByName(championName).isPresent(); } } diff --git a/src/main/java/com/st4s1k/leagueteamcomp/service/ChampionSuggestionService.java b/src/main/java/com/st4s1k/leagueteamcomp/service/ChampionSuggestionService.java new file mode 100644 index 0000000..c403577 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/service/ChampionSuggestionService.java @@ -0,0 +1,90 @@ +package com.st4s1k.leagueteamcomp.service; + +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; +import com.st4s1k.leagueteamcomp.model.champion.select.ChampSelectDTO; +import com.st4s1k.leagueteamcomp.model.champion.select.SlotDTO; +import com.st4s1k.leagueteamcomp.model.champion.select.SummonerDTO; +import com.st4s1k.leagueteamcomp.model.interfaces.SlotItem; +import com.st4s1k.leagueteamcomp.model.javafx.LTCChampionListCell; +import com.st4s1k.leagueteamcomp.model.javafx.LTCChampionSuggestionListCell; +import javafx.beans.Observable; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.control.ListView; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +import static lombok.AccessLevel.PRIVATE; + +@Slf4j +@NoArgsConstructor(access = PRIVATE) +public class ChampionSuggestionService { + + private static ChampionSuggestionService INSTANCE; + + private static final LeagueTeamCompService service = LeagueTeamCompService.getInstance(); + + public static ChampionSuggestionService getInstance() { + if (INSTANCE == null) { + INSTANCE = new ChampionSuggestionService(); + } + return INSTANCE; + } + + @SuppressWarnings("SameParameterValue") + public void initializeSuggestionsListView( + ChampSelectDTO champSelect, + ListView>> suggestionsListView, + double imageSize + ) { + List>> championSuggestionsListViews = champSelect.getAllyTeam().getSlots().stream() + .map(SlotDTO::getItem) + .flatMap(Optional::stream) + .map(SummonerDTO::getChampionSuggestions) + .map(championSuggestions -> + initializeSuggestionsSubListView(championSuggestions, imageSize)) + .toList(); + initializeSuggestionsListView(championSuggestionsListViews, suggestionsListView); + } + + public ListView> initializeSuggestionsSubListView( + List> championSuggestions, + double imageSize + ) { + var suggestionsListView = new ListView>(); + suggestionsListView.getStyleClass().addAll("champion-list-view", "suggestions-sub-list-view"); + initializeChampionListView(championSuggestions, suggestionsListView, imageSize); + return suggestionsListView; + } + + public void initializeChampionListView( + List> slots, + ListView> listView, + double imageSize + ) { + ObservableList> itemsList = FXCollections.observableArrayList(SlotItem::getObservables); + itemsList.addAll(slots); + listView.setItems(itemsList); + listView.setCellFactory(param -> new LTCChampionListCell<>(imageSize)); + } + + public void initializeSuggestionsListView( + List>> items, + ListView>> listView + ) { + ObservableList>> itemsList = FXCollections.observableArrayList(param -> + param.getItems().stream() + .map(SlotItem::getObservables) + .map(Arrays::asList) + .flatMap(Collection::stream) + .toArray(Observable[]::new)); + itemsList.addAll(items); + listView.setItems(itemsList); + listView.setCellFactory(param -> new LTCChampionSuggestionListCell()); + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/service/LeagueTeamCompService.java b/src/main/java/com/st4s1k/leagueteamcomp/service/LeagueTeamCompService.java index a4a8a8b..5333ab9 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/service/LeagueTeamCompService.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/service/LeagueTeamCompService.java @@ -1,14 +1,19 @@ package com.st4s1k.leagueteamcomp.service; -import com.st4s1k.leagueteamcomp.model.champion.AttributeRatings; -import com.st4s1k.leagueteamcomp.model.champion.Champion; +import com.st4s1k.leagueteamcomp.model.champion.AttributeRatingsDTO; +import com.st4s1k.leagueteamcomp.model.champion.ChampionDTO; import com.st4s1k.leagueteamcomp.repository.ChampionRepository; +import lombok.NoArgsConstructor; +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.ToDoubleFunction; +import static lombok.AccessLevel.PRIVATE; + +@NoArgsConstructor(access = PRIVATE) public class LeagueTeamCompService { private static LeagueTeamCompService INSTANCE; @@ -24,35 +29,39 @@ public static LeagueTeamCompService getInstance() { return INSTANCE; } + public Collection getAllChampions() { + return championRepository.getAllChampions(); + } + public Set getAllChampionKeys() { return championRepository.getAllChampionKeys(); } - public Optional findChampionDataById(Integer championId) { - return championRepository.findChampionDataById(championId); + public Optional findChampionDataById(Integer championId) { + return championRepository.findChampionById(championId); } - public Optional findChampionDataByKey(String championKey) { - return championRepository.findChampionDataByKey(championKey); + public Optional findChampionDataByKey(String championKey) { + return championRepository.findChampionByKey(championKey); } - public Optional findChampionDataByName(String championName) { - return championRepository.findChampionDataByName(championName); + public Optional findChampionDataByName(String championName) { + return championRepository.findChampionByName(championName); } public boolean existsChampionDataByName(String championName) { - return championRepository.existsChampionDataByName(championName); + return championRepository.existsChampionByName(championName); } - public double getChampionInfoValue( + public double getChampionStatValue( List championList, - ToDoubleFunction getValue, + ToDoubleFunction getValue, double valueScale ) { double teamStatSum = championList.stream() - .map(championRepository::findChampionDataByKey) + .map(championRepository::findChampionByKey) .flatMap(Optional::stream) - .map(Champion::getAttributeRatings) + .map(ChampionDTO::getAttributeRatings) .mapToDouble(getValue) .reduce(0, Double::sum); double teamStat = teamStatSum / championList.size() / valueScale * CHAMPION_STAT_OUTPUT_SCALE; diff --git a/src/main/java/com/st4s1k/leagueteamcomp/service/SummonerRoleListGeneratorService.java b/src/main/java/com/st4s1k/leagueteamcomp/service/SummonerRoleListGeneratorService.java index 55a39ec..b151c52 100644 --- a/src/main/java/com/st4s1k/leagueteamcomp/service/SummonerRoleListGeneratorService.java +++ b/src/main/java/com/st4s1k/leagueteamcomp/service/SummonerRoleListGeneratorService.java @@ -1,6 +1,6 @@ package com.st4s1k.leagueteamcomp.service; -import com.st4s1k.leagueteamcomp.model.SummonerRole; +import com.st4s1k.leagueteamcomp.model.enums.SummonerRoleEnum; import java.util.ArrayList; import java.util.List; @@ -13,38 +13,48 @@ public class SummonerRoleListGeneratorService { - public static List>> getCombinations(Map> playerToRoles) { + private static SummonerRoleListGeneratorService INSTANCE; + + public static SummonerRoleListGeneratorService getInstance() { + if (INSTANCE == null) { + INSTANCE = new SummonerRoleListGeneratorService(); + } + return INSTANCE; + } + + public List> getCombinations(Map> playerToRoles) { if (playerToRoles.isEmpty() || playerToRoles.size() < 5) { return emptyList(); } List> allPlayerPermutations = new ArrayList<>(); List players = new ArrayList<>(playerToRoles.keySet()); generateAllPlayerPermutations(playerToRoles.size(), players, allPlayerPermutations); - return allPlayerPermutations.stream().map(playersInOrder -> List.of( - Map.entry(playersInOrder.get(0), SummonerRole.TOP), - Map.entry(playersInOrder.get(1), SummonerRole.MID), - Map.entry(playersInOrder.get(2), SummonerRole.ADC), - Map.entry(playersInOrder.get(3), SummonerRole.SUP), - Map.entry(playersInOrder.get(4), SummonerRole.JGL) - )).filter(newPlayerToRoleList -> arePlayersOnCorrectRoles(playerToRoles, newPlayerToRoleList)) + return allPlayerPermutations.stream().map(playersInOrder -> Map.ofEntries( + Map.entry(playersInOrder.get(0), SummonerRoleEnum.TOP), + Map.entry(playersInOrder.get(1), SummonerRoleEnum.MID), + Map.entry(playersInOrder.get(2), SummonerRoleEnum.ADC), + Map.entry(playersInOrder.get(3), SummonerRoleEnum.SUP), + Map.entry(playersInOrder.get(4), SummonerRoleEnum.JGL) + )).filter(playersToRoles -> arePlayersOnCorrectRoles(playerToRoles, playersToRoles)) .collect(Collectors.toList()); } - private static boolean arePlayersOnCorrectRoles( - Map> playerToRoles, - List> newPlayerToRoleList + private boolean arePlayersOnCorrectRoles( + Map> playerToRoles, + Map newPlayerToRoleList ) { - return newPlayerToRoleList.stream().allMatch(newPlayerToRole -> isPlayerOnCorrectRole(playerToRoles, newPlayerToRole)); + return newPlayerToRoleList.entrySet().stream() + .allMatch(newPlayerToRole -> isPlayerOnCorrectRole(playerToRoles, newPlayerToRole)); } - private static boolean isPlayerOnCorrectRole( - Map> playerToRoles, - Entry playerToRole + private boolean isPlayerOnCorrectRole( + Map> playerToRoles, + Entry playerToRole ) { return playerToRoles.get(playerToRole.getKey()).contains(playerToRole.getValue()); } - public static void generateAllPlayerPermutations( + public void generateAllPlayerPermutations( int n, List playerPermutation, List> allPlayerPermutations diff --git a/src/main/java/com/st4s1k/leagueteamcomp/utils/ResizeHelper.java b/src/main/java/com/st4s1k/leagueteamcomp/utils/ResizeHelper.java new file mode 100644 index 0000000..66f1d3d --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/utils/ResizeHelper.java @@ -0,0 +1,145 @@ +package com.st4s1k.leagueteamcomp.utils; + +import javafx.collections.ObservableList; +import javafx.event.EventHandler; +import javafx.event.EventType; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.input.MouseEvent; +import javafx.stage.Stage; +import lombok.extern.slf4j.Slf4j; + +import static javafx.scene.Cursor.*; +import static javafx.scene.input.MouseEvent.*; + +/** + * Util class to handle window resizing when a stage style set to StageStyle.UNDECORATED. + * Includes dragging of the stage. + * Original on 6/13/14. + * Updated on 8/15/17. + * Updated on 12/19/19. + * + * @author Alexander.Berg + * @author Evgenii Kanivets + * @author Zachary Perales + */ + +@Slf4j +public class ResizeHelper { + + public static void addResizeListener( + Stage stage, + double border + ) { + ResizeListener resizeListener = new ResizeListener(stage, border); + stage.getScene().addEventHandler(MOUSE_MOVED, resizeListener); + stage.getScene().addEventHandler(MOUSE_PRESSED, resizeListener); + stage.getScene().addEventHandler(MOUSE_DRAGGED, resizeListener); + stage.getScene().addEventHandler(MOUSE_EXITED, resizeListener); + stage.getScene().addEventHandler(MOUSE_EXITED_TARGET, resizeListener); + ObservableList children = stage.getScene().getRoot().getChildrenUnmodifiable(); + for (Node child : children) { + addListenerDeeply(child, resizeListener); + } + } + + private static void addListenerDeeply(Node node, EventHandler listener) { + node.addEventHandler(MOUSE_MOVED, listener); + node.addEventHandler(MOUSE_PRESSED, listener); + node.addEventHandler(MOUSE_DRAGGED, listener); + node.addEventHandler(MOUSE_EXITED, listener); + node.addEventHandler(MOUSE_EXITED_TARGET, listener); + if (node instanceof Parent parent) { + ObservableList children = parent.getChildrenUnmodifiable(); + for (Node child : children) { + addListenerDeeply(child, listener); + } + } + } + + static class ResizeListener implements EventHandler { + private final Stage stage; + private final double border; + private Cursor cursorEvent = DEFAULT; + private double startX = 0; + private double startY = 0; + + public ResizeListener(Stage stage, double border) { + this.stage = stage; + this.border = border; + } + + @Override + public void handle(MouseEvent mouseEvent) { + EventType mouseEventType = mouseEvent.getEventType(); + Scene scene = stage.getScene(); + + double mouseEventX = mouseEvent.getSceneX(), + mouseEventY = mouseEvent.getSceneY(), + sceneWidth = scene.getWidth(), + sceneHeight = scene.getHeight(); + + if (MOUSE_MOVED.equals(mouseEventType)) { + if (mouseEventX < border && mouseEventY < border) { + cursorEvent = NW_RESIZE; + } else if (mouseEventX < border && mouseEventY > sceneHeight - border) { + cursorEvent = SW_RESIZE; + } else if (mouseEventX > sceneWidth - border && mouseEventY < border) { + cursorEvent = NE_RESIZE; + } else if (mouseEventX > sceneWidth - border && mouseEventY > sceneHeight - border) { + cursorEvent = SE_RESIZE; + } else if (mouseEventX < border) { + cursorEvent = W_RESIZE; + } else if (mouseEventX > sceneWidth - border) { + cursorEvent = E_RESIZE; + } else if (mouseEventY < border) { + cursorEvent = N_RESIZE; + } else if (mouseEventY > sceneHeight - border) { + cursorEvent = S_RESIZE; + } else { + cursorEvent = DEFAULT; + } + scene.setCursor(cursorEvent); + } else if (MOUSE_EXITED.equals(mouseEventType) || MOUSE_EXITED_TARGET.equals(mouseEventType)) { + scene.setCursor(DEFAULT); + } else if (MOUSE_PRESSED.equals(mouseEventType)) { + startX = stage.getWidth() - mouseEventX; + startY = stage.getHeight() - mouseEventY; + } else if (MOUSE_DRAGGED.equals(mouseEventType)) { + if (!DEFAULT.equals(cursorEvent)) { + if (!W_RESIZE.equals(cursorEvent) && !E_RESIZE.equals(cursorEvent)) { + double minHeight = Math.max(stage.getMinHeight(), border * 2); + if (NW_RESIZE.equals(cursorEvent) || N_RESIZE.equals(cursorEvent) || NE_RESIZE.equals(cursorEvent)) { + if (stage.getHeight() > minHeight || mouseEventY < 0) { + stage.setHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight()); + double screenY = mouseEvent.getScreenY(); + stage.setY(screenY); + } + } else { + if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0) { + stage.setHeight(mouseEventY + startY); + } + } + } + + if (!N_RESIZE.equals(cursorEvent) && !S_RESIZE.equals(cursorEvent)) { + double minWidth = Math.max(stage.getMinWidth(), border * 2); + if (NW_RESIZE.equals(cursorEvent) || W_RESIZE.equals(cursorEvent) || SW_RESIZE.equals(cursorEvent)) { + if (stage.getWidth() > minWidth || mouseEventX < 0) { + stage.setWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth()); + double screenX = mouseEvent.getScreenX(); + stage.setX(screenX); + } + } else { + if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0) { + stage.setWidth(mouseEventX + startX); + } + } + } + } + } + } + } +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/utils/Resources.java b/src/main/java/com/st4s1k/leagueteamcomp/utils/Resources.java new file mode 100644 index 0000000..7b4631d --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/utils/Resources.java @@ -0,0 +1,54 @@ +package com.st4s1k.leagueteamcomp.utils; + +import com.st4s1k.leagueteamcomp.LeagueTeamCompApplication; +import javafx.scene.image.Image; +import lombok.experimental.UtilityClass; + +import java.util.ResourceBundle; + +import static java.lang.Boolean.parseBoolean; +import static java.lang.Integer.parseInt; + +@UtilityClass +@SuppressWarnings("ConstantConditions") +public class Resources { + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Resource path constants * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + private final String APP_BUNDLE_PATH = "com.st4s1k.leagueteamcomp.ltc"; + private final String FXML_BUNDLE_PATH = "com.st4s1k.leagueteamcomp.ltc-view"; + public final ResourceBundle LTC_PROPERTIES = ResourceBundle.getBundle(APP_BUNDLE_PATH); + public final ResourceBundle LTC_VIEW_PROPERTIES = ResourceBundle.getBundle(FXML_BUNDLE_PATH); + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Window constants * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + public final String WINDOW_TITLE = LTC_PROPERTIES.getString("window-title"); + public final int WINDOW_WIDTH = parseInt(LTC_PROPERTIES.getString("window-width")); + public final int WINDOW_HEIGHT = parseInt(LTC_PROPERTIES.getString("window-height")); + public final boolean WINDOW_IS_RESIZABLE = parseBoolean(LTC_PROPERTIES.getString("window-is-resizable")); + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Files * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + public final String ICON_FILE_PATH = LTC_PROPERTIES.getString("icon-file-path"); + public final String FXML_FILE_PATH = LTC_PROPERTIES.getString("fxml-file-path"); + public final String EMPTY_SLOT_IMAGE_PATH = LTC_PROPERTIES.getString("empty-slot-image"); + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Urls * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + public final String CHAMPIONS_URL = LTC_PROPERTIES.getString("champions-url"); + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Application constants * + * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + + public final Image EMPTY_SLOT_IMAGE = new Image(LeagueTeamCompApplication.class.getResourceAsStream(EMPTY_SLOT_IMAGE_PATH)); + +} diff --git a/src/main/java/com/st4s1k/leagueteamcomp/utils/Utils.java b/src/main/java/com/st4s1k/leagueteamcomp/utils/Utils.java new file mode 100644 index 0000000..e6d8846 --- /dev/null +++ b/src/main/java/com/st4s1k/leagueteamcomp/utils/Utils.java @@ -0,0 +1,30 @@ +package com.st4s1k.leagueteamcomp.utils; + +import javafx.scene.Node; +import lombok.experimental.UtilityClass; +import lombok.extern.slf4j.Slf4j; + +import java.util.Collection; + +@Slf4j +@UtilityClass +public class Utils { + + public boolean same(Collection collection1, Collection collection2) { + return collection1.size() == collection2.size() && collection1.containsAll(collection2); + } + + public boolean notSame(Collection collection1, Collection collection2) { + return !same(collection1, collection2); + } + + public void disableInteraction(Node node) { + node.setMouseTransparent(true); + node.setFocusTraversable(false); + } + + public void enableInteraction(Node node) { + node.setMouseTransparent(false); + node.setFocusTraversable(true); + } +} diff --git a/src/main/resources/META-INF/native-image/jni-config.json b/src/main/resources/META-INF/native-image/jni-config.json index bb995df..cbb8812 100644 --- a/src/main/resources/META-INF/native-image/jni-config.json +++ b/src/main/resources/META-INF/native-image/jni-config.json @@ -36,7 +36,10 @@ }, { "name":"com.sun.glass.ui.Screen", - "methods":[{"name":"","parameterTypes":["long","int","int","int","int","int","int","int","int","int","int","int","int","int","int","int","float","float","float","float"] }] + "methods":[ + {"name":"","parameterTypes":["long","int","int","int","int","int","int","int","int","int","int","int","int","int","int","int","float","float","float","float"] }, + {"name":"notifySettingsChanged","parameterTypes":[] } + ] }, { "name":"com.sun.glass.ui.Size", @@ -303,6 +306,10 @@ ], "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.sun.javafx.geom.Path2D", + "methods":[{"name":"","parameterTypes":["int","byte[]","int","float[]","int"] }] +}, { "name":"com.sun.management.internal.DiagnosticCommandArgumentInfo", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","boolean","boolean","boolean","int"] }] @@ -335,10 +342,28 @@ "name":"java.lang.Runnable", "methods":[{"name":"run","parameterTypes":[] }] }, +{ + "name":"java.lang.String", + "methods":[{"name":"toLowerCase","parameterTypes":["java.util.Locale"] }] +}, +{ + "name":"java.util.ArrayList", + "methods":[ + {"name":"","parameterTypes":["int"] }, + {"name":"add","parameterTypes":["java.lang.Object"] } + ] +}, { "name":"java.util.Arrays", "methods":[{"name":"asList","parameterTypes":["java.lang.Object[]"] }] }, +{ + "name":"java.util.HashMap", + "methods":[ + {"name":"containsKey","parameterTypes":["java.lang.Object"] }, + {"name":"put","parameterTypes":["java.lang.Object","java.lang.Object"] } + ] +}, { "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader" }, diff --git a/src/main/resources/META-INF/native-image/reflect-config.json b/src/main/resources/META-INF/native-image/reflect-config.json index 015e9ca..e23d102 100644 --- a/src/main/resources/META-INF/native-image/reflect-config.json +++ b/src/main/resources/META-INF/native-image/reflect-config.json @@ -47,6 +47,24 @@ { "name":"[Ljavax.management.openmbean.CompositeData;" }, +{ + "name":"[Lorg.apache.logging.log4j.core.Appender;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.appender.rolling.action.Action;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.config.AppenderRef;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.config.LoggerConfig;" +}, +{ + "name":"[Lorg.apache.logging.log4j.core.config.Property;" +}, { "name":"[Lsun.security.pkcs.SignerInfo;" }, @@ -63,6 +81,15 @@ {"name":"main","parameterTypes":["java.lang.String[]"] } ] }, +{ + "name":"com.st4s1k.leagueteamcomp.controller.LTCExceptionController", + "allDeclaredFields":true, + "queryAllDeclaredMethods":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"close","parameterTypes":[] } + ] +}, { "name":"com.st4s1k.leagueteamcomp.controller.LeagueTeamCompController", "allDeclaredFields":true, @@ -73,26 +100,51 @@ "allDeclaredFields":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.st4s1k.leagueteamcomp.model.champion.AttributeRatingsDTO", + "allDeclaredFields":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.st4s1k.leagueteamcomp.model.champion.Champion", "allDeclaredFields":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.st4s1k.leagueteamcomp.model.champion.ChampionDTO", + "allDeclaredFields":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.st4s1k.leagueteamcomp.model.champion.Champions", "allDeclaredFields":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.st4s1k.leagueteamcomp.model.champion.ChampionsDTO", + "allDeclaredFields":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.st4s1k.leagueteamcomp.model.champion.Stat", "allDeclaredFields":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.st4s1k.leagueteamcomp.model.champion.StatDTO", + "allDeclaredFields":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.st4s1k.leagueteamcomp.model.champion.Stats", "allDeclaredFields":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"com.st4s1k.leagueteamcomp.model.champion.StatsDTO", + "allDeclaredFields":true, + "methods":[{"name":"","parameterTypes":[] }] +}, { "name":"com.st4s1k.leagueteamcomp.model.champion.enums.AdaptiveType", "allDeclaredFields":true @@ -109,6 +161,22 @@ "name":"com.st4s1k.leagueteamcomp.model.champion.enums.Role", "allDeclaredFields":true }, +{ + "name":"com.st4s1k.leagueteamcomp.model.enums.AdaptiveTypeEnum", + "allDeclaredFields":true +}, +{ + "name":"com.st4s1k.leagueteamcomp.model.enums.AttackTypeEnum", + "allDeclaredFields":true +}, +{ + "name":"com.st4s1k.leagueteamcomp.model.enums.ResourceEnum", + "allDeclaredFields":true +}, +{ + "name":"com.st4s1k.leagueteamcomp.model.enums.RoleEnum", + "allDeclaredFields":true +}, { "name":"com.stirante.lolclient.libs.org.apache.commons.logging.LogFactory" }, @@ -212,6 +280,9 @@ "name":"com.sun.javafx.scene.control.skin.Utils", "methods":[{"name":"getResource","parameterTypes":["java.lang.String"] }] }, +{ + "name":"com.sun.javafx.tk.quantum.OverlayWarning" +}, { "name":"com.sun.javafx.tk.quantum.QuantumToolkit", "methods":[{"name":"","parameterTypes":[] }] @@ -279,10 +350,22 @@ "name":"com.sun.prism.shader.FillPgram_Color_Loader", "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] }, +{ + "name":"com.sun.prism.shader.FillPgram_LinearGradient_PAD_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, { "name":"com.sun.prism.shader.FillRoundRect_Color_Loader", "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] }, +{ + "name":"com.sun.prism.shader.FillRoundRect_LinearGradient_PAD_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, +{ + "name":"com.sun.prism.shader.Solid_Color_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, { "name":"com.sun.prism.shader.Solid_TextureFirstPassLCD_Loader", "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] @@ -299,6 +382,10 @@ "name":"com.sun.prism.shader.Texture_Color_Loader", "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] }, +{ + "name":"com.sun.prism.shader.Texture_LinearGradient_PAD_Loader", + "methods":[{"name":"loadShader","parameterTypes":["com.sun.prism.ps.ShaderFactory","java.io.InputStream"] }] +}, { "name":"com.sun.scenario.effect.impl.hw.d3d.D3DShaderSource", "methods":[{"name":"","parameterTypes":[] }] @@ -932,6 +1019,10 @@ "name":"java.lang.Module", "queriedMethods":[{"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }] }, +{ + "name":"java.lang.Object", + "allDeclaredFields":true +}, { "name":"java.lang.Short", "fields":[{"name":"TYPE"}] @@ -1082,9 +1173,13 @@ "methods":[ {"name":"getId","parameterTypes":[] }, {"name":"getStyleClass","parameterTypes":[] }, + {"name":"setEffect","parameterTypes":["javafx.scene.effect.Effect"] }, {"name":"setId","parameterTypes":["java.lang.String"] }, {"name":"setLayoutX","parameterTypes":["double"] }, - {"name":"setLayoutY","parameterTypes":["double"] } + {"name":"setLayoutY","parameterTypes":["double"] }, + {"name":"setOpacity","parameterTypes":["double"] }, + {"name":"setStyle","parameterTypes":["java.lang.String"] }, + {"name":"setVisible","parameterTypes":["boolean"] } ] }, { @@ -1106,7 +1201,8 @@ }, { "name":"javafx.scene.control.ButtonBase", - "queryAllDeclaredMethods":true + "queryAllDeclaredMethods":true, + "methods":[{"name":"setOnAction","parameterTypes":["javafx.event.EventHandler"] }] }, { "name":"javafx.scene.control.CheckBox", @@ -1114,6 +1210,10 @@ "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"javafx.scene.control.ContentDisplay", + "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] +}, { "name":"javafx.scene.control.Control", "queryAllDeclaredMethods":true @@ -1129,11 +1229,13 @@ "queryAllDeclaredMethods":true, "methods":[ {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, + {"name":"setContentDisplay","parameterTypes":["javafx.scene.control.ContentDisplay"] }, {"name":"setFont","parameterTypes":["javafx.scene.text.Font"] }, {"name":"setGraphic","parameterTypes":["javafx.scene.Node"] }, {"name":"setMnemonicParsing","parameterTypes":["boolean"] }, {"name":"setText","parameterTypes":["java.lang.String"] }, - {"name":"setTextAlignment","parameterTypes":["javafx.scene.text.TextAlignment"] } + {"name":"setTextAlignment","parameterTypes":["javafx.scene.text.TextAlignment"] }, + {"name":"setWrapText","parameterTypes":["boolean"] } ] }, { @@ -1145,6 +1247,15 @@ {"name":"setOrientation","parameterTypes":["javafx.geometry.Orientation"] } ] }, +{ + "name":"javafx.scene.control.ScrollPane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setContent","parameterTypes":["javafx.scene.Node"] } + ] +}, { "name":"javafx.scene.control.Tab", "queryAllDeclaredMethods":true, @@ -1179,7 +1290,10 @@ "name":"javafx.scene.control.TextField", "queryAllDeclaredMethods":true, "queryAllPublicConstructors":true, - "methods":[{"name":"","parameterTypes":[] }] + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] } + ] }, { "name":"javafx.scene.control.TextInputControl", @@ -1197,7 +1311,23 @@ ] }, { - "name":"javafx.scene.effect.Effect" + "name":"javafx.scene.control.ToggleButton", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"javafx.scene.effect.DropShadow", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setColor","parameterTypes":["javafx.scene.paint.Color"] } + ] +}, +{ + "name":"javafx.scene.effect.Effect", + "queryAllDeclaredMethods":true }, { "name":"javafx.scene.image.Image" @@ -1223,6 +1353,18 @@ {"name":"getTopAnchor","parameterTypes":["javafx.scene.Node"] } ] }, +{ + "name":"javafx.scene.layout.BorderPane", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setAlignment","parameterTypes":["javafx.scene.Node","javafx.geometry.Pos"] }, + {"name":"setBottom","parameterTypes":["javafx.scene.Node"] }, + {"name":"setCenter","parameterTypes":["javafx.scene.Node"] }, + {"name":"setTop","parameterTypes":["javafx.scene.Node"] } + ] +}, { "name":"javafx.scene.layout.ColumnConstraints", "queryAllDeclaredMethods":true, @@ -1248,6 +1390,7 @@ {"name":"","parameterTypes":[] }, {"name":"getColumnConstraints","parameterTypes":[] }, {"name":"getRowConstraints","parameterTypes":[] }, + {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] }, {"name":"setColumnIndex","parameterTypes":["javafx.scene.Node","java.lang.Integer"] }, {"name":"setColumnSpan","parameterTypes":["javafx.scene.Node","java.lang.Integer"] }, {"name":"setHalignment","parameterTypes":["javafx.scene.Node","javafx.geometry.HPos"] }, @@ -1261,10 +1404,23 @@ {"name":"getRowIndex","parameterTypes":["javafx.scene.Node"] } ] }, +{ + "name":"javafx.scene.layout.HBox", + "queryAllDeclaredMethods":true, + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"setAlignment","parameterTypes":["javafx.geometry.Pos"] } + ] +}, { "name":"javafx.scene.layout.Pane", "queryAllDeclaredMethods":true, - "methods":[{"name":"getChildren","parameterTypes":[] }] + "queryAllPublicConstructors":true, + "methods":[ + {"name":"","parameterTypes":[] }, + {"name":"getChildren","parameterTypes":[] } + ] }, { "name":"javafx.scene.layout.Priority", @@ -1310,6 +1466,12 @@ ], "queriedMethods":[{"name":"getVgrow","parameterTypes":["javafx.scene.Node"] }] }, +{ + "name":"javafx.scene.paint.Color", + "queryAllPublicMethods":true, + "queryAllPublicConstructors":true, + "methods":[{"name":"","parameterTypes":["double","double","double","double"] }] +}, { "name":"javafx.scene.shape.LineTo" }, @@ -1415,6 +1577,14 @@ "name":"jdk.management.jfr.SettingDescriptorInfo", "queryAllPublicMethods":true }, +{ + "name":"org.apache.logging.log4j.core.appender.AbstractAppender$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender$Builder", + "allDeclaredFields":true +}, { "name":"org.apache.logging.log4j.core.appender.AppenderSet" }, @@ -1422,7 +1592,13 @@ "name":"org.apache.logging.log4j.core.appender.AsyncAppender" }, { - "name":"org.apache.logging.log4j.core.appender.ConsoleAppender" + "name":"org.apache.logging.log4j.core.appender.ConsoleAppender", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.appender.ConsoleAppender$Builder", + "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.CountingNoOpAppender" @@ -1452,7 +1628,13 @@ "name":"org.apache.logging.log4j.core.appender.RandomAccessFileAppender" }, { - "name":"org.apache.logging.log4j.core.appender.RollingFileAppender" + "name":"org.apache.logging.log4j.core.appender.RollingFileAppender", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.appender.RollingFileAppender$Builder", + "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender" @@ -1515,13 +1697,21 @@ "name":"org.apache.logging.log4j.core.appender.rewrite.RewriteAppender" }, { - "name":"org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy" + "name":"org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createPolicy","parameterTypes":["org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy[]"] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.CronTriggeringPolicy" }, { - "name":"org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy" + "name":"org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy$Builder", + "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.rolling.DirectWriteRolloverStrategy" @@ -1533,10 +1723,18 @@ "name":"org.apache.logging.log4j.core.appender.rolling.OnStartupTriggeringPolicy" }, { - "name":"org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy" + "name":"org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createPolicy","parameterTypes":["java.lang.String"] }] +}, +{ + "name":"org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy" + "name":"org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy$Builder", + "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.DeleteAction" @@ -1602,10 +1800,14 @@ "name":"org.apache.logging.log4j.core.async.LinkedTransferQueueFactory" }, { - "name":"org.apache.logging.log4j.core.config.AppenderRef" + "name":"org.apache.logging.log4j.core.config.AppenderRef", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createAppenderRef","parameterTypes":["java.lang.String","org.apache.logging.log4j.Level","org.apache.logging.log4j.core.Filter"] }] }, { - "name":"org.apache.logging.log4j.core.config.AppendersPlugin" + "name":"org.apache.logging.log4j.core.config.AppendersPlugin", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createAppenders","parameterTypes":["org.apache.logging.log4j.core.Appender[]"] }] }, { "name":"org.apache.logging.log4j.core.config.CustomLevelConfig" @@ -1620,13 +1822,27 @@ "name":"org.apache.logging.log4j.core.config.HttpWatcher" }, { - "name":"org.apache.logging.log4j.core.config.LoggerConfig" + "name":"org.apache.logging.log4j.core.config.LoggerConfig", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.LoggerConfig$RootLogger" + "name":"org.apache.logging.log4j.core.config.LoggerConfig$Builder", + "allDeclaredFields":true }, { - "name":"org.apache.logging.log4j.core.config.LoggersPlugin" + "name":"org.apache.logging.log4j.core.config.LoggerConfig$RootLogger", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newRootBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.LoggerConfig$RootLogger$Builder", + "allDeclaredFields":true +}, +{ + "name":"org.apache.logging.log4j.core.config.LoggersPlugin", + "queryAllDeclaredMethods":true, + "methods":[{"name":"createLoggers","parameterTypes":["org.apache.logging.log4j.core.config.LoggerConfig[]"] }] }, { "name":"org.apache.logging.log4j.core.config.PropertiesPlugin" @@ -1657,82 +1873,132 @@ "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigDecimalConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigDecimalConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigIntegerConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigIntegerConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BooleanConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BooleanConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteArrayConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteArrayConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharArrayConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharArrayConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharacterConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharacterConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharsetConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharsetConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ClassConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ClassConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CronExpressionConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DoubleConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CronExpressionConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DurationConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DoubleConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FileConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DurationConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FloatConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FileConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$InetAddressConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FloatConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$IntegerConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$InetAddressConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$IntegerConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LongConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PathConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PatternConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$SecurityProviderConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ShortConverter", + "methods":[{"name":"","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$StringConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LongConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UriConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PathConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UrlConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PatternConverter" + "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UuidConverter", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$SecurityProviderConverter" + "name":"org.apache.logging.log4j.core.config.plugins.validation.validators.RequiredValidator", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ShortConverter" + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$StringConverter" + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UriConverter" + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginConfigurationVisitor", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UrlConverter" + "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor", + "methods":[{"name":"","parameterTypes":[] }] }, { - "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UuidConverter" + "name":"org.apache.logging.log4j.core.config.properties.PropertiesConfiguration", + "methods":[{"name":"","parameterTypes":["org.apache.logging.log4j.core.LoggerContext","org.apache.logging.log4j.core.config.ConfigurationSource","org.apache.logging.log4j.core.config.builder.api.Component"] }] }, { "name":"org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory", @@ -1746,6 +2012,10 @@ "name":"org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory", "methods":[{"name":"","parameterTypes":[] }] }, +{ + "name":"org.apache.logging.log4j.core.filter.AbstractFilterable$Builder", + "allDeclaredFields":true +}, { "name":"org.apache.logging.log4j.core.filter.BurstFilter" }, @@ -1814,6 +2084,14 @@ "name":"org.apache.logging.log4j.core.jmx.ContextSelectorAdminMBean", "queryAllPublicMethods":true }, +{ + "name":"org.apache.logging.log4j.core.jmx.LoggerConfigAdmin", + "queryAllPublicConstructors":true +}, +{ + "name":"org.apache.logging.log4j.core.jmx.LoggerConfigAdminMBean", + "queryAllPublicMethods":true +}, { "name":"org.apache.logging.log4j.core.jmx.LoggerContextAdmin", "queryAllPublicConstructors":true @@ -1858,7 +2136,13 @@ "name":"org.apache.logging.log4j.core.layout.MessageLayout" }, { - "name":"org.apache.logging.log4j.core.layout.PatternLayout" + "name":"org.apache.logging.log4j.core.layout.PatternLayout", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newBuilder","parameterTypes":[] }] +}, +{ + "name":"org.apache.logging.log4j.core.layout.PatternLayout$Builder", + "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.layout.PatternMatch" @@ -1990,7 +2274,9 @@ "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Yellow" }, { - "name":"org.apache.logging.log4j.core.pattern.ClassNamePatternConverter" + "name":"org.apache.logging.log4j.core.pattern.ClassNamePatternConverter", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.DatePatternConverter", @@ -2013,7 +2299,9 @@ "name":"org.apache.logging.log4j.core.pattern.ExtendedThrowablePatternConverter" }, { - "name":"org.apache.logging.log4j.core.pattern.FileDatePatternConverter" + "name":"org.apache.logging.log4j.core.pattern.FileDatePatternConverter", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.FileLocationPatternConverter" @@ -2025,7 +2313,9 @@ "name":"org.apache.logging.log4j.core.pattern.HighlightConverter" }, { - "name":"org.apache.logging.log4j.core.pattern.IntegerPatternConverter" + "name":"org.apache.logging.log4j.core.pattern.IntegerPatternConverter", + "queryAllDeclaredMethods":true, + "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.LevelPatternConverter", diff --git a/src/main/resources/META-INF/native-image/resource-config.json b/src/main/resources/META-INF/native-image/resource-config.json index 43095f9..e1d10d7 100644 --- a/src/main/resources/META-INF/native-image/resource-config.json +++ b/src/main/resources/META-INF/native-image/resource-config.json @@ -13,12 +13,18 @@ { "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.util.PropertySource\\E" }, + { + "pattern":"\\Qcom/st4s1k/leagueteamcomp/exception.fxml\\E" + }, { "pattern":"\\Qcom/st4s1k/leagueteamcomp/helmet_bro.png\\E" }, { "pattern":"\\Qcom/st4s1k/leagueteamcomp/icon.png\\E" }, + { + "pattern":"\\Qcom/st4s1k/leagueteamcomp/ltc-exception.fxml\\E" + }, { "pattern":"\\Qcom/st4s1k/leagueteamcomp/ltc-view.fxml\\E" }, @@ -28,6 +34,9 @@ { "pattern":"\\Qcom/stirante/lolclient/libs/org/slf4j/impl/StaticLoggerBinder.class\\E" }, + { + "pattern":"\\Qlog4j2.properties\\E" + }, { "pattern":"\\Qmozilla/public-suffix-list.txt\\E" }, diff --git a/src/main/resources/com/st4s1k/leagueteamcomp/ltc-exception.fxml b/src/main/resources/com/st4s1k/leagueteamcomp/ltc-exception.fxml new file mode 100644 index 0000000..556a37e --- /dev/null +++ b/src/main/resources/com/st4s1k/leagueteamcomp/ltc-exception.fxml @@ -0,0 +1,57 @@ + + + + + + + + + + +
+ + + +