diff --git a/pom.xml b/pom.xml index ff4d6d8..21a46da 100644 --- a/pom.xml +++ b/pom.xml @@ -127,9 +127,9 @@ - - - + + + @@ -169,6 +169,10 @@ 3.8.1 test + + com.google.code.gson + gson + diff --git a/src/main/java/com/example/application/data/chesscom/ChessComArchive.java b/src/main/java/com/example/application/data/chesscom/ChessComArchive.java new file mode 100644 index 0000000..c2ad97e --- /dev/null +++ b/src/main/java/com/example/application/data/chesscom/ChessComArchive.java @@ -0,0 +1,15 @@ +package com.example.application.data.chesscom; + +import java.util.List; + +public class ChessComArchive { + private List games; + + public List getGames() { + return games; + } + + public void setGames(List games) { + this.games = games; + } +} diff --git a/src/main/java/com/example/application/data/chesscom/ChessComArchiveList.java b/src/main/java/com/example/application/data/chesscom/ChessComArchiveList.java new file mode 100644 index 0000000..fb10c17 --- /dev/null +++ b/src/main/java/com/example/application/data/chesscom/ChessComArchiveList.java @@ -0,0 +1,15 @@ +package com.example.application.data.chesscom; + +import java.util.List; + +public class ChessComArchiveList { + private List archives; + + public List getArchives() { + return archives; + } + + public void setArchives(List archives) { + this.archives = archives; + } +} diff --git a/src/main/java/com/example/application/data/chesscom/ChessComGame.java b/src/main/java/com/example/application/data/chesscom/ChessComGame.java new file mode 100644 index 0000000..f7f4262 --- /dev/null +++ b/src/main/java/com/example/application/data/chesscom/ChessComGame.java @@ -0,0 +1,134 @@ +package com.example.application.data.chesscom; + +public class ChessComGame { + private String url; + private String pgn; + private String timeControl; + private long endTime; + private boolean rated; + private String fen; + private String timeClass; + private String rules; + private ChessComGamePlayer white; + private ChessComGamePlayer black; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getPgn() { + return pgn; + } + + public void setPgn(String pgn) { + this.pgn = pgn; + } + + public String getTimeControl() { + return timeControl; + } + + public void setTimeControl(String timeControl) { + this.timeControl = timeControl; + } + + public long getEndTime() { + return endTime; + } + + public void setEndTime(long endTime) { + this.endTime = endTime; + } + + public boolean isRated() { + return rated; + } + + public void setRated(boolean rated) { + this.rated = rated; + } + + public String getFen() { + return fen; + } + + public void setFen(String fen) { + this.fen = fen; + } + + public String getTimeClass() { + return timeClass; + } + + public void setTimeClass(String timeClass) { + this.timeClass = timeClass; + } + + public String getRules() { + return rules; + } + + public void setRules(String rules) { + this.rules = rules; + } + + public ChessComGamePlayer getWhite() { + return white; + } + + public void setWhite(ChessComGamePlayer white) { + this.white = white; + } + + public ChessComGamePlayer getBlack() { + return black; + } + + public void setBlack(ChessComGamePlayer black) { + this.black = black; + } + + public static class ChessComGamePlayer { + private int rating; + private String result; + private String id; + private String username; + + public int getRating() { + return rating; + } + + public void setRating(int rating) { + this.rating = rating; + } + + public String getResult() { + return result; + } + + public void setResult(String result) { + this.result = result; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + } + +} diff --git a/src/main/java/com/example/application/data/entity/Season.java b/src/main/java/com/example/application/data/entity/Season.java index 59a5200..ecaac56 100644 --- a/src/main/java/com/example/application/data/entity/Season.java +++ b/src/main/java/com/example/application/data/entity/Season.java @@ -69,7 +69,7 @@ public class Season { public String toString() { String s = yearStart.toString(); if (!yearEnd.equals(yearStart)) { - s += "/" + yearEnd.toString(); + s += "-" + yearEnd.toString(); } return s; } diff --git a/src/main/java/com/example/application/data/service/ChessComService.java b/src/main/java/com/example/application/data/service/ChessComService.java new file mode 100644 index 0000000..5268025 --- /dev/null +++ b/src/main/java/com/example/application/data/service/ChessComService.java @@ -0,0 +1,81 @@ +package com.example.application.data.service; + +import com.example.application.data.chesscom.ChessComArchive; +import com.example.application.data.chesscom.ChessComArchiveList; +import com.example.application.data.chesscom.ChessComGame; +import com.example.application.data.entity.Game; +import com.example.application.data.entity.GameInfo; +import com.example.application.data.entity.Player; +import com.example.application.utils.ChessComUtils; +import com.example.application.utils.HttpUtils; +import com.google.gson.Gson; +import org.springframework.lang.NonNull; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + + +@Service +public class ChessComService { + // TODO: make everything nullsafe + + private static final Gson gson = new Gson(); + + public ChessComService() { + } + + @NonNull + public List getLatestGamesBetweenPlayers(@NonNull Player player1, @NonNull Player player2, int minimum_amount_of_games) { + String url = String.format("https://api.chess.com/pub/player/%s/games/archives", ChessComUtils.getUrlPart(player1)); + + List archiveUrls = gson.fromJson(HttpUtils.getJson(url), ChessComArchiveList.class).getArchives(); + archiveUrls.sort(Collections.reverseOrder()); + + if (archiveUrls.size() >= 2) { + archiveUrls = archiveUrls.subList(0, 2); + } + + List list = new ArrayList<>(); + for (String archiveUrl : archiveUrls) { + String unpreparedJson = HttpUtils.getJson(archiveUrl); + if (unpreparedJson == null) { + continue; + } + String preparedJson = ChessComUtils.getPreparedArchiveJson(unpreparedJson); + List games = gson.fromJson(preparedJson, ChessComArchive.class).getGames(); + + list.addAll(games.stream() + .sorted(Comparator.comparingLong(ChessComGame::getEndTime).reversed()) + .filter(chessComGame -> ChessComUtils.isGameBetween(chessComGame, player1, player2)) + .filter(ChessComUtils::hasValidTimeControl) + .map(chessComGame -> getGame(chessComGame, player1, player2)) + .collect(Collectors.toList())); + + if (list.size() >= minimum_amount_of_games) { + break; + } + } + return list; + } // TODO: find exactly two games of each time control + + @NonNull + private Game getGame(@NonNull ChessComGame chessComGame, @NonNull Player player1, @NonNull Player player2) { + Game game = new Game(); + GameInfo gameInfo = new GameInfo(); + + game.setPlayer1IsWhite(chessComGame.getWhite().getUsername().equals(player1.getNickname())); + game.setResult(ChessComUtils.getResult(chessComGame)); + game.setGameInfo(gameInfo); + + gameInfo.setUrl(chessComGame.getUrl()); + gameInfo.setFormat(Integer.parseInt(chessComGame.getTimeControl())/60); // TODO: change gameinfo.format in database to reflect this! + gameInfo.setGame(game); + + return game; // important: match is not set here! + } + +} diff --git a/src/main/java/com/example/application/data/service/MatchdayService.java b/src/main/java/com/example/application/data/service/MatchdayService.java index ce5b70b..9e1ead7 100644 --- a/src/main/java/com/example/application/data/service/MatchdayService.java +++ b/src/main/java/com/example/application/data/service/MatchdayService.java @@ -4,12 +4,12 @@ import com.example.application.data.entity.Matchday; import com.example.application.data.entity.Season; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.vaadin.artur.helpers.CrudService; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -34,6 +34,14 @@ public class MatchdayService extends CrudService { .collect(Collectors.toList()); } + public Optional getFirstMatchdayForSeason(@NonNull Season season) { + List matchdays = getMatchdaysForSeasonSorted(season); + if (matchdays.isEmpty()) { + return Optional.empty(); + } + return Optional.of(matchdays.get(0)); + } + @NonNull public List getMatchdaysWithActivityForSeasonSorted(@NonNull Season season) { return repository.findAll().stream() @@ -43,13 +51,12 @@ public class MatchdayService extends CrudService { .collect(Collectors.toList()); } - @Nullable - public Matchday getLastMatchdayWithActivityForSeason(@NonNull Season season) { + public Optional getLastMatchdayWithActivityForSeason(@NonNull Season season) { List matchdaysWithActivity = getMatchdaysWithActivityForSeasonSorted(season); if (matchdaysWithActivity.isEmpty()) { - return null; + return Optional.empty(); } - return matchdaysWithActivity.get(matchdaysWithActivity.size()-1); + return Optional.of(matchdaysWithActivity.get(matchdaysWithActivity.size()-1)); } public boolean hasActivity(@NonNull Matchday matchday) { diff --git a/src/main/java/com/example/application/data/service/SeasonService.java b/src/main/java/com/example/application/data/service/SeasonService.java index 574ee20..4b70688 100644 --- a/src/main/java/com/example/application/data/service/SeasonService.java +++ b/src/main/java/com/example/application/data/service/SeasonService.java @@ -2,12 +2,12 @@ package com.example.application.data.service; import com.example.application.data.entity.Season; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.lang.Nullable; import org.springframework.stereotype.Service; import org.vaadin.artur.helpers.CrudService; import java.util.Comparator; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; @Service @@ -31,13 +31,18 @@ public class SeasonService extends CrudService { .collect(Collectors.toList()); } - @Nullable - public Season getLatestSeason() { + public Optional getLatestSeason() { List allSeasonsSorted = getAllSeasonsSorted(); if (allSeasonsSorted.isEmpty()) { - return null; + return Optional.empty(); } - return allSeasonsSorted.get(allSeasonsSorted.size() - 1); + return Optional.of(allSeasonsSorted.get(allSeasonsSorted.size() - 1)); + } + + public Optional getSeason(String name) { + return repository.findAll().stream() + .filter(season -> season.toString().equals(name)) + .findFirst(); } } diff --git a/src/main/java/com/example/application/utils/ChessComUtils.java b/src/main/java/com/example/application/utils/ChessComUtils.java new file mode 100644 index 0000000..2e027c4 --- /dev/null +++ b/src/main/java/com/example/application/utils/ChessComUtils.java @@ -0,0 +1,48 @@ +package com.example.application.utils; + +import com.example.application.data.chesscom.ChessComGame; +import com.example.application.data.entity.Player; +import org.springframework.lang.NonNull; + +public class ChessComUtils { + private ChessComUtils() {} + + public static String getPreparedArchiveJson(String unpreparedArchiveJson) { + return unpreparedArchiveJson + .replace("@id", "id") + .replace("end_time", "endTime") + .replace("time_class", "timeClass") + .replace("time_control", "timeControl"); + } + + public static String getUrlPart(Player player) { + return player.getNickname().toLowerCase(); + } + + public static int getResult(ChessComGame chessComGame) { + if (chessComGame.getWhite().getResult().equals("win")) { + return 1; + } + if (chessComGame.getBlack().getResult().equals("win")) { + return -1; + } + return 0; + } + + public static boolean isGameBetween(@NonNull ChessComGame game, @NonNull Player player1, @NonNull Player player2) { + String white = game.getWhite().getUsername(); + String black = game.getBlack().getUsername(); + String name1 = player1.getNickname(); + String name2 = player2.getNickname(); + return (white.equals(name1) || white.equals(name2)) && (black.equals(name1) || black.equals(name2)); + } + + public static boolean hasValidTimeControl(ChessComGame game) { + String timeControl = game.getTimeControl(); + if (timeControl != null) { + return timeControl.equals("600") || timeControl.equals("300") || timeControl.equals("180"); + } + return false; + } + +} diff --git a/src/main/java/com/example/application/utils/HttpUtils.java b/src/main/java/com/example/application/utils/HttpUtils.java new file mode 100644 index 0000000..f66e2a2 --- /dev/null +++ b/src/main/java/com/example/application/utils/HttpUtils.java @@ -0,0 +1,41 @@ +package com.example.application.utils; + +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Scanner; + +public class HttpUtils { + private HttpUtils() { + } + + @Nullable + public static String getJson(@NonNull String url) { + try { + URL url_ = new URL(url); + + HttpURLConnection conn = (HttpURLConnection) url_.openConnection(); + conn.setRequestMethod("GET"); + conn.connect(); + + int responsecode = conn.getResponseCode(); + + if (responsecode != 200) { + throw new RuntimeException("HttpResponseCode: " + responsecode); + } + StringBuilder jsonBuilder = new StringBuilder(); + Scanner scanner = new Scanner(url_.openStream()); + while (scanner.hasNext()) { + jsonBuilder.append(scanner.nextLine()); + } + scanner.close(); + + return jsonBuilder.toString(); + } catch (IOException e) { + return null; + } + } +} diff --git a/src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigationView.java b/src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigation.java similarity index 51% rename from src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigationView.java rename to src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigation.java index 50feb9b..267df6d 100644 --- a/src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigationView.java +++ b/src/main/java/com/example/application/views/abstractnavigation/SeasonAndMatchdayNavigation.java @@ -4,69 +4,62 @@ import com.example.application.data.entity.Matchday; import com.example.application.data.entity.Season; import com.example.application.data.service.MatchdayService; import com.example.application.data.service.SeasonService; -import com.vaadin.flow.component.AbstractField; -import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.HasValue; -import com.vaadin.flow.component.UI; -import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.*; +import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.html.Label; +import com.vaadin.flow.component.icon.Icon; +import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.select.Select; -import com.vaadin.flow.router.BeforeEvent; -import com.vaadin.flow.router.HasUrlParameter; -import com.vaadin.flow.router.WildcardParameter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import java.util.ArrayList; import java.util.List; +import java.util.Optional; -public abstract class SeasonAndMatchdayNavigationView extends Div implements HasUrlParameter { +public class SeasonAndMatchdayNavigation { // TODO: show dropdown menus also for invalid URLs (with content that fits the situation) - protected final SeasonService seasonService; - protected final MatchdayService matchdayService; - - protected String seasonParam; - protected String matchdayParam; + private final SeasonService seasonService; + private final MatchdayService matchdayService; private final Label invalidUrlLabel = new Label(); - protected final VerticalLayout outer = new VerticalLayout(); - protected final HorizontalLayout selectionLayout = new HorizontalLayout(); - - protected final List seasonList = new ArrayList<>(); - protected final Select seasonSelect = new Select<>(); + private final HorizontalLayout selectionLayout = new HorizontalLayout(); - protected final List matchdayList = new ArrayList<>(); - protected final Select matchdaySelect = new Select<>(); - protected Component contentLayout; + private final List seasonList = new ArrayList<>(); + private final Select seasonSelect = new Select<>(); - public SeasonAndMatchdayNavigationView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService) { - this.seasonService = seasonService; - this.matchdayService = matchdayService; + private final List matchdayList = new ArrayList<>(); + private final Select matchdaySelect = new Select<>(); + private final String route; + private final boolean onlyMatchdaysWithActivity; + private Runnable runnableToBeRunAfterSelection; + private final Button prevButton = new Button(new Icon(VaadinIcon.ARROW_LEFT)); + private final Button nextButton = new Button(new Icon(VaadinIcon.ARROW_RIGHT)); - addClassName("results-view"); + private String seasonParam; + private String matchdayParam; - configureOuterLayout(); - configureSelectionLayout(); - configureContentLayout(); + public SeasonAndMatchdayNavigation(String route, + @Autowired SeasonService seasonService, + @Autowired MatchdayService matchdayService) { + this(route, seasonService, matchdayService, false); } - protected abstract String route(); - - protected abstract void configureContentLayout(); - - protected abstract void configureContent(); - - protected abstract boolean showOnlyMatchdaysWithActivity(); + public SeasonAndMatchdayNavigation(String route, + @Autowired SeasonService seasonService, + @Autowired MatchdayService matchdayService, + boolean onlyMatchdaysWithActivity) { + this.route = route; + this.seasonService = seasonService; + this.matchdayService = matchdayService; + this.onlyMatchdaysWithActivity = onlyMatchdaysWithActivity; - private void configureOuterLayout() { - add(outer); - outer.setAlignItems(FlexComponent.Alignment.CENTER); + configureSelectionLayout(); } private void configureSelectionLayout() { @@ -83,19 +76,19 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has } private HasValue.ValueChangeListener, Matchday>> matchdaySelectValueChangeListener() { - return matchdayChangeEvent -> getUI().ifPresent(ui -> { + return matchdayChangeEvent -> { Season season = seasonSelect.getValue(); Matchday matchday = matchdayChangeEvent.getValue(); if (season != null && matchday != null) { String seasonParam = season.toString(); String matchdayParam = matchday.toString(); - navigate(ui, seasonParam, matchdayParam); + navigate(seasonParam, matchdayParam); } - }); + }; } private HasValue.ValueChangeListener, Season>> seasonSelectValueChangeListener() { - return seasonChangeEvent -> getUI().ifPresent(ui -> { + return seasonChangeEvent -> { Season newSeason = seasonChangeEvent.getValue(); if (newSeason != null) { String seasonParam = newSeason.toString(); @@ -109,9 +102,9 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has } matchdayParam = matchdayInNewSeason == null ? "1" : matchdayParam; - navigate(ui, seasonParam, matchdayParam); + navigate(seasonParam, matchdayParam); } - }); + }; } private void fillSeasonSelectWithData() { @@ -122,73 +115,62 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has private void fillMatchdaySelectWithData() { matchdayList.clear(); - List matchdaysToAdd = showOnlyMatchdaysWithActivity() ? + List matchdaysToAdd = onlyMatchdaysWithActivity ? matchdayService.getMatchdaysWithActivityForSeasonSorted(seasonSelect.getValue()) : matchdayService.getMatchdaysForSeasonSorted(seasonSelect.getValue()); matchdayList.addAll(matchdaysToAdd); matchdaySelect.setItems(matchdayList); } - protected boolean isMatchDayParamValid(@NonNull String matchdayParam) { + private boolean isMatchDayParamValid(@NonNull String matchdayParam) { return matchdayList.stream().anyMatch(matchday -> matchdayParam.equals(matchday.toString())); } - protected void navigate(UI ui, String seasonParam, String matchdayParam) { - ui.navigate(String.format("%s/%s/%s/", route(), seasonParam, matchdayParam)); + private void navigate(String seasonParam, String matchdayParam) { + UI.getCurrent().navigate(String.format("%s/%s/%s/", route, seasonParam, matchdayParam)); +// setParameter(seasonParam, matchdayParam); } - @Override - public void setParameter(BeforeEvent beforeEvent, @WildcardParameter String param) { - outer.removeAll(); + public void setParameter(String seasonParam, String matchdayParam) { // TODO: handle corner case where first matchday has no active games yet: still show first matchday - if (param.equals("")) { - Season latestSeason = seasonService.getLatestSeason(); - if (latestSeason != null) { - Matchday latestMatchday = matchdayService.getLastMatchdayWithActivityForSeason(latestSeason); - if (latestMatchday != null) { - String seasonParam = latestSeason.toString(); - String matchdayParam = latestMatchday.toString(); - navigate(UI.getCurrent(), seasonParam, matchdayParam); - setParameter(beforeEvent, String.format("%s/%s/", seasonParam, matchdayParam)); // TODO: check why this line seems to be necessary + if (seasonParam == null || seasonParam.equals("") || matchdayParam == null || matchdayParam.equals("")) { + Optional latestSeason = seasonService.getLatestSeason(); + if (latestSeason.isPresent()) { + Optional latestMatchday = matchdayService.getLastMatchdayWithActivityForSeason(latestSeason.get()); + if (latestMatchday.isPresent()) { + seasonParam = latestSeason.get().toString(); + matchdayParam = latestMatchday.get().toString(); + navigate(seasonParam, matchdayParam); + setParameter(seasonParam, matchdayParam); } } return; } - if (!param.matches("^[0-9]*/[0-9]*/?$")) { - invalidUrlLabel.setText("Invalid URL! Please provide params in the form season/matchday/"); - outer.add(invalidUrlLabel); - return; - } - - String[] params = param.split("/"); - seasonParam = params[0]; - matchdayParam = params[1]; - Season season = getSeasonFromParam(seasonParam); if (season == null) { invalidUrlLabel.setText(String.format("Invalid URL! Season \"%s\" does not exist in the database!", seasonParam)); - outer.add(invalidUrlLabel); return; } seasonSelect.setValue(season); + this.seasonParam = seasonParam; fillMatchdaySelectWithData(); Matchday matchday = getMatchdayFromParam(matchdayParam); if (matchday == null) { - String messageExtra = showOnlyMatchdaysWithActivity() ? " or has no games played yet" : ""; + String messageExtra = onlyMatchdaysWithActivity ? " or has no games played yet" : ""; invalidUrlLabel.setText(String.format("Invalid URL! Matchday \"%s\" in Season \"%s\" does not exist in the database%s!", matchdayParam, seasonParam, messageExtra)); - outer.add(invalidUrlLabel); return; } matchdaySelect.setValue(matchday); + this.matchdayParam = matchdayParam; + configureButtons(); - outer.add(selectionLayout); - outer.add(contentLayout); - - configureContent(); + if ((runnableToBeRunAfterSelection) != null) { + runnableToBeRunAfterSelection.run(); + } } @Nullable @@ -216,4 +198,61 @@ public abstract class SeasonAndMatchdayNavigationView extends Div implements Has } return null; } + + public void setRunnableToBeRunAfterSelection(Runnable runnableToBeRunAfterSelection) { + this.runnableToBeRunAfterSelection = runnableToBeRunAfterSelection; + } + + + private void configureButtons() { + prevButton.setEnabled(isMatchDayParamValid(getPrevMatchdayParam())); + prevButton.addClickListener(getButtonClickListener(getPrevMatchdayParam())); + + nextButton.setEnabled(isMatchDayParamValid(getNextMatchdayParam())); + nextButton.addClickListener(getButtonClickListener(getNextMatchdayParam())); + } + + private ComponentEventListener> getButtonClickListener(String matchdayParam) { + return buttonClickEvent -> navigate(seasonParam, matchdayParam); + } + + private String getPrevMatchdayParam() { + try { + return String.valueOf(Integer.parseInt(matchdayParam) - 1); + } catch (NumberFormatException e) { + return ""; + } + } + + private String getNextMatchdayParam() { + try { + return String.valueOf(Integer.parseInt(matchdayParam) + 1); + } catch (NumberFormatException e) { + return ""; + } + } + + public Optional getSelectedMatchday() { + return matchdaySelect.getOptionalValue(); + } + + public Optional getSelectedSeason() { + return seasonSelect.getOptionalValue(); + } + + public HorizontalLayout getSelectionLayout() { + return selectionLayout; + } + + public Button getPrevButton() { + return prevButton; + } + + public Button getNextButton() { + return nextButton; + } + + public Label getInvalidUrlLabel() { + return invalidUrlLabel; + } } diff --git a/src/main/java/com/example/application/views/main/MainView.java b/src/main/java/com/example/application/views/main/MainView.java index ff93686..4c9f85f 100644 --- a/src/main/java/com/example/application/views/main/MainView.java +++ b/src/main/java/com/example/application/views/main/MainView.java @@ -6,7 +6,6 @@ import com.vaadin.flow.component.Component; import com.vaadin.flow.component.ComponentUtil; import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.DrawerToggle; -import com.vaadin.flow.component.avatar.Avatar; import com.vaadin.flow.component.dependency.CssImport; import com.vaadin.flow.component.dependency.JsModule; import com.vaadin.flow.component.html.H1; @@ -37,8 +36,8 @@ import java.util.Optional; @Theme(value = Lumo.class, variant = Lumo.DARK) public class MainView extends AppLayout { // TODO: Add Localization - // TODO: View for adding match data // TODO: View for each match + // TODO: View for adding match data // TODO: Handle database connection with environment variables // TODO: make it look even more like chess.com (use darker colors etc.) // TODO: add light theme like on chess.com diff --git a/src/main/java/com/example/application/views/masterdetail/MasterDetailView.java b/src/main/java/com/example/application/views/masterdetail/MasterDetailView.java index eae2506..f4f12c9 100644 --- a/src/main/java/com/example/application/views/masterdetail/MasterDetailView.java +++ b/src/main/java/com/example/application/views/masterdetail/MasterDetailView.java @@ -24,8 +24,9 @@ import com.vaadin.flow.data.renderer.TemplateRenderer; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import org.springframework.beans.factory.annotation.Autowired; -import org.vaadin.artur.helpers.CrudServiceDataProvider; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; @CssImport("./views/masterdetail/master-detail-view.css") @@ -73,7 +74,13 @@ public class MasterDetailView extends Div { .withProperty("important", SamplePerson::isImportant); grid.addColumn(importantRenderer).setHeader("Important").setAutoWidth(true); - grid.setDataProvider(new CrudServiceDataProvider<>(samplePersonService)); + List samplePeople = new ArrayList<>(); + samplePeople.add(new SamplePerson()); + samplePeople.add(new SamplePerson()); + samplePeople.add(new SamplePerson()); + + grid.setItems(samplePeople); +// grid.setDataProvider(new CrudServiceDataProvider<>(samplePersonService)); grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); grid.setHeightFull(); diff --git a/src/main/java/com/example/application/views/results/MatchView.java b/src/main/java/com/example/application/views/results/MatchView.java new file mode 100644 index 0000000..38418eb --- /dev/null +++ b/src/main/java/com/example/application/views/results/MatchView.java @@ -0,0 +1,22 @@ +package com.example.application.views.results; + +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; + +public class MatchView extends Div implements HasUrlParameter { + +// private final ChessComService chessComService; +// private final PlayerService playerService; + +// public ResultsViewRight(@Autowired ChessComService chessComService, @Autowired PlayerService playerService) { + public MatchView() { +// this.chessComService = chessComService; +// this.playerService = playerService; + } + + @Override + public void setParameter(BeforeEvent beforeEvent, String[] SeasonParamAndMatchdayParamAndGameParam) { + + } +} diff --git a/src/main/java/com/example/application/views/results/MatchdayView.java b/src/main/java/com/example/application/views/results/MatchdayView.java new file mode 100644 index 0000000..77091a6 --- /dev/null +++ b/src/main/java/com/example/application/views/results/MatchdayView.java @@ -0,0 +1,140 @@ +package com.example.application.views.results; + +import com.example.application.data.bean.CalculatedMatch; +import com.example.application.data.entity.Matchday; +import com.example.application.data.service.MatchService; +import com.example.application.data.service.MatchdayService; +import com.example.application.data.service.SeasonService; +import com.example.application.utils.StringUtils; +import com.example.application.utils.VaadinUtils; +import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigation; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.grid.ColumnTextAlign; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.grid.GridVariant; +import com.vaadin.flow.component.html.Label; +import com.vaadin.flow.component.orderedlayout.FlexComponent; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.function.ValueProvider; +import com.vaadin.flow.router.BeforeEvent; +import com.vaadin.flow.router.HasUrlParameter; +import com.vaadin.flow.router.WildcardParameter; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Optional; + +public class MatchdayView extends VerticalLayout implements HasUrlParameter { + + private final MatchService matchService; + + private final SeasonAndMatchdayNavigation seasonAndMatchdayNavigation; + + private final VerticalLayout matchdayLayout = new VerticalLayout(); + private final Label matchdayHeader = new Label(); + private final Grid grid = new Grid<>(); + + private final Button prevButton; + private final Button nextButton; + + public MatchdayView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired MatchService matchService) { + this.seasonAndMatchdayNavigation = new SeasonAndMatchdayNavigation("results", seasonService, matchdayService); + this.seasonAndMatchdayNavigation.setRunnableToBeRunAfterSelection(this::configureContent); + this.matchService = matchService; + + prevButton = seasonAndMatchdayNavigation.getPrevButton(); + nextButton = seasonAndMatchdayNavigation.getNextButton(); + + configureContentLayout(); + } + + //////////// + // LAYOUT // + //////////// + + + protected void configureContentLayout() { + setWidthFull(); + setHeightFull(); + setAlignItems(FlexComponent.Alignment.CENTER); + add(seasonAndMatchdayNavigation.getSelectionLayout(), matchdayLayout); + + // TODO add background color for content + matchdayHeader.addClassName("matchday_header"); // TODO: add dates + matchdayHeader.setWidth("100%"); + HorizontalLayout matchdayHeaderLayout = new HorizontalLayout(prevButton, matchdayHeader, nextButton); + matchdayHeaderLayout.setWidthFull(); + + matchdayLayout.setPadding(false); + matchdayLayout.add(matchdayHeaderLayout, grid); + matchdayLayout.setAlignItems(FlexComponent.Alignment.CENTER); + matchdayLayout.setWidth(""); + + Label headerPlayer1 = new Label("Player 1"); + headerPlayer1.addClassName("column_header"); + Label headerPlayer2 = new Label("Player 2"); + headerPlayer2.addClassName("column_header"); + + grid.addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer1)) + .setHeader(headerPlayer1) + .setTextAlign(ColumnTextAlign.END) + .setWidth("13em") + .setFlexGrow(1); + + grid.addColumn((ValueProvider) this::getResultString) + .setHeader("vs.") + .setTextAlign(ColumnTextAlign.CENTER) + .setWidth("6em"); + + grid.addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer2)) + .setHeader(headerPlayer2) + .setTextAlign(ColumnTextAlign.START) + .setWidth("13em") + .setFlexGrow(1); + + grid.setWidth("32em"); // TODO: find a way to set this dynamically based on column widths + grid.setHeightByRows(true); + + grid.addThemeVariants(GridVariant.LUMO_NO_BORDER, + GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES); + } + + private String getResultString(CalculatedMatch match) { + return StringUtils.getResultString(match.getScore1(), match.getScore2()); + } + + + ///////////// + // CONTENT // + ///////////// + + protected void configureContent() { + Optional selectedMatchday = seasonAndMatchdayNavigation.getSelectedMatchday(); + selectedMatchday.ifPresent(matchday -> { + matchdayHeader.setText(String.format("Matchday %s", matchday.toString())); + grid.setItems(matchService.getCalculatedMatches(matchday)); + }); + } + + + @Override + public void setParameter(BeforeEvent event, @WildcardParameter String param) { + String[] params = param.split("/"); + String seasonParam; + String matchdayParam; + switch (params.length) { + case 0: + seasonParam = ""; + matchdayParam = ""; + break; + case 1: + seasonParam = params[0]; + matchdayParam = ""; + break; + default: + seasonParam = params[0]; + matchdayParam = params[1]; + } + seasonAndMatchdayNavigation.setParameter(seasonParam, matchdayParam); + } +} diff --git a/src/main/java/com/example/application/views/results/ResultsView.java b/src/main/java/com/example/application/views/results/ResultsView.java index cfd5ae4..bf3c8fb 100644 --- a/src/main/java/com/example/application/views/results/ResultsView.java +++ b/src/main/java/com/example/application/views/results/ResultsView.java @@ -1,181 +1,72 @@ package com.example.application.views.results; -import com.example.application.data.bean.CalculatedMatch; -import com.example.application.data.service.MatchService; -import com.example.application.data.service.MatchdayService; -import com.example.application.data.service.SeasonService; -import com.example.application.utils.StringUtils; -import com.example.application.utils.VaadinUtils; -import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigationView; +import com.example.application.data.service.*; import com.example.application.views.main.MainView; -import com.vaadin.flow.component.ClickEvent; -import com.vaadin.flow.component.ComponentEventListener; -import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.dependency.CssImport; -import com.vaadin.flow.component.grid.ColumnTextAlign; -import com.vaadin.flow.component.grid.Grid; -import com.vaadin.flow.component.grid.GridVariant; -import com.vaadin.flow.component.html.Label; -import com.vaadin.flow.component.icon.Icon; -import com.vaadin.flow.component.icon.VaadinIcon; -import com.vaadin.flow.component.orderedlayout.FlexComponent; -import com.vaadin.flow.component.orderedlayout.HorizontalLayout; -import com.vaadin.flow.component.orderedlayout.VerticalLayout; -import com.vaadin.flow.function.ValueProvider; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.splitlayout.SplitLayout; +import com.vaadin.flow.router.*; import org.springframework.beans.factory.annotation.Autowired; @CssImport("./views/results/results-view.css") @Route(value = "results", layout = MainView.class) @PageTitle("Schachliga DACH - Results") -public class ResultsView extends SeasonAndMatchdayNavigationView { +public class ResultsView extends Div implements HasUrlParameter { - private final MatchService matchService; + private final SplitLayout splitLayout = new SplitLayout(); - private VerticalLayout matchdayLayout; - private Label matchdayHeader; - private Grid grid; + private final MatchdayView matchdayView; + private final MatchView matchView; - private Button prevButton; - private Button nextButton; + public ResultsView(@Autowired SeasonService seasonService, + @Autowired MatchdayService matchdayService, + @Autowired MatchService matchService, + @Autowired ChessComService chessComService, + @Autowired PlayerService playerService) { - public ResultsView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired MatchService matchService) { - super(seasonService, matchdayService); - this.matchService = matchService; - addClassName("results-view"); - } - - @Override - protected String route() { - return "results"; - } - - @Override - protected boolean showOnlyMatchdaysWithActivity() { - return false; - } - - //////////// - // LAYOUT // - //////////// + this.matchdayView = new MatchdayView(seasonService, matchdayService, matchService); + this.matchView = new MatchView(); - private VerticalLayout getMatchdayLayout() { - if (matchdayLayout == null) { - matchdayLayout = new VerticalLayout(); - } - return matchdayLayout; - } - - private Label getMatchdayHeader() { - if (matchdayHeader == null) { - matchdayHeader = new Label(); - } - return matchdayHeader; - } - - private Grid getGrid() { - if (grid == null) { - grid = new Grid<>(); - } - return grid; - } + addClassName("results-view"); - private Button getPrevButton() { - if (prevButton == null) { - prevButton = new Button(new Icon(VaadinIcon.ARROW_LEFT)); - } - return prevButton; + configureLayout(); +// disableSplit(); + enableSplit(); } - private Button getNextButton() { - if (nextButton == null) { - nextButton = new Button(new Icon(VaadinIcon.ARROW_RIGHT)); - } - return nextButton; + public void enableSplit() { + removeAll(); + add(splitLayout); } - @Override - protected void configureContentLayout() { - contentLayout = getMatchdayLayout(); - configureMatchdayLayout(); + public void disableSplit() { + removeAll(); + add(matchdayView); } - private void configureMatchdayLayout() { - // TODO add background color for content - getMatchdayHeader().addClassName("matchday_header"); // TODO: add dates - getMatchdayHeader().setWidth("100%"); - HorizontalLayout matchdayHeaderLayout = new HorizontalLayout(getPrevButton(), getMatchdayHeader(), getNextButton()); - matchdayHeaderLayout.setWidthFull(); - - getMatchdayLayout().setPadding(false); - getMatchdayLayout().add(matchdayHeaderLayout, getGrid()); - getMatchdayLayout().setAlignItems(FlexComponent.Alignment.CENTER); - getMatchdayLayout().setWidth(""); - - Label headerPlayer1 = new Label("Player 1"); - headerPlayer1.addClassName("column_header"); - Label headerPlayer2 = new Label("Player 2"); - headerPlayer2.addClassName("column_header"); + private void configureLayout() { + setWidthFull(); + setHeightFull(); - getGrid().addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer1)) - .setHeader(headerPlayer1) - .setTextAlign(ColumnTextAlign.END) - .setWidth("13em") - .setFlexGrow(1); - - getGrid().addColumn((ValueProvider) this::getResultString) - .setHeader("vs.") - .setTextAlign(ColumnTextAlign.CENTER) - .setWidth("6em"); - - getGrid().addColumn(VaadinUtils.getPlayerRenderer(CalculatedMatch::getPlayer2)) - .setHeader(headerPlayer2) - .setTextAlign(ColumnTextAlign.START) - .setWidth("13em") - .setFlexGrow(1); - - getGrid().setWidth("32em"); // TODO: find a way to set this dynamically based on column widths - getGrid().setHeightByRows(true); - - getGrid().addThemeVariants(GridVariant.LUMO_NO_BORDER, - GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES); + splitLayout.addToPrimary(matchdayView); + splitLayout.addToSecondary(matchView); + splitLayout.setWidthFull(); + splitLayout.setHeightFull(); } - private String getResultString(CalculatedMatch match) { - return StringUtils.getResultString(match.getScore1(), match.getScore2()); - } - - - ///////////// - // CONTENT // - ///////////// - @Override - protected void configureContent() { - getMatchdayHeader().setText(String.format("Matchday %s", matchdaySelect.getValue().toString())); - getGrid().setItems(matchService.getCalculatedMatches(matchdaySelect.getValue())); - configureButtons(); - } - - private void configureButtons() { - getPrevButton().setEnabled(isMatchDayParamValid(getPrevMatchdayParam())); - getPrevButton().addClickListener(getButtonClickListener(getPrevMatchdayParam())); - - getNextButton().setEnabled(isMatchDayParamValid(getNextMatchdayParam())); - getNextButton().addClickListener(getButtonClickListener(getNextMatchdayParam())); + public void setParameter(BeforeEvent beforeEvent, @WildcardParameter String param) { +// String[] params = param.split("/"); +// if (params.length < 2) { +// // TODO: handle this case! +// return; +// } +// String[] seasonParamAndMatchdayParam = Arrays.copyOfRange(params, 0, 2); + matchdayView.setParameter(beforeEvent, param); + +// if (params.length < 3) { +// disableSplit(); +// } +// matchView.setParameter(beforeEvent, params); } - - private ComponentEventListener> getButtonClickListener(String matchdayParam) { - return buttonClickEvent -> getUI().ifPresent(ui -> navigate(ui, seasonParam, matchdayParam)); - } - - private String getPrevMatchdayParam() { - return String.valueOf(Integer.parseInt(matchdayParam) - 1); - } - - private String getNextMatchdayParam() { - return String.valueOf(Integer.parseInt(matchdayParam) + 1); - } - } diff --git a/src/main/java/com/example/application/views/table/TableView.java b/src/main/java/com/example/application/views/table/TableView.java index c540bb2..a40fb28 100644 --- a/src/main/java/com/example/application/views/table/TableView.java +++ b/src/main/java/com/example/application/views/table/TableView.java @@ -1,69 +1,72 @@ package com.example.application.views.table; import com.example.application.data.bean.PlayerForTable; -import com.example.application.data.entity.Player; import com.example.application.data.service.MatchdayService; import com.example.application.data.service.PlayerService; import com.example.application.data.service.SeasonService; import com.example.application.utils.StringUtils; import com.example.application.utils.VaadinUtils; -import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigationView; +import com.example.application.views.abstractnavigation.SeasonAndMatchdayNavigation; import com.example.application.views.main.MainView; import com.vaadin.flow.component.dependency.CssImport; import com.vaadin.flow.component.grid.ColumnTextAlign; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.html.Label; +import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.function.ValueProvider; -import com.vaadin.flow.router.PageTitle; -import com.vaadin.flow.router.Route; -import com.vaadin.flow.router.RouteAlias; +import com.vaadin.flow.router.*; import org.springframework.beans.factory.annotation.Autowired; +import java.util.Arrays; +import java.util.NoSuchElementException; + @CssImport("./views/table/table-view.css") @Route(value = "table", layout = MainView.class) @RouteAlias(value = "", layout = MainView.class) @PageTitle("Schachliga DACH - Table") -public class TableView extends SeasonAndMatchdayNavigationView { +public class TableView extends VerticalLayout implements HasUrlParameter { private final PlayerService playerService; - private Grid grid; + private final SeasonAndMatchdayNavigation seasonAndMatchdayNavigation; + private final Label invalidUrlLabel; + + private final Grid grid = new Grid<>(); public TableView(@Autowired SeasonService seasonService, @Autowired MatchdayService matchdayService, @Autowired PlayerService playerService) { - super(seasonService, matchdayService); this.playerService = playerService; - addClassName("table-view"); - } - @Override - protected String route() { - return "table"; - } + this.seasonAndMatchdayNavigation = new SeasonAndMatchdayNavigation("table", seasonService, matchdayService, true); + this.seasonAndMatchdayNavigation.setRunnableToBeRunAfterSelection(this::configureContent); + this.invalidUrlLabel = seasonAndMatchdayNavigation.getInvalidUrlLabel(); - @Override - protected boolean showOnlyMatchdaysWithActivity() { - return true; + addClassName("table-view"); + configureLayout(); + +// seasonAndMatchdayNavigation.addRunnableToBeRunAfterSeasonParamChange(this::configureContent); +// seasonAndMatchdayNavigation.addRunnableToBeRunAfterMatchdayParamChange(this::configureContent); } //////////// // LAYOUT // //////////// - private Grid getGrid() { - if (grid == null) { - grid = new Grid<>(); - } - return grid; + private void configureLayout() { + setWidthFull(); + setHeightFull(); + setAlignItems(FlexComponent.Alignment.CENTER); + + add(seasonAndMatchdayNavigation.getSelectionLayout(), new HorizontalLayout(grid)); + + configureGrid(); } - @Override - protected void configureContentLayout() { + protected void configureGrid() { // TODO: add background color for content // TODO: add diff to last matchday - // TODO: bold matchpoints etc. - contentLayout = new HorizontalLayout(getGrid()); Label headerPlace = new Label("Place"); headerPlace.addClassName("important_table_column_header"); @@ -86,55 +89,55 @@ public class TableView extends SeasonAndMatchdayNavigationView { Label headerDiff = new Label("Diff"); - getGrid().addColumn(VaadinUtils.getBoldStringRenderer(PlayerForTable::getPlaceString)) + grid.addColumn(VaadinUtils.getBoldStringRenderer(PlayerForTable::getPlaceString)) .setHeader(headerPlace) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("5em"); - getGrid().addColumn(VaadinUtils.getPlayerRenderer(PlayerForTable::getPlayer)) + grid.addColumn(VaadinUtils.getPlayerRenderer(PlayerForTable::getPlayer)) .setHeader(headerPlayer) .setTextAlign(ColumnTextAlign.START) .setWidth("13em"); - getGrid().addColumn((ValueProvider) PlayerForTable::getAmountOfMatches) + grid.addColumn((ValueProvider) PlayerForTable::getAmountOfMatches) .setHeader(headerMatchesPlayed) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("5em"); - getGrid().addColumn(VaadinUtils.getBoldStringRenderer(player -> String.valueOf(player.getMatchPoints()))) + grid.addColumn(VaadinUtils.getBoldStringRenderer(player -> String.valueOf(player.getMatchPoints()))) .setHeader(headerMatchPoints) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("6em"); - getGrid().addColumn((ValueProvider) PlayerForTable::getAmountOfMatchesWon) + grid.addColumn((ValueProvider) PlayerForTable::getAmountOfMatchesWon) .setHeader(headerWon) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("3em"); - getGrid().addColumn((ValueProvider) PlayerForTable::getAmountOfMatchesDrawn) + grid.addColumn((ValueProvider) PlayerForTable::getAmountOfMatchesDrawn) .setHeader(headerDrawn) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("3em"); - getGrid().addColumn((ValueProvider) PlayerForTable::getAmountOfMatchesLost) + grid.addColumn((ValueProvider) PlayerForTable::getAmountOfMatchesLost) .setHeader(headerLost) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("3em"); - getGrid().addColumn((ValueProvider) this::getResultString) + grid.addColumn((ValueProvider) this::getResultString) .setHeader(headerGames) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("6em"); - getGrid().addColumn((ValueProvider) player -> StringUtils.getSignedString(player.getGamePointDiff())) + grid.addColumn((ValueProvider) player -> StringUtils.getSignedString(player.getGamePointDiff())) .setHeader(headerDiff) .setTextAlign(ColumnTextAlign.CENTER) .setWidth("5em"); - getGrid().setWidth("51em"); - getGrid().setHeightByRows(true); + grid.setWidth("51em"); + grid.setHeightByRows(true); - getGrid().addThemeVariants(GridVariant.LUMO_NO_BORDER, + grid.addThemeVariants(GridVariant.LUMO_NO_BORDER, GridVariant.LUMO_NO_ROW_BORDERS, GridVariant.LUMO_ROW_STRIPES); } @@ -146,8 +149,40 @@ public class TableView extends SeasonAndMatchdayNavigationView { // CONTENT // ///////////// - @Override protected void configureContent() { - getGrid().setItems(playerService.getPlayersForTable(matchdaySelect.getValue())); + try { + grid.setItems(playerService.getPlayersForTable(seasonAndMatchdayNavigation.getSelectedMatchday().orElseThrow())); + } catch (NoSuchElementException e) { + invalidUrlLabel.setText("No season and/or matchday selected! Please select them above."); + } + } + + @Override + public void setParameter(BeforeEvent event, @WildcardParameter String param) { + String[] params = param.split("/"); + String seasonParam; + String matchdayParam; + switch (params.length) { + case 0: + seasonParam = ""; + matchdayParam = ""; + break; + case 1: + seasonParam = params[0]; + matchdayParam = ""; + break; + default: + seasonParam = params[0]; + matchdayParam = params[1]; + } + seasonAndMatchdayNavigation.setParameter(seasonParam, matchdayParam); + } + + private String[] getSeasonParamAndMatchdayParam(String[] params) { + if (params.length < 2) { + String seasonParam = params.length == 1 ? params[0] : ""; + params = new String[]{seasonParam, ""}; + } + return Arrays.copyOfRange(params, 0, 2); } }