Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
4890aae
Refactor Match and Round classes to use Lombok annotations for boiler…
xji650 Feb 26, 2026
dc6f0a0
Merge remote-tracking branch 'origin' into fix/XiaoLong
xji650 Mar 2, 2026
8be15c3
Refactor domain classes to use Lombok annotations for boilerplate cod…
xji650 Mar 3, 2026
08b8d3e
Merge pull request #6 from UdL-Arch-Software-Empresite/fix/XiaoLong
xji650 Mar 3, 2026
1a14d3e
Refactor CompetitionTable and Round classes to improve setter behavio…
xji650 Mar 3, 2026
ca7c8d2
Merge pull request #7 from UdL-Arch-Software-Empresite/fix/XiaoLong
xji650 Mar 3, 2026
71038e6
Merge branch 'UdL-EPS-SoftArch-Igualada:main' into main
xji650 Mar 3, 2026
19e49b2
Refactor CompetitionTable, Match, and Round classes to improve match …
xji650 Mar 3, 2026
678083a
Refactor CompetitionTable and Match classes to update cascade behavio…
xji650 Mar 3, 2026
15c352b
Refactor CompetitionTable and Round classes to enhance match and refe…
xji650 Mar 3, 2026
e8bc4ad
Refactor match handling in Round class to simplify match addition and…
xji650 Mar 4, 2026
653d1b0
Merge pull request #8 from UdL-Arch-Software-Empresite/fix/XiaoLong
xji650 Mar 4, 2026
266e371
Merge branch 'main' into main
xji650 Mar 4, 2026
5b33af1
Refactor Match class to enhance state and relationship management wit…
xji650 Mar 4, 2026
a562ade
Merge pull request #9 from UdL-Arch-Software-Empresite/fix/XiaoLong
xji650 Mar 4, 2026
d7ae4c8
Remove unused import and ensure state is properly enumerated in Match…
xji650 Mar 4, 2026
00e3fe6
Change state field type from String to MatchState in Match class
xji650 Mar 4, 2026
21b8915
Refactor match management in Round class to improve clarity and effic…
xji650 Mar 4, 2026
62fc1e3
Refactor CompetitionTable and Match classes to enhance match and refe…
xji650 Mar 4, 2026
956367a
Remove commented code regarding identity check in addMatch method
xji650 Mar 4, 2026
e9949d9
Refactor setMatches method in Round class for improved clarity and ef…
xji650 Mar 4, 2026
c36aa63
Add step definitions for CompetitionTable, Match, Referee, and Round …
xji650 Mar 4, 2026
be1b573
Merge pull request #10 from UdL-Arch-Software-Empresite/test/cucumber…
xji650 Mar 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 55 additions & 38 deletions src/main/java/cat/udl/eps/softarch/fll/domain/CompetitionTable.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,88 +9,105 @@
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.Size;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Table(name = "competition_tables")
@Getter
@Setter
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
public class CompetitionTable extends UriEntity<String> {

@Id
@EqualsAndHashCode.Include
private String id;

@OneToMany(mappedBy = "competitionTable", cascade = CascadeType.ALL)
@OneToMany(mappedBy = "competitionTable", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@JsonManagedReference("table-matches")
@Setter(lombok.AccessLevel.NONE)
private List<Match> matches = new ArrayList<>();

@OneToMany(mappedBy = "supervisesTable")
@Size(max = 3, message = "A table can have a maximum of 3 referees")
@JsonManagedReference("table-referees")
@Setter(lombok.AccessLevel.NONE)
private List<Referee> referees = new ArrayList<>();


@Override
public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

public List<Match> getMatches() {
return matches;
}

public void setMatches(List<Match> matches) {
new ArrayList<>(this.matches).forEach(this::removeMatch);
if (matches != null) {
matches.forEach(this::addMatch);
}
}

public List<Referee> getReferees() {
return referees;
}
public void addMatch(Match match) {
if (match == null) {
return;
}

public void setReferees(List<Referee> referees) {
new ArrayList<>(this.referees).forEach(this::removeReferee);
if (referees != null) {
referees.forEach(this::addReferee);
if (this.matches.stream().anyMatch(m -> m == match)) {
return;
}
}

public void addMatch(Match match) {
CompetitionTable previousTable = match.getCompetitionTable();
if (previousTable != null && previousTable != this) {
previousTable.removeMatch(match);
}
if (!matches.contains(match)) {
matches.add(match);
previousTable.getMatches().removeIf(m -> m == match);
}

this.matches.add(match);
match.setCompetitionTable(this);
Comment on lines +52 to 62
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addMatch uses reference equality (m == match) to detect duplicates and to detach from the previous table. Since Match equality is id-based, this can allow duplicates (or fail to detach) when the same DB row is represented by another instance/proxy. Prefer matches.contains(match) / previousTable.removeMatch(match) for equality-based behavior and consistent bidirectional updates.

Copilot uses AI. Check for mistakes.
}
Comment on lines +47 to 63
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are Cucumber tests for addReferee/addMatch, but the changed semantics around moving/removing associations (especially setMatches/setReferees and the new duplicate/relocation logic) aren’t covered. Please add scenarios that verify removed matches/referees have their owning-side references cleared and that moving a match/referee between tables/rounds keeps both sides consistent.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines +61 to 63
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addMatch now supports transferring a Match from its previous CompetitionTable. There are no Cucumber scenarios covering moving a match between tables (and verifying the previous table no longer contains it), so regressions could slip in. Please add a test scenario for that transfer behavior.

Copilot generated this review using guidance from repository custom instructions.

public void removeMatch(Match match) {
matches.remove(match);
match.setCompetitionTable(null);
if (match == null) {
return;
}

if (this.matches.removeIf(m -> m == match)) {
match.setCompetitionTable(null);
}
Comment on lines 65 to +72
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeMatch uses removeIf(m -> m == match), so removal only works for the exact same instance. With JPA, it’s common to have a different instance/proxy representing the same Match id, in which case removal will silently fail. Use equality-based removal (e.g., matches.remove(match) / removeIf(match::equals)).

Copilot uses AI. Check for mistakes.
}

public void setReferees(List<Referee> referees) {
new ArrayList<>(this.referees).forEach(this::removeReferee);
if (referees != null) {
referees.forEach(this::addReferee);
}
}
Comment on lines +75 to +80
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CompetitionTable.setReferees clears the collection without calling removeReferee (or otherwise nulling Referee.supervisesTable) for previously associated referees. Since Referee owns the relationship (ManyToOne supervisesTable), this can leave old referees still pointing at this table, producing an inconsistent bidirectional association. Consider iterating existing referees and removing them via removeReferee before re-adding the new list.

Copilot uses AI. Check for mistakes.

public void addReferee(Referee referee) {
CompetitionTable previousTable = referee.getSupervisesTable();
if (previousTable != null && previousTable != this) {
previousTable.removeReferee(referee);
if (referee == null) {
return;
}
if (referees.contains(referee)) {

if (this.referees.stream().anyMatch(r -> r == referee)) {
return;
}
Comment on lines +87 to 89
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addReferee checks duplicates using identity (r == referee). With JPA proxies/detached instances this can allow duplicates for the same underlying row. Prefer comparing by a stable key (e.g., non-null id) and falling back to reference equality only when the key is null.

Copilot uses AI. Check for mistakes.
if (referees.size() >= 3) {

if (this.referees.size() >= 3) {
throw new IllegalStateException("A table can have a maximum of 3 referees");
}
referees.add(referee);

CompetitionTable previousTable = referee.getSupervisesTable();
if (previousTable != null && previousTable != this) {
previousTable.getReferees().removeIf(r -> r == referee);
}
Comment on lines +96 to +98
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing from previousTable via previousTable.getReferees().removeIf(r -> r == referee) relies on reference equality and can fail when the same referee is represented by a different instance (detached/proxy). Prefer removal by stable key (e.g., non-null id) and only fall back to reference equality for transient entities.

Copilot uses AI. Check for mistakes.

this.referees.add(referee);
referee.setSupervisesTable(this);
Comment on lines +87 to 101
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addReferee prevents duplicates using reference equality (r == referee) and detaches from the previous table with removeIf(r -> r == referee). If the same referee is represented by another instance/proxy with the same id, this can lead to duplicates and inconsistent lists. Prefer referees.contains(referee) and previousTable.removeReferee(referee) for equality-based behavior and bidirectional consistency.

Copilot uses AI. Check for mistakes.
}
Comment on lines +100 to 102
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addReferee now supports transferring a referee from a previous table. There are no Cucumber tests covering moving a referee between tables and asserting the previous table no longer lists the referee. Please add a scenario for that transfer behavior.

Copilot generated this review using guidance from repository custom instructions.

public void removeReferee(Referee referee) {
referees.remove(referee);
referee.setSupervisesTable(null);
if (referee == null) {
return;
}

if (this.referees.removeIf(r -> r == referee)) {
referee.setSupervisesTable(null);
}
Comment on lines 104 to +111
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeReferee uses removeIf(r -> r == referee), which won’t remove an equal referee represented by a different instance/proxy. Use equality-based removal (e.g., referees.remove(referee) / removeIf(referee::equals)) to ensure the association is reliably cleared.

Copilot uses AI. Check for mistakes.
}
}
107 changes: 20 additions & 87 deletions src/main/java/cat/udl/eps/softarch/fll/domain/Match.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,33 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "matches")
@Getter
@Setter
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
public class Match extends UriEntity<Long> {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Comment on lines +26 to 30
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EqualsAndHashCode(onlyExplicitlyIncluded = true) with only the generated id included means two transient Match instances (both with id == null) will compare equal and share the same hash code. That can break Set/List.contains behavior and forces identity (==) workarounds elsewhere. Consider a custom equals/hashCode that uses id only when non-null and otherwise falls back to reference equality.

Copilot uses AI. Check for mistakes.
@EqualsAndHashCode.Include
private Long id;
Comment on lines +23 to 32
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EqualsAndHashCode(onlyExplicitlyIncluded = true) with only id included makes two transient Match objects (both with id == null) compare equal. This breaks collection operations like List.contains and can prevent adding multiple new matches. Consider implementing equals/hashCode so that entities with null ids are only equal to themselves (reference equality), or avoid using id-only equality until persistence assigns an id.

Copilot uses AI. Check for mistakes.

private LocalTime startTime;

private LocalTime endTime;

@JsonBackReference("round-matches")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "round_id")
private Round round;
@Enumerated(EnumType.STRING)
private MatchState state = MatchState.SCHEDULED;

@JsonBackReference("table-matches")
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "table_id")
private CompetitionTable competitionTable;
@JoinColumn(name = "referee_id")
private Referee referee;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "team_a_id")
Expand All @@ -47,84 +52,12 @@ public class Match extends UriEntity<Long> {
private Team teamB;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "referee_id")
private Referee referee;

@Enumerated(EnumType.STRING)
private MatchState state = MatchState.SCHEDULED;

public Match() {}

@Override
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public LocalTime getStartTime() {
return startTime;
}

public void setStartTime(LocalTime startTime) {
this.startTime = startTime;
}

public LocalTime getEndTime() {
return endTime;
}

public void setEndTime(LocalTime endTime) {
this.endTime = endTime;
}

public Round getRound() {
return round;
}

public void setRound(Round round) {
this.round = round;
}

public CompetitionTable getCompetitionTable() {
return competitionTable;
}

public void setCompetitionTable(CompetitionTable competitionTable) {
this.competitionTable = competitionTable;
}

public Team getTeamA() {
return teamA;
}

public void setTeamA(Team teamA) {
this.teamA = teamA;
}

public Team getTeamB() {
return teamB;
}

public void setTeamB(Team teamB) {
this.teamB = teamB;
}

public Referee getReferee() {
return referee;
}

public void setReferee(Referee referee) {
this.referee = referee;
}

public MatchState getState() {
return state;
}
@JoinColumn(name = "round_id")
@JsonBackReference("round-matches")
private Round round;

public void setState(MatchState state) {
this.state = state;
}
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "table_id")
@JsonBackReference("table-matches")
private CompetitionTable competitionTable;
}
25 changes: 8 additions & 17 deletions src/main/java/cat/udl/eps/softarch/fll/domain/Referee.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;


@Entity
@Table(name = "referees")
@Getter
@Setter
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
public class Referee extends Volunteer {

private boolean expert;
Expand All @@ -18,21 +26,4 @@ public class Referee extends Volunteer {
@JoinColumn(name = "supervises_table_id")
@JsonBackReference("table-referees")
private CompetitionTable supervisesTable;


public boolean isExpert() {
return expert;
}

public void setExpert(boolean expert) {
this.expert = expert;
}

public CompetitionTable getSupervisesTable() {
return supervisesTable;
}

public void setSupervisesTable(CompetitionTable supervisesTable) {
this.supervisesTable = supervisesTable;
}
}
Loading
Loading