/* * Copyright (C) 2017 John Garner * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.pikatimer.race; import com.pikatimer.results.RaceReport; import com.pikatimer.timing.Segment; import com.pikatimer.timing.Split; import com.pikatimer.util.DurationFormatter; import com.pikatimer.util.Unit; import java.math.BigDecimal; import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import javafx.beans.property.BooleanProperty; import javafx.beans.property.IntegerProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleIntegerProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javax.persistence.CascadeType; import javax.persistence.CollectionTable; import javax.persistence.Column; import javax.persistence.ElementCollection; import javax.persistence.Entity; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.FetchType; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.MapKeyColumn; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.OrderBy; import javax.persistence.PrimaryKeyJoinColumn; import javax.persistence.Table; import javax.persistence.Transient; import org.hibernate.annotations.DynamicUpdate; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; import org.hibernate.annotations.GenericGenerator; @Entity @DynamicUpdate @Table(name="race") public class Race { private final IntegerProperty IDProperty; private final StringProperty uuidProperty = new SimpleStringProperty(java.util.UUID.randomUUID().toString()); private BigDecimal raceDistance; private Unit raceUnits; private final StringProperty raceUnitsProperty; private final StringProperty raceName; private Duration raceCutoff; private final StringProperty raceCutoffProperty; private final StringProperty raceBibStart; private final StringProperty raceBibEnd; private final BooleanProperty relayRace; private final StringProperty raceDistanceProperty; private final ObservableList<Wave> raceWaves; private List<Wave> raceWavesList; private final ObservableList<Split> raceSplits; private List<Split> raceSplitList; private List<RaceReport> raceReportsList; private final ObservableList<RaceReport> raceReports; private List<Segment> segmentsList; private final ObservableList<Segment> raceSegments = FXCollections.observableArrayList(Segment.extractor()); //private final Race self; private RaceAwards awards; private AgeGroups ageGroups; private Map<String,String> attributes = new HashMap(); private Map<String,Integer> intAttributes = new HashMap(); private Map<String,Boolean> boolAttributes = new HashMap(); public Race() { this.IDProperty = new SimpleIntegerProperty(); this.raceUnitsProperty = new SimpleStringProperty(); this.raceName = new SimpleStringProperty(); this.raceBibStart = new SimpleStringProperty(); this.raceBibEnd = new SimpleStringProperty(); this.raceCutoffProperty = new SimpleStringProperty(); this.relayRace = new SimpleBooleanProperty(); this.raceDistanceProperty = new SimpleStringProperty(); this.raceWaves = FXCollections.observableArrayList(Wave.extractor()); this.raceSplits = FXCollections.observableArrayList(Split.extractor()); this.raceReports = FXCollections.observableArrayList(); } @Id @GenericGenerator(name="race_id" , strategy="increment") @GeneratedValue(generator="race_id") @Column(name="RACE_ID") public Integer getID() { return IDProperty.getValue(); } public void setID(Integer id) { IDProperty.setValue(id); } public IntegerProperty idProperty() { return IDProperty; } @Column(name="RACE_NAME") public String getRaceName() { return raceName.getValueSafe(); } public void setRaceName(String n) { raceName.setValue(n); } public StringProperty raceNameProperty() { return raceName; } @Column(name="RACE_DISTANCE") public BigDecimal getRaceDistance() { return raceDistance; } public void setRaceDistance(BigDecimal d) { raceDistance = d; if(raceDistance != null && raceUnits != null) raceDistanceProperty.setValue(raceDistance.toPlainString()+raceUnits.toShortString()); //raceDistance = new BigDecimal(d).setScale(3, BigDecimal.ROUND_HALF_UP); } public StringProperty raceDistanceProperty() { return raceDistanceProperty; } @Enumerated(EnumType.STRING) @Column(name="RACE_DIST_UNIT") public Unit getRaceDistanceUnits() { return raceUnits; } public void setRaceDistanceUnits(Unit d) { if ( raceUnits != d) { // update existing splits raceUnits=d; raceSplits.stream().forEach(e -> { e.setSplitDistanceUnits(raceUnits); RaceDAO.getInstance().updateSplit(e); }); if (raceUnits != null) raceUnitsProperty.setValue(d.toString()); if(raceDistance != null && raceUnits != null) raceDistanceProperty.setValue(raceDistance.toPlainString() + " " +raceUnits.toShortString()); } } public StringProperty raceDistanceUnitsProperty() { return raceUnitsProperty; } @Column(name="RACE_BIB_START") public String getBibStart() { return raceBibStart.getValueSafe(); } public void setBibStart(String b) { raceBibStart.setValue(b); } public StringProperty bibStartProperty() { return raceBibStart; } @Column(name="RACE_BIB_END") public String getBibEnd() { return raceBibEnd.getValueSafe(); } public void setBibEnd(String b) { raceBibEnd.setValue(b); } public StringProperty bibEndProperty() { return raceBibEnd; } @Column(name="RACE_CUTOFF", nullable=true) public Long getRaceCutoff() { if (raceCutoff != null) { return raceCutoff.toNanos(); } else { return 0L; } } public void setRaceCutoff(Long c) { if(c != null) { System.out.println("setRaceCutoff " + c.toString()); raceCutoff = Duration.ofNanos(c); if (raceCutoff.isZero()) raceCutoffProperty.set(""); else raceCutoffProperty.set(DurationFormatter.durationToString(raceCutoff, 0, Boolean.TRUE)); //raceCutoffProperty.set(DurationFormatter.durationToString(raceCutoff,0)); } } public Duration raceCutoffDuration(){ return raceCutoff; } public StringProperty raceCutoffProperty(){ return raceCutoffProperty; } @OneToMany(mappedBy="race",cascade={CascadeType.PERSIST, CascadeType.REMOVE},fetch = FetchType.EAGER) @Fetch(FetchMode.SELECT) public List<Wave> getWaves() { return raceWavesList; } public void setWaves(List<Wave> waves) { raceWavesList = waves; if (waves == null) { System.out.println("Race.setWaves(list) called for " + raceName.getValueSafe() + "(" + IDProperty.toString() + ")" + " with null waves"); } else { System.out.println("Race.setWaves(list) called for " + raceName.getValueSafe() + "(" + IDProperty.toString() + ")" + " with " + waves.size() + " waves"); } if (waves != null) raceWaves.setAll(waves); System.out.println("Race.setWaves(list) " + raceName.getValueSafe() + "(" + IDProperty.toString() + ")" + " now has " + raceWaves.size() + " waves"); } public ObservableList<Wave> wavesProperty() { return raceWaves; } public void addWave(Wave w) { raceWaves.add(w); if (raceWavesList == null) raceWavesList = new ArrayList(); raceWavesList.add(w); } public void removeWave(Wave w) { if (raceWaves.contains(w)){ raceWaves.remove(w); raceWavesList.remove(w); } } @OneToMany(mappedBy="race",cascade={CascadeType.PERSIST, CascadeType.REMOVE},fetch = FetchType.EAGER) @OrderBy("split_seq_number") @Fetch(FetchMode.SELECT) public List<Split> getSplits() { return raceSplitList; //return raceSplits.sorted((Split o1, Split o2) -> o1.getPosition().compareTo(o2.getPosition())); } public void setSplits(List<Split> splits) { // System.out.println("Race.setSplits(list) called for " + raceName + " with " + splits.size() + " splits"); // splits.stream().forEach(e -> System.out.println(e.getSplitName() + " " + e.getPosition())); // splits // .stream() // .sorted((e1, e2) -> Integer.compare(e1.getPosition(), // e2.getPosition())) // .forEach(e -> System.out.println(e.getSplitName())); raceSplitList = splits; if (splits != null) raceSplits.setAll(splits); // System.out.println("Race.setSplits(list) " + raceName + " now has " + raceSplits.size() + " splits"); } public ObservableList<Split> splitsProperty() { return raceSplits; } public void addSplit(Split s) { if (s.getPosition() > 0) { raceSplits.add(s.getPosition()-1,s); } else if (raceSplits.size() < 2 ) { raceSplits.add(s); } else { raceSplits.add(raceSplits.size()-1, s); } raceSplitList = raceSplits.sorted((Split o1, Split o2) -> o1.getPosition().compareTo(o2.getPosition())); } public void removeSplit(Split s) { raceSplits.remove(s); raceSplitList = raceSplits.sorted((Split o1, Split o2) -> o1.getPosition().compareTo(o2.getPosition())); } @OneToOne(cascade=CascadeType.ALL,fetch = FetchType.EAGER) @PrimaryKeyJoinColumn public RaceAwards getAwards() { return awards; } public void setAwards(RaceAwards a) { System.out.println("Race::setAwards called.... "); if (awards == null) awards = a; if (awards != null && awards.equals(a)) System.out.println("Race::setAwards called to set the awards to an equal awards object... "); // make sure awards is linked back to us if (awards != null && awards.getRace() != this) awards.setRace(this); } @OneToOne(cascade=CascadeType.ALL,fetch = FetchType.EAGER) @PrimaryKeyJoinColumn public AgeGroups getAgeGroups() { return ageGroups; } public void setAgeGroups(AgeGroups a) { ageGroups = a; // make sure awards is linked back to us if (ageGroups != null && ageGroups.getRace() != this) ageGroups.setRace(this); } @OneToMany(mappedBy="race",cascade={CascadeType.PERSIST, CascadeType.REMOVE},fetch = FetchType.EAGER) @Fetch(FetchMode.SELECT) public List<RaceReport> getRaceReports() { return raceReportsList; } public void setRaceReports(List<RaceReport> rr) { raceReportsList = rr; if (rr == null) System.out.println("Race.setRaceReports(list) called with null list"); if (rr != null) raceReports.setAll(rr); System.out.println("Race.setRaceReports(list) " + raceName.getValueSafe() + "( " + IDProperty.getValue().toString() + ")" + " now has " + raceReports.size() + " Reports"); } public ObservableList<RaceReport> raceReportsProperty() { return raceReports; } public void addRaceReport(RaceReport w) { raceReports.add(w); w.setRace(this); //raceReportsList.add(w); raceReportsList = raceReports.sorted(); } public void removeRaceReport(RaceReport w) { raceReports.remove(w); //raceReportsList.remove(w); raceReportsList = raceReports.sorted(); } @OneToMany(mappedBy="race",cascade={CascadeType.PERSIST, CascadeType.REMOVE},fetch = FetchType.EAGER) @Fetch(FetchMode.SELECT) public List<Segment> getSegments() { if (segmentsList == null) segmentsList = new ArrayList(); return segmentsList; } public void setSegments(List<Segment> s) { segmentsList = s; if (s == null) System.out.println("Race.setRaceReports(list) called with null list"); if (s != null) raceSegments.setAll(s); //System.out.println("Race.setRaceReports(list) " + raceName.getValueSafe() + "( " + IDProperty.getValue().toString() + ")" + " now has " + raceReports.size() + " Reports"); } public ObservableList<Segment> raceSegmentsProperty() { return raceSegments.sorted((s1, s2)-> s1.compareTo(s2)); } public ObservableList<Segment> unsortedSegmentsProperty() { return raceSegments; } public void addRaceSegment(Segment s) { s.setRace(this); raceSegments.add(s); segmentsList = raceSegments.sorted((s1, s2)-> s1.compareTo(s2)); } public void removeRaceSegment(Segment s) { raceSegments.remove(s); segmentsList = raceSegments.sorted((s1, s2)-> s1.compareTo(s2)); } @Column(name="uuid") public String getUUID() { // System.out.println("Participant UUID is " + uuidProperty.get()); return uuidProperty.getValue(); } public void setUUID(String uuid) { uuidProperty.setValue(uuid); //System.out.println("Participant UUID is now " + uuidProperty.get()); } public StringProperty uuidProperty() { return uuidProperty; } // The map of attributes -> values // easier than a really wide table of attributes since this thing will just // grow once we add in custom stuff @ElementCollection(fetch = FetchType.EAGER) @MapKeyColumn(name="attribute", insertable=false,updatable=false) @Column(name="value") @CollectionTable(name="race_attributes", joinColumns=@JoinColumn(name="race_id")) private Map<String, String> getAttributes() { return attributes; } private void setAttributes(Map<String,String> m) { attributes = m; } @Transient public Set<String> getKnownAttributeNames() { return attributes.keySet(); } //Overall //male public Integer getIntegerAttribute(String key) { if (!intAttributes.containsKey(key)) { if (attributes.containsKey(key)) { intAttributes.put(key,Integer.parseUnsignedInt(attributes.get(key))); } else { System.out.println("RaceAwards.getIntegerAtrribute key of " + key + " is NULL!"); return null; } } return intAttributes.get(key); } public void setIntegerAttribute(String key, Integer n) { intAttributes.put(key,n); attributes.put(key, n.toString()); } //Pull, Gun, etc public Boolean getBooleanAttribute(String key) { if (!boolAttributes.containsKey(key)) { if (attributes.containsKey(key)) { boolAttributes.put(key,Boolean.parseBoolean(attributes.get(key))); } else { System.out.println("RaceAwards.getBooleanAtrribute key of " + key + " is NULL!"); return null; } } return boolAttributes.get(key); } public void setBooleanAttribute(String key, Boolean n) { boolAttributes.put(key,n); attributes.put(key, n.toString()); } public String getStringAttribute(String key) { if (!attributes.containsKey(key)) { return null; } return attributes.get(key); } public void setStringAttribute(String key, String v) { attributes.put(key, v); } @Override public int hashCode() { int hash = 5; hash = 89 * hash + Objects.hashCode(this.uuidProperty.getValue()); return hash; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Race other = (Race) obj; if (!Objects.equals(this.uuidProperty.getValue(),other.uuidProperty.getValue())) { return false; } return true; } @Override public String toString() { return raceName.getValueSafe(); } }