cdb31781cca1357812e724dd5b2af80f180d0004
home.md
| ... | ... | @@ -6,6 +6,8 @@ This is the <img src="http://www.sapsailing.com/images/sap-logo_grey.png"/> Wiki |
| 6 | 6 | |
| 7 | 7 | Like businesses, sailors need the latest information to make strategic decisions - but they need it even faster. One wrong tack, a false estimation of the current, or the slightest wind shift can cost the skipper the entire race. As premium sponsor of the Kieler Woche 2011, and co-sponsor of Sailing Team Germany (STG), SAP is showing how innovative IT solutions providing real time data analysis can give teams the competitive edge. |
| 8 | 8 | |
| 9 | +SAP is at the center of today’s technology revolution, developing innovations that not only help businesses run like never before, but also improve the lives of people everywhere. As market leader in enterprise application software, SAP SE (NYSE: SAP) helps companies of all sizes and industries run better. From back office to boardroom, warehouse to storefront, desktop to mobile device – SAP empowers people and organizations to work together more efficiently and use business insight more effectively to stay ahead of the competition. SAP applications and services enable more than 258,000 customers to operate profitably, adapt continuously, and grow sustainably. |
|
| 10 | + |
|
| 9 | 11 | ### Table of Contents |
| 10 | 12 | |
| 11 | 13 | * [[Information about this Wiki and HowTo|wiki/howto]] |
| ... | ... | @@ -38,6 +40,7 @@ Like businesses, sailors need the latest information to make strategic decisions |
| 38 | 40 | * [[Layout repository|wiki/webdesign]] |
| 39 | 41 | * [[Create boat graphics for the 2D race viewer|wiki/boatGraphicsSVG]] |
| 40 | 42 | * [[Create clickable UI prototypes with Axure|wiki/ui-clickable-prototypes]] |
| 43 | + * [[Uploading Media Content|wiki/uploading-media-content]] |
|
| 41 | 44 | * General Information |
| 42 | 45 | * [[Architecture and Infrastructure|wiki/architecture-and-infrastructure]] |
| 43 | 46 | * [[Sailing Domain Algorithms|wiki/sailing-domain-algorithms]] |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/AbstractBearing.java
| ... | ... | @@ -60,5 +60,10 @@ public abstract class AbstractBearing implements Bearing { |
| 60 | 60 | return object != null && object instanceof Bearing && getDegrees() == ((Bearing) object).getDegrees();
|
| 61 | 61 | }
|
| 62 | 62 | |
| 63 | + @Override
|
|
| 64 | + public double getRadians() {
|
|
| 65 | + return getDegrees() / 180. * Math.PI;
|
|
| 66 | + }
|
|
| 67 | +
|
|
| 63 | 68 | |
| 64 | 69 | }
|
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/BoatClassMasterdata.java
| ... | ... | @@ -18,6 +18,7 @@ public enum BoatClassMasterdata { |
| 18 | 18 | B_ONE ("B/ONE", true, 7.80, 2.49, BoatHullType.MONOHULL, "B-ONE"),
|
| 19 | 19 | DRAGON_INT ("Dragon Int.", true, 8.89, 1.96, BoatHullType.MONOHULL, "Drachen", "Dragon"),
|
| 20 | 20 | EXTREME_40 ("Extreme 40", false, 12.2, 6.60, BoatHullType.CATAMARAN, "Extreme-40", "Extreme40", "ESS40"),
|
| 21 | + D_35 ("D35", false, 10.81, 6.89, BoatHullType.CATAMARAN),
|
|
| 21 | 22 | EUROPE_INT ("Europe Int.", true, 3.35, 1.35, BoatHullType.MONOHULL, "Europe"),
|
| 22 | 23 | F_18 ("Formula 18", true, 6.85, 2.25, BoatHullType.CATAMARAN, "F18", "F-18"),
|
| 23 | 24 | FARR_30 ("Farr 30", true, 9.42, 3.08, BoatHullType.MONOHULL, "F30", "F-30", "Farr-30"),
|
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ImageSize.java
| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | +package com.sap.sailing.domain.common; |
|
| 2 | + |
|
| 3 | +import java.io.Serializable; |
|
| 4 | + |
|
| 5 | +public interface ImageSize extends Serializable { |
|
| 6 | + int getWidth(); |
|
| 7 | + int getHeight(); |
|
| 8 | +} |
|
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ScoringSchemeType.java
| ... | ... | @@ -4,5 +4,6 @@ public enum ScoringSchemeType { |
| 4 | 4 | LOW_POINT, HIGH_POINT, HIGH_POINT_ESS_OVERALL, HIGH_POINT_LAST_BREAKS_TIE, HIGH_POINT_FIRST_GETS_TEN,
|
| 5 | 5 | HIGH_POINT_FIRST_GETS_ONE, LOW_POINT_WINNER_GETS_ZERO, HIGH_POINT_WINNER_GETS_SIX, HIGH_POINT_WINNER_GETS_FIVE,
|
| 6 | 6 | HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT,
|
| 7 | - HIGH_POINT_WINNER_GETS_FIVE_IGNORING_RACE_COUNT
|
|
| 7 | + HIGH_POINT_WINNER_GETS_FIVE_IGNORING_RACE_COUNT,
|
|
| 8 | + HIGH_POINT_WINNER_GETS_SIX_IGNORING_RACE_COUNT
|
|
| 8 | 9 | }
|
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/AbstractTimePoint.java
| ... | ... | @@ -89,7 +89,13 @@ public abstract class AbstractTimePoint implements TimePoint { |
| 89 | 89 | |
| 90 | 90 | @Override |
| 91 | 91 | public boolean equals(Object obj) { |
| 92 | - return compareTo((TimePoint) obj) == 0; |
|
| 92 | + if (this == obj) { |
|
| 93 | + return true; |
|
| 94 | + } else if (obj instanceof TimePoint) { |
|
| 95 | + return compareTo((TimePoint) obj) == 0; |
|
| 96 | + } else { |
|
| 97 | + return false; |
|
| 98 | + } |
|
| 93 | 99 | } |
| 94 | 100 | |
| 95 | 101 | @Override |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/DegreeBearingImpl.java
| ... | ... | @@ -20,10 +20,5 @@ public class DegreeBearingImpl extends AbstractBearing implements Bearing { |
| 20 | 20 | public double getDegrees() {
|
| 21 | 21 | return bearingDeg;
|
| 22 | 22 | }
|
| 23 | -
|
|
| 24 | - @Override
|
|
| 25 | - public double getRadians() {
|
|
| 26 | - return getDegrees() / 180. * Math.PI;
|
|
| 27 | - }
|
|
| 28 | 23 | |
| 29 | 24 | }
|
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/ImageSizeImpl.java
| ... | ... | @@ -0,0 +1,58 @@ |
| 1 | +package com.sap.sailing.domain.common.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.common.ImageSize; |
|
| 4 | + |
|
| 5 | +public class ImageSizeImpl implements ImageSize { |
|
| 6 | + private static final long serialVersionUID = 1170701774852068780L; |
|
| 7 | + private int width; |
|
| 8 | + private int height; |
|
| 9 | + |
|
| 10 | + ImageSizeImpl() { |
|
| 11 | + } // for GWT serialization |
|
| 12 | + |
|
| 13 | + public ImageSizeImpl(int width, int height) { |
|
| 14 | + super(); |
|
| 15 | + this.width = width; |
|
| 16 | + this.height = height; |
|
| 17 | + } |
|
| 18 | + |
|
| 19 | + @Override |
|
| 20 | + public int getWidth() { |
|
| 21 | + return width; |
|
| 22 | + } |
|
| 23 | + |
|
| 24 | + @Override |
|
| 25 | + public int getHeight() { |
|
| 26 | + return height; |
|
| 27 | + } |
|
| 28 | + |
|
| 29 | + @Override |
|
| 30 | + public String toString() { |
|
| 31 | + return "(" + getWidth() + "x" + getHeight() + ")"; |
|
| 32 | + } |
|
| 33 | + |
|
| 34 | + @Override |
|
| 35 | + public int hashCode() { |
|
| 36 | + final int prime = 31; |
|
| 37 | + int result = 1; |
|
| 38 | + result = prime * result + height; |
|
| 39 | + result = prime * result + width; |
|
| 40 | + return result; |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + @Override |
|
| 44 | + public boolean equals(Object obj) { |
|
| 45 | + if (this == obj) |
|
| 46 | + return true; |
|
| 47 | + if (obj == null) |
|
| 48 | + return false; |
|
| 49 | + if (getClass() != obj.getClass()) |
|
| 50 | + return false; |
|
| 51 | + ImageSizeImpl other = (ImageSizeImpl) obj; |
|
| 52 | + if (height != other.height) |
|
| 53 | + return false; |
|
| 54 | + if (width != other.width) |
|
| 55 | + return false; |
|
| 56 | + return true; |
|
| 57 | + } |
|
| 58 | +} |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/MillisecondsDurationImpl.java
| ... | ... | @@ -80,4 +80,21 @@ public class MillisecondsDurationImpl implements Duration { |
| 80 | 80 | long diff = asMillis() - o.asMillis(); |
| 81 | 81 | return diff > 0l ? 1 : diff < 0l ? -1 : 0; |
| 82 | 82 | } |
| 83 | + |
|
| 84 | + @Override |
|
| 85 | + public int hashCode() { |
|
| 86 | + return (int) (asMillis() & Integer.MAX_VALUE); |
|
| 87 | + } |
|
| 88 | + |
|
| 89 | + @Override |
|
| 90 | + public boolean equals(Object obj) { |
|
| 91 | + if (this == obj) { |
|
| 92 | + return true; |
|
| 93 | + } else if (obj instanceof Duration) { |
|
| 94 | + return compareTo((Duration) obj) == 0; |
|
| 95 | + } else { |
|
| 96 | + return false; |
|
| 97 | + } |
|
| 98 | + } |
|
| 99 | + |
|
| 83 | 100 | } |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaTrack.java
| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | 1 | package com.sap.sailing.domain.common.media;
|
| 2 | 2 | |
| 3 | 3 | import java.io.Serializable;
|
| 4 | -import java.util.Date;
|
|
| 4 | +
|
|
| 5 | +import com.sap.sailing.domain.common.Duration;
|
|
| 6 | +import com.sap.sailing.domain.common.TimePoint;
|
|
| 5 | 7 | |
| 6 | 8 | /**
|
| 7 | 9 | * See http://my.opera.com/core/blog/2010/03/03/everything-you-need-to-know-about-html5-video-and-audio-2
|
| ... | ... | @@ -69,38 +71,38 @@ public class MediaTrack implements Serializable { |
| 69 | 71 | public String dbId;
|
| 70 | 72 | public String title;
|
| 71 | 73 | public String url;
|
| 72 | - public Date startTime;
|
|
| 73 | - public int durationInMillis;
|
|
| 74 | + public TimePoint startTime;
|
|
| 75 | + public Duration duration;
|
|
| 74 | 76 | public MimeType mimeType;
|
| 75 | 77 | public Status status = Status.UNDEFINED;
|
| 76 | 78 | |
| 77 | 79 | public MediaTrack() {
|
| 78 | 80 | }
|
| 79 | 81 | |
| 80 | - public MediaTrack(String title, String url, Date startTime, int durationInMillis, MimeType mimeType) {
|
|
| 82 | + public MediaTrack(String title, String url, TimePoint startTime, Duration duration, MimeType mimeType) {
|
|
| 81 | 83 | this.title = title;
|
| 82 | 84 | this.url = url;
|
| 83 | 85 | this.startTime = startTime;
|
| 84 | - this.durationInMillis = durationInMillis;
|
|
| 86 | + this.duration = duration;
|
|
| 85 | 87 | this.mimeType = mimeType;
|
| 86 | 88 | }
|
| 87 | 89 | |
| 88 | - public MediaTrack(String dbId, String title, String url, Date startTime, int durationInMillis, MimeType mimeType) {
|
|
| 90 | + public MediaTrack(String dbId, String title, String url, TimePoint startTime, Duration duration, MimeType mimeType) {
|
|
| 89 | 91 | this.dbId = dbId;
|
| 90 | 92 | this.title = title;
|
| 91 | 93 | this.url = url;
|
| 92 | 94 | this.startTime = startTime;
|
| 93 | - this.durationInMillis = durationInMillis;
|
|
| 95 | + this.duration = duration;
|
|
| 94 | 96 | this.mimeType = mimeType;
|
| 95 | 97 | }
|
| 96 | 98 | |
| 97 | 99 | public String toString() {
|
| 98 | - return title + " - " + url + " [" + typeToString() + ']' + startTime + " [" + durationInMillis + status + ']';
|
|
| 100 | + return title + " - " + url + " [" + typeToString() + ']' + startTime + " [" + duration + status + ']';
|
|
| 99 | 101 | }
|
| 100 | 102 | |
| 101 | - public Date deriveEndTime() {
|
|
| 103 | + public TimePoint deriveEndTime() {
|
|
| 102 | 104 | if (startTime != null) {
|
| 103 | - return new Date(startTime.getTime() + durationInMillis);
|
|
| 105 | + return startTime.plus(duration);
|
|
| 104 | 106 | } else {
|
| 105 | 107 | return null;
|
| 106 | 108 | }
|
| ... | ... | @@ -121,11 +123,11 @@ public class MediaTrack implements Serializable { |
| 121 | 123 | * @param startTime Must not be null.
|
| 122 | 124 | * @param endTime May be null representing "open end".
|
| 123 | 125 | */
|
| 124 | - public boolean overlapsWith(Date startTime, Date endTime) {
|
|
| 126 | + public boolean overlapsWith(TimePoint startTime, TimePoint endTime) {
|
|
| 125 | 127 | if (this.startTime == null) {
|
| 126 | 128 | return false;
|
| 127 | 129 | } else {
|
| 128 | - return this.deriveEndTime().getTime() > startTime.getTime() && (endTime == null || this.startTime.getTime() < endTime.getTime());
|
|
| 130 | + return this.deriveEndTime().after(startTime) && (endTime == null || this.startTime.before(endTime));
|
|
| 129 | 131 | }
|
| 130 | 132 | }
|
| 131 | 133 |
java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaUtil.java
| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | 1 | package com.sap.sailing.domain.common.media; |
| 2 | 2 | |
| 3 | -import java.util.Date; |
|
| 3 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 4 | 4 | |
| 5 | 5 | public class MediaUtil { |
| 6 | 6 | |
| 7 | - public static int compareDatesAllowingNull(Date date1, Date date2) { |
|
| 7 | + public static int compareDatesAllowingNull(TimePoint date1, TimePoint date2) { |
|
| 8 | 8 | if (date1 == null) { |
| 9 | 9 | return date2 == null ? 0 : -1; |
| 10 | 10 | } else if (date2 == null) { |
| ... | ... | @@ -14,7 +14,7 @@ public class MediaUtil { |
| 14 | 14 | } |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | - public static boolean equalsDatesAllowingNull(Date date1, Date date2) { |
|
| 17 | + public static boolean equalsDatesAllowingNull(TimePoint date1, TimePoint date2) { |
|
| 18 | 18 | if (date1 == null) { |
| 19 | 19 | return date2 == null; |
| 20 | 20 | } else if (date2 == null) { |
java/com.sap.sailing.domain.igtimiadapter/src/com/sap/sailing/domain/igtimiadapter/shared/IgtimiWindReceiver.java
| ... | ... | @@ -220,9 +220,13 @@ public class IgtimiWindReceiver implements BulkFixReceiver { |
| 220 | 220 | Speed sog = getSOG(timePoint, sogPair); |
| 221 | 221 | com.sap.sse.common.Util.Pair<COG, COG> cogPair = getSurroundingFixes(getCogTrack(deviceSerialNumber), timePoint); |
| 222 | 222 | Bearing cog = getCOG(timePoint, cogPair); |
| 223 | - SpeedWithBearing sogCog = new KnotSpeedWithBearingImpl(sog.getKnots(), cog); |
|
| 224 | - SpeedWithBearing trueWindSpeedAndDirection = apparentWindSpeedWithDirection.add(sogCog); |
|
| 225 | - result = new WindImpl(pos, timePoint, trueWindSpeedAndDirection); |
|
| 223 | + if (sog != null && cog != null) { |
|
| 224 | + SpeedWithBearing sogCog = new KnotSpeedWithBearingImpl(sog.getKnots(), cog); |
|
| 225 | + SpeedWithBearing trueWindSpeedAndDirection = apparentWindSpeedWithDirection.add(sogCog); |
|
| 226 | + result = new WindImpl(pos, timePoint, trueWindSpeedAndDirection); |
|
| 227 | + } else { |
|
| 228 | + result = null; |
|
| 229 | + } |
|
| 226 | 230 | } else { |
| 227 | 231 | result = null; |
| 228 | 232 | } |
java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/DBMediaTrack.java
| ... | ... | @@ -1,23 +1,25 @@ |
| 1 | 1 | package com.sap.sailing.domain.persistence.media;
|
| 2 | 2 | |
| 3 | -import java.util.Date;
|
|
| 3 | +import com.sap.sailing.domain.common.Duration;
|
|
| 4 | +import com.sap.sailing.domain.common.TimePoint;
|
|
| 4 | 5 | |
| 5 | 6 | public class DBMediaTrack {
|
| 6 | 7 | |
| 7 | 8 | public final String dbId;
|
| 8 | 9 | public final String title;
|
| 9 | 10 | public final String url;
|
| 10 | - public final Date startTime;
|
|
| 11 | - public final int durationInMillis;
|
|
| 11 | + public final TimePoint startTime;
|
|
| 12 | + public final Duration duration;
|
|
| 12 | 13 | public final String mimeType;
|
| 13 | 14 | |
| 14 | - public DBMediaTrack(String dbId, String title, String url, Date startTime, int duration, String mimeType) {
|
|
| 15 | + public DBMediaTrack(String dbId, String title, String url, TimePoint startTime, Duration duration, String mimeType) {
|
|
| 15 | 16 | super();
|
| 16 | 17 | this.dbId = dbId;
|
| 17 | 18 | this.title = title;
|
| 18 | 19 | this.url = url;
|
| 19 | 20 | this.startTime = startTime;
|
| 20 | - this.durationInMillis = duration;
|
|
| 21 | + this.duration = duration;
|
|
| 21 | 22 | this.mimeType = mimeType;
|
| 22 | 23 | }
|
| 24 | +
|
|
| 23 | 25 | }
|
java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java
| ... | ... | @@ -1,9 +1,11 @@ |
| 1 | 1 | package com.sap.sailing.domain.persistence.media;
|
| 2 | 2 | |
| 3 | 3 | import java.util.Collections;
|
| 4 | -import java.util.Date;
|
|
| 5 | 4 | import java.util.List;
|
| 6 | 5 | |
| 6 | +import com.sap.sailing.domain.common.Duration;
|
|
| 7 | +import com.sap.sailing.domain.common.TimePoint;
|
|
| 8 | +
|
|
| 7 | 9 | /**
|
| 8 | 10 | * Offers CRUD methods for mongo representation of media track objects.
|
| 9 | 11 | *
|
| ... | ... | @@ -15,7 +17,7 @@ public interface MediaDB { |
| 15 | 17 | /**
|
| 16 | 18 | * Stores a new track to the database, returning the db-generated id.
|
| 17 | 19 | */
|
| 18 | - String insertMediaTrack(String title, String url, Date startTime, int durationInMillis, String mimeType);
|
|
| 20 | + String insertMediaTrack(String title, String url, TimePoint startTime, Duration duration, String mimeType);
|
|
| 19 | 21 | |
| 20 | 22 | /**
|
| 21 | 23 | * Stores a new track to the database, using the db id of the specified trackToImport.
|
| ... | ... | @@ -23,7 +25,7 @@ public interface MediaDB { |
| 23 | 25 | * @throws NullpointerException When trackToImport.dbId is null.
|
| 24 | 26 | * @throws IllegalArgumentException When track with specified dbId already exists.
|
| 25 | 27 | */
|
| 26 | - void insertMediaTrackWithId(String dbId, String videoTitle, String url, Date startTime, int durationInMillis, String mimeType);
|
|
| 28 | + void insertMediaTrackWithId(String dbId, String videoTitle, String url, TimePoint startTime, Duration duration, String mimeType);
|
|
| 27 | 29 | |
| 28 | 30 | List<DBMediaTrack> loadAllMediaTracks();
|
| 29 | 31 | |
| ... | ... | @@ -33,19 +35,19 @@ public interface MediaDB { |
| 33 | 35 | |
| 34 | 36 | void updateUrl(String dbId, String url);
|
| 35 | 37 | |
| 36 | - void updateStartTime(String dbId, Date startTime);
|
|
| 38 | + void updateStartTime(String dbId, TimePoint startTime);
|
|
| 37 | 39 | |
| 38 | - void updateDuration(String dbId, int durationInMillis);
|
|
| 40 | + void updateDuration(String dbId, Duration duration);
|
|
| 39 | 41 | |
| 40 | 42 | MediaDB TEST_STUB = new MediaDB() {
|
| 41 | 43 | |
| 42 | 44 | @Override
|
| 43 | - public String insertMediaTrack(String title, String url, Date startTime, int durationInMillis, String mimeType) {
|
|
| 45 | + public String insertMediaTrack(String title, String url, TimePoint startTime, Duration duration, String mimeType) {
|
|
| 44 | 46 | return "0";
|
| 45 | 47 | }
|
| 46 | 48 | |
| 47 | 49 | @Override
|
| 48 | - public void insertMediaTrackWithId(String dbId, String videoTitle, String url, Date startTime, int durationInMillis, String mimeType) {};
|
|
| 50 | + public void insertMediaTrackWithId(String dbId, String videoTitle, String url, TimePoint startTime, Duration duration, String mimeType) {};
|
|
| 49 | 51 | |
| 50 | 52 | @Override
|
| 51 | 53 | public List<DBMediaTrack> loadAllMediaTracks() {
|
| ... | ... | @@ -62,10 +64,10 @@ public interface MediaDB { |
| 62 | 64 | public void updateUrl(String dbId, String url) {}
|
| 63 | 65 | |
| 64 | 66 | @Override
|
| 65 | - public void updateStartTime(String dbId, Date startTime) {}
|
|
| 67 | + public void updateStartTime(String dbId, TimePoint startTime) {}
|
|
| 66 | 68 | |
| 67 | 69 | @Override
|
| 68 | - public void updateDuration(String dbId, int durationInMillis) {}
|
|
| 70 | + public void updateDuration(String dbId, Duration duration) {}
|
|
| 69 | 71 | |
| 70 | 72 | };
|
| 71 | 73 |
java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java
| ... | ... | @@ -13,6 +13,10 @@ import com.mongodb.DBCursor; |
| 13 | 13 | import com.mongodb.DBObject;
|
| 14 | 14 | import com.mongodb.MongoException;
|
| 15 | 15 | import com.mongodb.WriteConcern;
|
| 16 | +import com.sap.sailing.domain.common.Duration;
|
|
| 17 | +import com.sap.sailing.domain.common.TimePoint;
|
|
| 18 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl;
|
|
| 19 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
|
| 16 | 20 | import com.sap.sailing.domain.persistence.media.DBMediaTrack;
|
| 17 | 21 | import com.sap.sailing.domain.persistence.media.MediaDB;
|
| 18 | 22 | |
| ... | ... | @@ -39,12 +43,12 @@ public class MediaDBImpl implements MediaDB { |
| 39 | 43 | }
|
| 40 | 44 | |
| 41 | 45 | @Override
|
| 42 | - public String insertMediaTrack(String title, String url, Date startTime, int durationInMillis, String mimeType) {
|
|
| 46 | + public String insertMediaTrack(String title, String url, TimePoint startTime, Duration duration, String mimeType) {
|
|
| 43 | 47 | BasicDBObject dbMediaTrack = new BasicDBObject();
|
| 44 | 48 | dbMediaTrack.put(DbNames.Fields.MEDIA_TITLE.name(), title);
|
| 45 | 49 | dbMediaTrack.put(DbNames.Fields.MEDIA_URL.name(), url);
|
| 46 | - dbMediaTrack.put(DbNames.Fields.STARTTIME.name(), startTime);
|
|
| 47 | - dbMediaTrack.put(DbNames.Fields.DURATION_IN_MILLIS.name(), durationInMillis);
|
|
| 50 | + dbMediaTrack.put(DbNames.Fields.STARTTIME.name(), startTime == null ? null : startTime.asDate());
|
|
| 51 | + dbMediaTrack.put(DbNames.Fields.DURATION_IN_MILLIS.name(), duration == null ? null : duration.asMillis());
|
|
| 48 | 52 | dbMediaTrack.put(DbNames.Fields.MIME_TYPE.name(), mimeType);
|
| 49 | 53 | DBCollection dbVideos = getVideoCollection();
|
| 50 | 54 | dbVideos.insert(dbMediaTrack);
|
| ... | ... | @@ -52,13 +56,13 @@ public class MediaDBImpl implements MediaDB { |
| 52 | 56 | }
|
| 53 | 57 | |
| 54 | 58 | @Override
|
| 55 | - public void insertMediaTrackWithId(String dbId, String title, String url, Date startTime, int durationInMillis, String mimeType) {
|
|
| 59 | + public void insertMediaTrackWithId(String dbId, String title, String url, TimePoint startTime, Duration duration, String mimeType) {
|
|
| 56 | 60 | BasicDBObject dbMediaTrack = new BasicDBObject();
|
| 57 | 61 | dbMediaTrack.put(DbNames.Fields._id.name(), new ObjectId(dbId));
|
| 58 | 62 | dbMediaTrack.put(DbNames.Fields.MEDIA_TITLE.name(), title);
|
| 59 | 63 | dbMediaTrack.put(DbNames.Fields.MEDIA_URL.name(), url);
|
| 60 | - dbMediaTrack.put(DbNames.Fields.STARTTIME.name(), startTime);
|
|
| 61 | - dbMediaTrack.put(DbNames.Fields.DURATION_IN_MILLIS.name(), durationInMillis);
|
|
| 64 | + dbMediaTrack.put(DbNames.Fields.STARTTIME.name(), startTime == null ? null : startTime.asDate());
|
|
| 65 | + dbMediaTrack.put(DbNames.Fields.DURATION_IN_MILLIS.name(), duration == null ? null : duration.asMillis());
|
|
| 62 | 66 | dbMediaTrack.put(DbNames.Fields.MIME_TYPE.name(), mimeType);
|
| 63 | 67 | DBCollection dbVideos = getVideoCollection();
|
| 64 | 68 | try {
|
| ... | ... | @@ -85,9 +89,12 @@ public class MediaDBImpl implements MediaDB { |
| 85 | 89 | String title = (String) dbObject.get(DbNames.Fields.MEDIA_TITLE.name());
|
| 86 | 90 | String url = (String) dbObject.get(DbNames.Fields.MEDIA_URL.name());
|
| 87 | 91 | Date startTime = (Date) dbObject.get(DbNames.Fields.STARTTIME.name());
|
| 88 | - Integer durationInMillis = (Integer) dbObject.get(DbNames.Fields.DURATION_IN_MILLIS.name());
|
|
| 92 | + Long duration = (Long) dbObject.get(DbNames.Fields.DURATION_IN_MILLIS.name());
|
|
| 89 | 93 | String mimeType = (String) dbObject.get(DbNames.Fields.MIME_TYPE.name());
|
| 90 | - DBMediaTrack dbMediaTrack = new DBMediaTrack(dbId, title, url, startTime, durationInMillis == null ? 0 : durationInMillis, mimeType);
|
|
| 94 | + DBMediaTrack dbMediaTrack = new DBMediaTrack(dbId, title, url,
|
|
| 95 | + startTime == null ? null : new MillisecondsTimePoint(startTime),
|
|
| 96 | + duration == null ? null : new MillisecondsDurationImpl(duration),
|
|
| 97 | + mimeType);
|
|
| 91 | 98 | return dbMediaTrack;
|
| 92 | 99 | }
|
| 93 | 100 | |
| ... | ... | @@ -131,23 +138,23 @@ public class MediaDBImpl implements MediaDB { |
| 131 | 138 | }
|
| 132 | 139 | |
| 133 | 140 | @Override
|
| 134 | - public void updateStartTime(String dbId, Date startTime) {
|
|
| 141 | + public void updateStartTime(String dbId, TimePoint startTime) {
|
|
| 135 | 142 | BasicDBObject updateQuery = new BasicDBObject();
|
| 136 | 143 | updateQuery.append(DbNames.Fields._id.name(), new ObjectId(dbId));
|
| 137 | 144 | |
| 138 | 145 | BasicDBObject updateCommand = new BasicDBObject();
|
| 139 | - updateCommand.append("$set", new BasicDBObject(DbNames.Fields.STARTTIME.name(), startTime));
|
|
| 146 | + updateCommand.append("$set", new BasicDBObject(DbNames.Fields.STARTTIME.name(), startTime == null ? null : startTime.asDate()));
|
|
| 140 | 147 | |
| 141 | 148 | getVideoCollection().update(updateQuery, updateCommand);
|
| 142 | 149 | }
|
| 143 | 150 | |
| 144 | 151 | @Override
|
| 145 | - public void updateDuration(String dbId, int durationInMillis) {
|
|
| 152 | + public void updateDuration(String dbId, Duration duration) {
|
|
| 146 | 153 | BasicDBObject updateQuery = new BasicDBObject();
|
| 147 | 154 | updateQuery.append(DbNames.Fields._id.name(), new ObjectId(dbId));
|
| 148 | 155 | |
| 149 | 156 | BasicDBObject updateCommand = new BasicDBObject();
|
| 150 | - updateCommand.append("$set", new BasicDBObject(DbNames.Fields.DURATION_IN_MILLIS.name(), durationInMillis));
|
|
| 157 | + updateCommand.append("$set", new BasicDBObject(DbNames.Fields.DURATION_IN_MILLIS.name(), duration == null ? null : duration.asMillis()));
|
|
| 151 | 158 | |
| 152 | 159 | getVideoCollection().update(updateQuery, updateCommand);
|
| 153 | 160 | }
|
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/EventBase.java
| ... | ... | @@ -1,7 +1,9 @@ |
| 1 | 1 | package com.sap.sailing.domain.base;
|
| 2 | 2 | |
| 3 | 3 | import java.net.URL;
|
| 4 | +import java.util.concurrent.ExecutionException;
|
|
| 4 | 5 | |
| 6 | +import com.sap.sailing.domain.common.ImageSize;
|
|
| 5 | 7 | import com.sap.sailing.domain.common.Renamable;
|
| 6 | 8 | import com.sap.sailing.domain.common.TimePoint;
|
| 7 | 9 | import com.sap.sailing.domain.common.WithID;
|
| ... | ... | @@ -61,6 +63,14 @@ public interface EventBase extends Named, WithDescription, Renamable, WithID { |
| 61 | 63 | |
| 62 | 64 | void removeSponsorImageURL(URL sponsorImageURL);
|
| 63 | 65 | |
| 66 | + /**
|
|
| 67 | + * Replaces the {@link #getSponsorImageURLs() current contents of the sponsorship image URL sequence} by the image URLs in
|
|
| 68 | + * <code>sponsorImageURLs</code>.
|
|
| 69 | + *
|
|
| 70 | + * @param sponsorImageURLs
|
|
| 71 | + * if <code>null</code>, the internal sequence of sponsorship image URLs is cleared but remains valid (non-
|
|
| 72 | + * <code>null</code>)
|
|
| 73 | + */
|
|
| 64 | 74 | void setSponsorImageURLs(Iterable<URL> sponsorImageURLs);
|
| 65 | 75 | |
| 66 | 76 | /**
|
| ... | ... | @@ -111,4 +121,10 @@ public interface EventBase extends Named, WithDescription, Renamable, WithID { |
| 111 | 121 | void setOfficialWebsiteURL(URL officialWebsiteURL);
|
| 112 | 122 | |
| 113 | 123 | Iterable<? extends LeaderboardGroupBase> getLeaderboardGroups();
|
| 124 | +
|
|
| 125 | + /**
|
|
| 126 | + * For the images references by the image URLs in {@link #getImageURLs()}, {@link #getSponsorImageURLs()} and {@link #getLogoImageURL()}
|
|
| 127 | + * determines the image dimensions.
|
|
| 128 | + */
|
|
| 129 | + ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException;
|
|
| 114 | 130 | }
|
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/EventBaseImpl.java
| ... | ... | @@ -146,7 +146,9 @@ public abstract class EventBaseImpl implements EventBase { |
| 146 | 146 | @Override
|
| 147 | 147 | public void setImageURLs(Iterable<URL> imageURLs) {
|
| 148 | 148 | this.imageURLs.clear();
|
| 149 | - Util.addAll(imageURLs, this.imageURLs);
|
|
| 149 | + if (imageURLs != null) {
|
|
| 150 | + Util.addAll(imageURLs, this.imageURLs);
|
|
| 151 | + }
|
|
| 150 | 152 | }
|
| 151 | 153 | |
| 152 | 154 | @Override
|
| ... | ... | @@ -169,7 +171,9 @@ public abstract class EventBaseImpl implements EventBase { |
| 169 | 171 | @Override
|
| 170 | 172 | public void setVideoURLs(Iterable<URL> videoURLs) {
|
| 171 | 173 | this.videoURLs.clear();
|
| 172 | - Util.addAll(videoURLs, this.videoURLs);
|
|
| 174 | + if (videoURLs != null) {
|
|
| 175 | + Util.addAll(videoURLs, this.videoURLs);
|
|
| 176 | + }
|
|
| 173 | 177 | }
|
| 174 | 178 | |
| 175 | 179 | @Override
|
| ... | ... | @@ -192,7 +196,9 @@ public abstract class EventBaseImpl implements EventBase { |
| 192 | 196 | @Override
|
| 193 | 197 | public void setSponsorImageURLs(Iterable<URL> sponsorImageURLs) {
|
| 194 | 198 | this.sponsorImageURLs.clear();
|
| 195 | - Util.addAll(sponsorImageURLs, this.sponsorImageURLs);
|
|
| 199 | + if (sponsorImageURLs != null) {
|
|
| 200 | + Util.addAll(sponsorImageURLs, this.sponsorImageURLs);
|
|
| 201 | + }
|
|
| 196 | 202 | }
|
| 197 | 203 | |
| 198 | 204 | @Override
|
| ... | ... | @@ -214,4 +220,5 @@ public abstract class EventBaseImpl implements EventBase { |
| 214 | 220 | public void setOfficialWebsiteURL(URL officialWebsiteURL) {
|
| 215 | 221 | this.officialWebsiteURL = officialWebsiteURL;
|
| 216 | 222 | }
|
| 223 | +
|
|
| 217 | 224 | }
|
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/StrippedEventImpl.java
| ... | ... | @@ -1,15 +1,21 @@ |
| 1 | 1 | package com.sap.sailing.domain.base.impl; |
| 2 | 2 | |
| 3 | +import java.net.URL; |
|
| 4 | +import java.util.HashMap; |
|
| 5 | +import java.util.Map; |
|
| 3 | 6 | import java.util.UUID; |
| 7 | +import java.util.concurrent.ExecutionException; |
|
| 4 | 8 | |
| 5 | 9 | import com.sap.sailing.domain.base.EventBase; |
| 6 | 10 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 7 | 11 | import com.sap.sailing.domain.base.Venue; |
| 12 | +import com.sap.sailing.domain.common.ImageSize; |
|
| 8 | 13 | import com.sap.sailing.domain.common.TimePoint; |
| 9 | 14 | |
| 10 | 15 | /** |
| 11 | 16 | * A simplified implementation of the {@link EventBase} interface which maintains an immutable collection of |
| 12 | - * {@link LeaderboardGroupBase} objects to implement the {@link #getLeaderboardGroups()} method. |
|
| 17 | + * {@link LeaderboardGroupBase} objects to implement the {@link #getLeaderboardGroups()} method. A local image |
|
| 18 | + * size cache can be maintained using the {@link #setImageSize} method. |
|
| 13 | 19 | * |
| 14 | 20 | * @author Axel Uhl (D043530) |
| 15 | 21 | * |
| ... | ... | @@ -17,21 +23,31 @@ import com.sap.sailing.domain.common.TimePoint; |
| 17 | 23 | public class StrippedEventImpl extends EventBaseImpl { |
| 18 | 24 | private static final long serialVersionUID = 5608501747499933988L; |
| 19 | 25 | private final Iterable<LeaderboardGroupBase> leaderboardGroups; |
| 26 | + private final Map<URL, ImageSize> imageSizes; |
|
| 20 | 27 | |
| 21 | 28 | public StrippedEventImpl(String name, TimePoint startDate, TimePoint endDate, String venueName, |
| 22 | 29 | boolean isPublic, UUID id, Iterable<LeaderboardGroupBase> leaderboardGroups) { |
| 23 | - super(name, startDate, endDate, venueName, isPublic, id); |
|
| 24 | - this.leaderboardGroups = leaderboardGroups; |
|
| 30 | + this(name, startDate, endDate, new VenueImpl(venueName), isPublic, id, leaderboardGroups); |
|
| 25 | 31 | } |
| 26 | 32 | |
| 27 | 33 | public StrippedEventImpl(String name, TimePoint startDate, TimePoint endDate, Venue venue, |
| 28 | 34 | boolean isPublic, UUID id, Iterable<LeaderboardGroupBase> leaderboardGroups) { |
| 29 | 35 | super(name, startDate, endDate, venue, isPublic, id); |
| 30 | 36 | this.leaderboardGroups = leaderboardGroups; |
| 37 | + this.imageSizes = new HashMap<URL, ImageSize>(); |
|
| 31 | 38 | } |
| 32 | 39 | |
| 33 | 40 | @Override |
| 34 | 41 | public Iterable<LeaderboardGroupBase> getLeaderboardGroups() { |
| 35 | 42 | return leaderboardGroups; |
| 36 | 43 | } |
| 44 | + |
|
| 45 | + public void setImageSize(URL imageURL, ImageSize imageSize) { |
|
| 46 | + imageSizes.put(imageURL, imageSize); |
|
| 47 | + } |
|
| 48 | + |
|
| 49 | + @Override |
|
| 50 | + public ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException { |
|
| 51 | + return imageSizes.get(imageURL); |
|
| 52 | + } |
|
| 37 | 53 | } |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/util/impl/ConcurrentHashBag.java
| ... | ... | @@ -0,0 +1,92 @@ |
| 1 | +package com.sap.sailing.util.impl; |
|
| 2 | + |
|
| 3 | +import java.util.AbstractCollection; |
|
| 4 | +import java.util.Map.Entry; |
|
| 5 | +import java.util.concurrent.ConcurrentHashMap; |
|
| 6 | + |
|
| 7 | +/** |
|
| 8 | + * An "almost" thread-safe bag implementation. The constraint: when for equal objects an add or remove is performed, |
|
| 9 | + * there should be no concurrency regarding that key. The implementation uses a {@link ConcurrentHashMap} internally. |
|
| 10 | + * |
|
| 11 | + * @author Axel Uhl (D043530) |
|
| 12 | + */ |
|
| 13 | +public class ConcurrentHashBag<T> extends AbstractCollection<T> { |
|
| 14 | + private ConcurrentHashMap<T, Integer> map = new ConcurrentHashMap<T, Integer>(); |
|
| 15 | + private int size; |
|
| 16 | + |
|
| 17 | + @Override |
|
| 18 | + public boolean contains(Object o) { |
|
| 19 | + return map.containsKey(o); |
|
| 20 | + } |
|
| 21 | + |
|
| 22 | + @Override |
|
| 23 | + public boolean remove(Object o) { |
|
| 24 | + @SuppressWarnings("unchecked") |
|
| 25 | + T t = (T) o; |
|
| 26 | + Integer oldCount = map.remove(t); |
|
| 27 | + if (oldCount != null && oldCount != 1) { |
|
| 28 | + map.put(t, oldCount - 1); |
|
| 29 | + } |
|
| 30 | + if (oldCount != null) { |
|
| 31 | + size--; |
|
| 32 | + } |
|
| 33 | + return oldCount != null; |
|
| 34 | + } |
|
| 35 | + |
|
| 36 | + @Override |
|
| 37 | + public boolean add(T e) { |
|
| 38 | + Integer oldCount = map.put(e, 1); |
|
| 39 | + if (oldCount != null && oldCount != 0) { |
|
| 40 | + map.put(e, oldCount + 1); |
|
| 41 | + } |
|
| 42 | + size++; |
|
| 43 | + return true; |
|
| 44 | + } |
|
| 45 | + |
|
| 46 | + @Override |
|
| 47 | + public java.util.Iterator<T> iterator() { |
|
| 48 | + return new Iterator(); |
|
| 49 | + } |
|
| 50 | + |
|
| 51 | + @Override |
|
| 52 | + public int size() { |
|
| 53 | + return size; |
|
| 54 | + } |
|
| 55 | + |
|
| 56 | + @Override |
|
| 57 | + public boolean isEmpty() { |
|
| 58 | + return map.isEmpty(); |
|
| 59 | + } |
|
| 60 | + |
|
| 61 | + private class Iterator implements java.util.Iterator<T> { |
|
| 62 | + private final java.util.Iterator<Entry<T, Integer>> iter = map.entrySet().iterator(); |
|
| 63 | + private int howManyMore = 0; |
|
| 64 | + private T lastElementOfWhichWeHaveMore; |
|
| 65 | + |
|
| 66 | + @Override |
|
| 67 | + public boolean hasNext() { |
|
| 68 | + return howManyMore > 0 || iter.hasNext(); |
|
| 69 | + } |
|
| 70 | + |
|
| 71 | + @Override |
|
| 72 | + public T next() { |
|
| 73 | + final T result; |
|
| 74 | + if (howManyMore > 0) { |
|
| 75 | + result = lastElementOfWhichWeHaveMore; |
|
| 76 | + howManyMore--; |
|
| 77 | + } else { |
|
| 78 | + Entry<T, Integer> next = iter.next(); |
|
| 79 | + howManyMore = next.getValue()-1; |
|
| 80 | + lastElementOfWhichWeHaveMore = next.getKey(); |
|
| 81 | + result = next.getKey(); |
|
| 82 | + } |
|
| 83 | + return result; |
|
| 84 | + } |
|
| 85 | + |
|
| 86 | + @Override |
|
| 87 | + public void remove() { |
|
| 88 | + // TODO Auto-generated method stub |
|
| 89 | + |
|
| 90 | + } |
|
| 91 | + } |
|
| 92 | +} |
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/util/impl/LockUtil.java
| ... | ... | @@ -2,7 +2,6 @@ package com.sap.sailing.util.impl; |
| 2 | 2 | |
| 3 | 3 | import java.util.HashMap;
|
| 4 | 4 | import java.util.HashSet;
|
| 5 | -import java.util.List;
|
|
| 6 | 5 | import java.util.Map;
|
| 7 | 6 | import java.util.Set;
|
| 8 | 7 | import java.util.concurrent.ConcurrentHashMap;
|
| ... | ... | @@ -381,7 +380,7 @@ public class LockUtil { |
| 381 | 380 | // capture the stack traces as quickly as possible to try to reflect the situation as it was when the lock couuldn't be obtained
|
| 382 | 381 | StackTraceElement[] writerStackTrace = writer != null ? writer.getStackTrace() : null;
|
| 383 | 382 | Map<Thread, StackTraceElement[]> readerStackTraces = new HashMap<Thread, StackTraceElement[]>();
|
| 384 | - final List<Thread> readers = lockParent.getReaders();
|
|
| 383 | + final Iterable<Thread> readers = lockParent.getReaders();
|
|
| 385 | 384 | for (Thread reader : readers) {
|
| 386 | 385 | readerStackTraces.put(reader, reader.getStackTrace());
|
| 387 | 386 | }
|
| ... | ... | @@ -396,7 +395,7 @@ public class LockUtil { |
| 396 | 395 | message.append("\nThe current writer is:\n");
|
| 397 | 396 | appendThreadData(message, writer, writerStackTrace);
|
| 398 | 397 | }
|
| 399 | - if (readers != null && !readers.isEmpty()) {
|
|
| 398 | + if (readers != null && !Util.isEmpty(readers)) {
|
|
| 400 | 399 | message.append("\nThe current readers are:\n");
|
| 401 | 400 | for (Thread reader : readers) {
|
| 402 | 401 | appendThreadData(message, reader, readerStackTraces.get(reader));
|
java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/util/impl/NamedReentrantReadWriteLock.java
| ... | ... | @@ -3,8 +3,6 @@ package com.sap.sailing.util.impl; |
| 3 | 3 | import java.io.IOException;
|
| 4 | 4 | import java.io.ObjectInputStream;
|
| 5 | 5 | import java.util.ArrayList;
|
| 6 | -import java.util.Collections;
|
|
| 7 | -import java.util.List;
|
|
| 8 | 6 | import java.util.concurrent.TimeUnit;
|
| 9 | 7 | import java.util.concurrent.locks.Condition;
|
| 10 | 8 | import java.util.concurrent.locks.ReentrantReadWriteLock;
|
| ... | ... | @@ -35,7 +33,7 @@ public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implemen |
| 35 | 33 | private final String writeLockName;
|
| 36 | 34 | private final WriteLockWrapper writeLockWrapper;
|
| 37 | 35 | private final ReadLockWrapper readLockWrapper;
|
| 38 | - private transient List<Thread> readers;
|
|
| 36 | + private transient ConcurrentHashBag<Thread> readers;
|
|
| 39 | 37 | |
| 40 | 38 | private class WriteLockWrapper extends WriteLock {
|
| 41 | 39 | private static final long serialVersionUID = -4234819025137348944L;
|
| ... | ... | @@ -110,7 +108,8 @@ public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implemen |
| 110 | 108 | public void lockInterruptibly() throws InterruptedException {
|
| 111 | 109 | try {
|
| 112 | 110 | readLock.lockInterruptibly();
|
| 113 | - readers.add(Thread.currentThread());
|
|
| 111 | + final Thread currentThread = Thread.currentThread();
|
|
| 112 | + readers.add(currentThread);
|
|
| 114 | 113 | } catch (InterruptedException ie) {
|
| 115 | 114 | throw ie;
|
| 116 | 115 | }
|
| ... | ... | @@ -120,7 +119,8 @@ public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implemen |
| 120 | 119 | public boolean tryLock() {
|
| 121 | 120 | boolean result = readLock.tryLock();
|
| 122 | 121 | if (result) {
|
| 123 | - readers.add(Thread.currentThread());
|
|
| 122 | + final Thread currentThread = Thread.currentThread();
|
|
| 123 | + readers.add(currentThread);
|
|
| 124 | 124 | }
|
| 125 | 125 | return result;
|
| 126 | 126 | }
|
| ... | ... | @@ -129,7 +129,8 @@ public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implemen |
| 129 | 129 | public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
|
| 130 | 130 | boolean result = readLock.tryLock(timeout, unit);
|
| 131 | 131 | if (result) {
|
| 132 | - readers.add(Thread.currentThread());
|
|
| 132 | + final Thread currentThread = Thread.currentThread();
|
|
| 133 | + readers.add(currentThread);
|
|
| 133 | 134 | }
|
| 134 | 135 | return result;
|
| 135 | 136 | }
|
| ... | ... | @@ -159,12 +160,12 @@ public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implemen |
| 159 | 160 | this.writeLockName = "writeLock "+name;
|
| 160 | 161 | this.writeLockWrapper = new WriteLockWrapper(super.writeLock());
|
| 161 | 162 | this.readLockWrapper = new ReadLockWrapper(super.readLock());
|
| 162 | - this.readers = Collections.synchronizedList(new ArrayList<Thread>());
|
|
| 163 | + this.readers = new ConcurrentHashBag<Thread>();
|
|
| 163 | 164 | }
|
| 164 | 165 | |
| 165 | 166 | private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
| 166 | 167 | ois.defaultReadObject();
|
| 167 | - this.readers = Collections.synchronizedList(new ArrayList<Thread>());
|
|
| 168 | + this.readers = new ConcurrentHashBag<Thread>();
|
|
| 168 | 169 | }
|
| 169 | 170 | |
| 170 | 171 | @Override
|
| ... | ... | @@ -184,9 +185,9 @@ public class NamedReentrantReadWriteLock extends ReentrantReadWriteLock implemen |
| 184 | 185 | |
| 185 | 186 | /**
|
| 186 | 187 | * Contains the threads currently holding a read lock. Each thread is contained as many times as it
|
| 187 | - * successfully acquired the read lock re-entrantly.
|
|
| 188 | + * successfully acquired the read lock re-entrantly. The result is a snapshot that is not live.
|
|
| 188 | 189 | */
|
| 189 | - public List<Thread> getReaders() {
|
|
| 190 | + public Iterable<Thread> getReaders() {
|
|
| 190 | 191 | return new ArrayList<Thread>(readers);
|
| 191 | 192 | }
|
| 192 | 193 |
java/com.sap.sailing.domain.swisstimingadapter/src/com/sap/sailing/domain/swisstimingadapter/impl/SailMasterConnectorImpl.java
| ... | ... | @@ -27,6 +27,7 @@ import java.util.logging.Level; |
| 27 | 27 | import java.util.logging.Logger;
|
| 28 | 28 | |
| 29 | 29 | import com.sap.sailing.domain.base.BoatClass;
|
| 30 | +import com.sap.sailing.domain.common.AbstractBearing;
|
|
| 30 | 31 | import com.sap.sailing.domain.common.Distance;
|
| 31 | 32 | import com.sap.sailing.domain.common.Position;
|
| 32 | 33 | import com.sap.sailing.domain.common.Speed;
|
| ... | ... | @@ -388,7 +389,7 @@ public class SailMasterConnectorImpl extends SailMasterTransceiverImpl implement |
| 388 | 389 | final Speed velocityMadeGood = fixSections[vmgIndex].trim().length() == 0 ? null : new KnotSpeedImpl(
|
| 389 | 390 | Double.valueOf(fixSections[vmgIndex]));
|
| 390 | 391 | fixDetailIndex += 2;
|
| 391 | - final DegreeBearingImpl cog = new DegreeBearingImpl(
|
|
| 392 | + final AbstractBearing cog = new DegreeBearingImpl(
|
|
| 392 | 393 | Double.valueOf(fixSections[fixDetailIndex++]));
|
| 393 | 394 | final SpeedWithBearing speed = new KnotSpeedWithBearingImpl(speedOverGroundInKnots, cog);
|
| 394 | 395 | final Integer nextMarkIndex = fixSections.length <= fixDetailIndex
|
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/MediaTrackTest.java
| ... | ... | @@ -1,24 +1,25 @@ |
| 1 | 1 | package com.sap.sailing.domain.common; |
| 2 | 2 | |
| 3 | -import static org.junit.Assert.assertFalse; |
|
| 4 | -import static org.junit.Assert.assertTrue; |
|
| 5 | - |
|
| 6 | -import java.util.Date; |
|
| 7 | - |
|
| 8 | 3 | import org.junit.Test; |
| 9 | 4 | |
| 5 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 6 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 10 | 7 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 11 | 8 | |
| 9 | +import static org.junit.Assert.*; |
|
| 10 | + |
|
| 12 | 11 | public class MediaTrackTest { |
| 13 | 12 | |
| 13 | + private static final Duration ONE_MILLISECOND = new MillisecondsDurationImpl(1); |
|
| 14 | + |
|
| 14 | 15 | @Test |
| 15 | 16 | public void testExactOverlap() throws Exception { |
| 16 | 17 | MediaTrack mediaTrack = new MediaTrack(); |
| 17 | - mediaTrack.startTime = new Date(); |
|
| 18 | - mediaTrack.durationInMillis = 1; |
|
| 18 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 19 | + mediaTrack.duration = ONE_MILLISECOND; |
|
| 19 | 20 | |
| 20 | - Date startTime = new Date(mediaTrack.startTime.getTime()); |
|
| 21 | - Date endTime = new Date(mediaTrack.deriveEndTime().getTime()); |
|
| 21 | + TimePoint startTime = mediaTrack.startTime; |
|
| 22 | + TimePoint endTime = mediaTrack.deriveEndTime(); |
|
| 22 | 23 | assertTrue(mediaTrack.overlapsWith(startTime, endTime)); |
| 23 | 24 | |
| 24 | 25 | } |
| ... | ... | @@ -26,11 +27,11 @@ public class MediaTrackTest { |
| 26 | 27 | @Test |
| 27 | 28 | public void testNoOverlapLeft() throws Exception { |
| 28 | 29 | MediaTrack mediaTrack = new MediaTrack(); |
| 29 | - mediaTrack.startTime = new Date(); |
|
| 30 | - mediaTrack.durationInMillis = 1; |
|
| 30 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 31 | + mediaTrack.duration = ONE_MILLISECOND; |
|
| 31 | 32 | |
| 32 | - Date startTime = new Date(mediaTrack.startTime.getTime() + 2); |
|
| 33 | - Date endTime = new Date(startTime.getTime() + 1); |
|
| 33 | + TimePoint startTime = mediaTrack.startTime.plus(2); |
|
| 34 | + TimePoint endTime = startTime.plus(1); |
|
| 34 | 35 | assertFalse(mediaTrack.overlapsWith(startTime, endTime)); |
| 35 | 36 | |
| 36 | 37 | } |
| ... | ... | @@ -38,11 +39,11 @@ public class MediaTrackTest { |
| 38 | 39 | @Test |
| 39 | 40 | public void testNoOverlapRight() throws Exception { |
| 40 | 41 | MediaTrack mediaTrack = new MediaTrack(); |
| 41 | - mediaTrack.startTime = new Date(); |
|
| 42 | - mediaTrack.durationInMillis = 1; |
|
| 42 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 43 | + mediaTrack.duration = ONE_MILLISECOND; |
|
| 43 | 44 | |
| 44 | - Date startTime = new Date(mediaTrack.startTime.getTime() - 2); |
|
| 45 | - Date endTime = new Date(startTime.getTime() + 1); |
|
| 45 | + TimePoint startTime = mediaTrack.startTime.minus( 2); |
|
| 46 | + TimePoint endTime = startTime.plus(1); |
|
| 46 | 47 | assertFalse(mediaTrack.overlapsWith(startTime, endTime)); |
| 47 | 48 | |
| 48 | 49 | } |
| ... | ... | @@ -50,11 +51,11 @@ public class MediaTrackTest { |
| 50 | 51 | @Test |
| 51 | 52 | public void testPartialOverlapLeft() throws Exception { |
| 52 | 53 | MediaTrack mediaTrack = new MediaTrack(); |
| 53 | - mediaTrack.startTime = new Date(); |
|
| 54 | - mediaTrack.durationInMillis = 2; |
|
| 54 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 55 | + mediaTrack.duration = ONE_MILLISECOND.times(2); |
|
| 55 | 56 | |
| 56 | - Date startTime = new Date(mediaTrack.startTime.getTime() - 1); |
|
| 57 | - Date endTime = new Date(startTime.getTime() + 2); |
|
| 57 | + TimePoint startTime = mediaTrack.startTime.minus( 1); |
|
| 58 | + TimePoint endTime = startTime.plus(2); |
|
| 58 | 59 | assertTrue(mediaTrack.overlapsWith(startTime, endTime)); |
| 59 | 60 | |
| 60 | 61 | } |
| ... | ... | @@ -62,11 +63,11 @@ public class MediaTrackTest { |
| 62 | 63 | @Test |
| 63 | 64 | public void testPartialOverlapRight() throws Exception { |
| 64 | 65 | MediaTrack mediaTrack = new MediaTrack(); |
| 65 | - mediaTrack.startTime = new Date(); |
|
| 66 | - mediaTrack.durationInMillis = 2; |
|
| 66 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 67 | + mediaTrack.duration = ONE_MILLISECOND.times(2); |
|
| 67 | 68 | |
| 68 | - Date startTime = new Date(mediaTrack.startTime.getTime() + 1); |
|
| 69 | - Date endTime = new Date(startTime.getTime() + 2); |
|
| 69 | + TimePoint startTime = mediaTrack.startTime.plus(1); |
|
| 70 | + TimePoint endTime = startTime.plus(2); |
|
| 70 | 71 | assertTrue(mediaTrack.overlapsWith(startTime, endTime)); |
| 71 | 72 | |
| 72 | 73 | } |
| ... | ... | @@ -74,11 +75,11 @@ public class MediaTrackTest { |
| 74 | 75 | @Test |
| 75 | 76 | public void testMediaFullyIncluded() throws Exception { |
| 76 | 77 | MediaTrack mediaTrack = new MediaTrack(); |
| 77 | - mediaTrack.startTime = new Date(); |
|
| 78 | - mediaTrack.durationInMillis = 1; |
|
| 78 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 79 | + mediaTrack.duration = ONE_MILLISECOND; |
|
| 79 | 80 | |
| 80 | - Date startTime = new Date(mediaTrack.startTime.getTime() - 1); |
|
| 81 | - Date endTime = new Date(startTime.getTime() + 3); |
|
| 81 | + TimePoint startTime = mediaTrack.startTime.minus( 1); |
|
| 82 | + TimePoint endTime = startTime.plus(3); |
|
| 82 | 83 | assertTrue(mediaTrack.overlapsWith(startTime, endTime)); |
| 83 | 84 | |
| 84 | 85 | } |
| ... | ... | @@ -86,11 +87,11 @@ public class MediaTrackTest { |
| 86 | 87 | @Test |
| 87 | 88 | public void testMediaFullyIncluding() throws Exception { |
| 88 | 89 | MediaTrack mediaTrack = new MediaTrack(); |
| 89 | - mediaTrack.startTime = new Date(); |
|
| 90 | - mediaTrack.durationInMillis = 3; |
|
| 90 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 91 | + mediaTrack.duration = ONE_MILLISECOND.times(3); |
|
| 91 | 92 | |
| 92 | - Date startTime = new Date(mediaTrack.startTime.getTime() + 1); |
|
| 93 | - Date endTime = new Date(startTime.getTime() + 1); |
|
| 93 | + TimePoint startTime = mediaTrack.startTime.plus(1); |
|
| 94 | + TimePoint endTime = startTime.plus(1); |
|
| 94 | 95 | assertTrue(mediaTrack.overlapsWith(startTime, endTime)); |
| 95 | 96 | |
| 96 | 97 | } |
| ... | ... | @@ -98,11 +99,11 @@ public class MediaTrackTest { |
| 98 | 99 | @Test |
| 99 | 100 | public void testOverlapOpenEndStartingEarlier() throws Exception { |
| 100 | 101 | MediaTrack mediaTrack = new MediaTrack(); |
| 101 | - mediaTrack.startTime = new Date(); |
|
| 102 | - mediaTrack.durationInMillis = 1; |
|
| 102 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 103 | + mediaTrack.duration = ONE_MILLISECOND; |
|
| 103 | 104 | |
| 104 | - Date startTime = new Date(mediaTrack.startTime.getTime() - 1); |
|
| 105 | - Date endTime = null; //--> open end |
|
| 105 | + TimePoint startTime = mediaTrack.startTime.minus( 1); |
|
| 106 | + TimePoint endTime = null; //--> open end |
|
| 106 | 107 | assertTrue(mediaTrack.overlapsWith(startTime, endTime)); |
| 107 | 108 | |
| 108 | 109 | } |
| ... | ... | @@ -110,11 +111,11 @@ public class MediaTrackTest { |
| 110 | 111 | @Test |
| 111 | 112 | public void testOverlapOpenEndStartingLater() throws Exception { |
| 112 | 113 | MediaTrack mediaTrack = new MediaTrack(); |
| 113 | - mediaTrack.startTime = new Date(); |
|
| 114 | - mediaTrack.durationInMillis = 2; |
|
| 114 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 115 | + mediaTrack.duration = ONE_MILLISECOND.times(2); |
|
| 115 | 116 | |
| 116 | - Date startTime = new Date(mediaTrack.startTime.getTime() + 1); |
|
| 117 | - Date endTime = null; //--> open end |
|
| 117 | + TimePoint startTime = mediaTrack.startTime.plus(1); |
|
| 118 | + TimePoint endTime = null; //--> open end |
|
| 118 | 119 | assertTrue(mediaTrack.overlapsWith(startTime, endTime)); |
| 119 | 120 | |
| 120 | 121 | } |
| ... | ... | @@ -122,11 +123,11 @@ public class MediaTrackTest { |
| 122 | 123 | @Test |
| 123 | 124 | public void testOpenEndNoOverlap() throws Exception { |
| 124 | 125 | MediaTrack mediaTrack = new MediaTrack(); |
| 125 | - mediaTrack.startTime = new Date(); |
|
| 126 | - mediaTrack.durationInMillis = 1; |
|
| 126 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 127 | + mediaTrack.duration = ONE_MILLISECOND; |
|
| 127 | 128 | |
| 128 | - Date startTime = new Date(mediaTrack.startTime.getTime() + 2); |
|
| 129 | - Date endTime = null; //--> open end |
|
| 129 | + TimePoint startTime = mediaTrack.startTime.plus(2); |
|
| 130 | + TimePoint endTime = null; //--> open end |
|
| 130 | 131 | assertFalse(mediaTrack.overlapsWith(startTime, endTime)); |
| 131 | 132 | |
| 132 | 133 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/MediaUtilTest.java
| ... | ... | @@ -1,83 +1,81 @@ |
| 1 | 1 | package com.sap.sailing.domain.common; |
| 2 | 2 | |
| 3 | -import static org.junit.Assert.assertFalse; |
|
| 4 | -import static org.junit.Assert.assertTrue; |
|
| 5 | - |
|
| 6 | -import java.util.Date; |
|
| 7 | - |
|
| 8 | 3 | import org.junit.Test; |
| 9 | 4 | |
| 5 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 10 | 6 | import com.sap.sailing.domain.common.media.MediaUtil; |
| 11 | 7 | |
| 8 | +import static org.junit.Assert.*; |
|
| 9 | + |
|
| 12 | 10 | public class MediaUtilTest { |
| 13 | 11 | |
| 14 | 12 | @Test |
| 15 | 13 | public void testCompareDatesAllNull() throws Exception { |
| 16 | - Date date1 = null; |
|
| 17 | - Date date2 = null; |
|
| 14 | + TimePoint date1 = null; |
|
| 15 | + TimePoint date2 = null; |
|
| 18 | 16 | assertTrue(MediaUtil.compareDatesAllowingNull(date1, date2) == 0); |
| 19 | 17 | } |
| 20 | 18 | |
| 21 | 19 | @Test |
| 22 | 20 | public void testCompareDatesFirstNull() throws Exception { |
| 23 | - Date date1 = null; |
|
| 24 | - Date date2 = new Date(); |
|
| 21 | + TimePoint date1 = null; |
|
| 22 | + TimePoint date2 = MillisecondsTimePoint.now(); |
|
| 25 | 23 | assertTrue(MediaUtil.compareDatesAllowingNull(date1, date2) < 0); |
| 26 | 24 | } |
| 27 | 25 | |
| 28 | 26 | @Test |
| 29 | 27 | public void testCompareDatesSecondNull() throws Exception { |
| 30 | - Date date1 = new Date(); |
|
| 31 | - Date date2 = null; |
|
| 28 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 29 | + TimePoint date2 = null; |
|
| 32 | 30 | assertTrue(MediaUtil.compareDatesAllowingNull(date1, date2) > 0); |
| 33 | 31 | } |
| 34 | 32 | |
| 35 | 33 | @Test |
| 36 | 34 | public void testCompareDatesFirstGreaterSecond() throws Exception { |
| 37 | - Date date1 = new Date(); |
|
| 38 | - Date date2 = new Date(date1.getTime() - 1); |
|
| 35 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 36 | + TimePoint date2 = date1.minus(1); |
|
| 39 | 37 | assertTrue(MediaUtil.compareDatesAllowingNull(date1, date2) > 0); |
| 40 | 38 | } |
| 41 | 39 | |
| 42 | 40 | @Test |
| 43 | 41 | public void testCompareDatesSecondGreaterFirst() throws Exception { |
| 44 | - Date date1 = new Date(); |
|
| 45 | - Date date2 = new Date(date1.getTime() + 1); |
|
| 42 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 43 | + TimePoint date2 = date1.plus(1); |
|
| 46 | 44 | assertTrue(MediaUtil.compareDatesAllowingNull(date1, date2) < 0); |
| 47 | 45 | } |
| 48 | 46 | |
| 49 | 47 | @Test |
| 50 | 48 | public void testEqualsDatesAllNull() throws Exception { |
| 51 | - Date date1 = null; |
|
| 52 | - Date date2 = null; |
|
| 49 | + TimePoint date1 = null; |
|
| 50 | + TimePoint date2 = null; |
|
| 53 | 51 | assertTrue(MediaUtil.equalsDatesAllowingNull(date1, date2)); |
| 54 | 52 | } |
| 55 | 53 | |
| 56 | 54 | @Test |
| 57 | 55 | public void testEqualsDatesFirstNull() throws Exception { |
| 58 | - Date date1 = null; |
|
| 59 | - Date date2 = new Date(); |
|
| 56 | + TimePoint date1 = null; |
|
| 57 | + TimePoint date2 = MillisecondsTimePoint.now(); |
|
| 60 | 58 | assertFalse(MediaUtil.equalsDatesAllowingNull(date1, date2)); |
| 61 | 59 | } |
| 62 | 60 | |
| 63 | 61 | @Test |
| 64 | 62 | public void testEqualsDatesSecondNull() throws Exception { |
| 65 | - Date date1 = new Date(); |
|
| 66 | - Date date2 = null; |
|
| 63 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 64 | + TimePoint date2 = null; |
|
| 67 | 65 | assertFalse(MediaUtil.equalsDatesAllowingNull(date1, date2)); |
| 68 | 66 | } |
| 69 | 67 | |
| 70 | 68 | @Test |
| 71 | 69 | public void testEqualsDatesBothEqual() throws Exception { |
| 72 | - Date date1 = new Date(); |
|
| 73 | - Date date2 = new Date(date1.getTime()); |
|
| 70 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 71 | + TimePoint date2 = date1.plus(0); |
|
| 74 | 72 | assertTrue(MediaUtil.equalsDatesAllowingNull(date1, date2)); |
| 75 | 73 | } |
| 76 | 74 | |
| 77 | 75 | @Test |
| 78 | 76 | public void testEqualsDatesNotEqual() throws Exception { |
| 79 | - Date date1 = new Date(); |
|
| 80 | - Date date2 = new Date(date1.getTime() + 1); |
|
| 77 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 78 | + TimePoint date2 = date1.plus(1); |
|
| 81 | 79 | assertFalse(MediaUtil.equalsDatesAllowingNull(date1, date2)); |
| 82 | 80 | } |
| 83 | 81 |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/DurationTest.java
| ... | ... | @@ -1,6 +1,8 @@ |
| 1 | 1 | package com.sap.sailing.domain.common.test; |
| 2 | 2 | |
| 3 | -import static org.junit.Assert.assertEquals; |
|
| 3 | +import static org.junit.Assert.*; |
|
| 4 | + |
|
| 5 | +import static org.hamcrest.Matchers.*; |
|
| 4 | 6 | |
| 5 | 7 | import org.junit.Test; |
| 6 | 8 | |
| ... | ... | @@ -98,4 +100,39 @@ public class DurationTest { |
| 98 | 100 | assertEquals(3600*25, oneDay.asSeconds(), 0.1); |
| 99 | 101 | assertEquals(3600*25*1000, oneDay.asMillis()); |
| 100 | 102 | } |
| 103 | + |
|
| 104 | + @Test |
|
| 105 | + public void compare() { |
|
| 106 | + assertThat(MillisecondsDurationImpl.ONE_MINUTE.compareTo(MillisecondsDurationImpl.ONE_HOUR), lessThan(0)); |
|
| 107 | + assertThat(MillisecondsDurationImpl.ONE_HOUR.compareTo(MillisecondsDurationImpl.ONE_MINUTE), greaterThan(0)); |
|
| 108 | + } |
|
| 109 | + |
|
| 110 | + @Test |
|
| 111 | + public void compareEquals() { |
|
| 112 | + assertThat(MillisecondsDurationImpl.ONE_HOUR.compareTo(MillisecondsDurationImpl.ONE_HOUR), is(0)); |
|
| 113 | + } |
|
| 114 | + |
|
| 115 | + @Test |
|
| 116 | + public void testEqualForSame() { |
|
| 117 | + assertThat(MillisecondsDurationImpl.ONE_HOUR, is(MillisecondsDurationImpl.ONE_HOUR)); |
|
| 118 | + } |
|
| 119 | + |
|
| 120 | + @Test |
|
| 121 | + public void testEqualForEuqal() { |
|
| 122 | + Duration t1 = MillisecondsDurationImpl.ONE_HOUR; |
|
| 123 | + Duration t2 = new MillisecondsDurationImpl(t1.asMillis()); |
|
| 124 | + assertThat(t1, is(t2)); |
|
| 125 | + assertThat(t2, is(t1)); |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + @Test |
|
| 129 | + public void testEqualForNull() { |
|
| 130 | + assertFalse(MillisecondsDurationImpl.ONE_HOUR.equals(null)); |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + @Test |
|
| 134 | + public void testEqualForString() { |
|
| 135 | + assertFalse(MillisecondsDurationImpl.ONE_HOUR.equals("s")); |
|
| 136 | + } |
|
| 137 | + |
|
| 101 | 138 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/common/test/TimePointTest.java
| ... | ... | @@ -1,17 +1,56 @@ |
| 1 | 1 | package com.sap.sailing.domain.common.test; |
| 2 | 2 | |
| 3 | -import static org.junit.Assert.assertTrue; |
|
| 4 | - |
|
| 5 | 3 | import org.junit.Test; |
| 6 | 4 | |
| 7 | 5 | import com.sap.sailing.domain.common.TimePoint; |
| 8 | 6 | import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
| 9 | 7 | |
| 8 | +import static org.junit.Assert.*; |
|
| 9 | + |
|
| 10 | +import static org.hamcrest.Matchers.*; |
|
| 11 | + |
|
| 10 | 12 | public class TimePointTest { |
| 11 | 13 | @Test |
| 12 | 14 | public void compare() { |
| 13 | 15 | TimePoint one = new MillisecondsTimePoint(Long.MIN_VALUE); |
| 14 | 16 | TimePoint two = new MillisecondsTimePoint(Long.MAX_VALUE); |
| 15 | 17 | assertTrue(one.before(two)); |
| 18 | + assertTrue(two.after(one)); |
|
| 19 | + } |
|
| 20 | + |
|
| 21 | + @Test |
|
| 22 | + public void compareEquals() { |
|
| 23 | + TimePoint one = new MillisecondsTimePoint(Long.MIN_VALUE); |
|
| 24 | + TimePoint two = new MillisecondsTimePoint(Long.MIN_VALUE); |
|
| 25 | + assertThat(one.compareTo(two), is(0)); |
|
| 26 | + assertFalse(one.before(two)); |
|
| 27 | + assertFalse(two.after(one)); |
|
| 28 | + } |
|
| 29 | + |
|
| 30 | + @Test |
|
| 31 | + public void testEqualForSame() { |
|
| 32 | + TimePoint t = new MillisecondsTimePoint(Long.MIN_VALUE); |
|
| 33 | + assertThat(t, is(t)); |
|
| 34 | + } |
|
| 35 | + |
|
| 36 | + @Test |
|
| 37 | + public void testEqualForEuqal() { |
|
| 38 | + TimePoint t1 = new MillisecondsTimePoint(Long.MIN_VALUE); |
|
| 39 | + TimePoint t2 = new MillisecondsTimePoint(Long.MIN_VALUE); |
|
| 40 | + assertThat(t1, is(t2)); |
|
| 41 | + assertThat(t2, is(t1)); |
|
| 42 | + } |
|
| 43 | + |
|
| 44 | + @Test |
|
| 45 | + public void testEqualForNull() { |
|
| 46 | + TimePoint t = new MillisecondsTimePoint(Long.MIN_VALUE); |
|
| 47 | + assertFalse(t.equals(null)); |
|
| 16 | 48 | } |
| 49 | + |
|
| 50 | + @Test |
|
| 51 | + public void testEqualForString() { |
|
| 52 | + TimePoint t = new MillisecondsTimePoint(Long.MIN_VALUE); |
|
| 53 | + assertFalse(t.equals("s")); |
|
| 54 | + } |
|
| 55 | + |
|
| 17 | 56 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CalculateImageSizeFromUrlTest.java
| ... | ... | @@ -3,7 +3,6 @@ package com.sap.sailing.domain.test; |
| 3 | 3 | import static org.junit.Assert.assertEquals; |
| 4 | 4 | import static org.junit.Assert.assertTrue; |
| 5 | 5 | |
| 6 | -import java.awt.Dimension; |
|
| 7 | 6 | import java.io.IOException; |
| 8 | 7 | import java.net.MalformedURLException; |
| 9 | 8 | import java.net.URL; |
| ... | ... | @@ -20,6 +19,8 @@ import org.junit.Test; |
| 20 | 19 | |
| 21 | 20 | import com.sap.sailing.domain.base.Event; |
| 22 | 21 | import com.sap.sailing.domain.base.impl.EventImpl; |
| 22 | +import com.sap.sailing.domain.common.ImageSize; |
|
| 23 | +import com.sap.sailing.domain.common.impl.ImageSizeImpl; |
|
| 23 | 24 | |
| 24 | 25 | public class CalculateImageSizeFromUrlTest { |
| 25 | 26 | |
| ... | ... | @@ -27,12 +28,12 @@ public class CalculateImageSizeFromUrlTest { |
| 27 | 28 | public void calculateImageSizeFromUrl() throws IOException { |
| 28 | 29 | int width = 350; |
| 29 | 30 | int height = 150; |
| 30 | - Dimension size = calculate("http://placehold.it/" + width + "x" + height); |
|
| 31 | + ImageSize size = calculate("http://placehold.it/" + width + "x" + height); |
|
| 31 | 32 | assertTrue(size.getWidth() == width); |
| 32 | 33 | assertTrue(size.getHeight() == height); |
| 33 | 34 | } |
| 34 | 35 | |
| 35 | - public Dimension calculate(String urlS) throws IOException { |
|
| 36 | + public ImageSize calculate(String urlS) throws IOException { |
|
| 36 | 37 | ImageInputStream in = null; |
| 37 | 38 | try { |
| 38 | 39 | URL url = new URL(urlS); |
| ... | ... | @@ -43,7 +44,7 @@ public class CalculateImageSizeFromUrlTest { |
| 43 | 44 | ImageReader reader = readers.next(); |
| 44 | 45 | try { |
| 45 | 46 | reader.setInput(in); |
| 46 | - return new Dimension(reader.getWidth(0), reader.getHeight(0)); |
|
| 47 | + return new ImageSizeImpl(reader.getWidth(0), reader.getHeight(0)); |
|
| 47 | 48 | } finally { |
| 48 | 49 | reader.dispose(); |
| 49 | 50 | } |
| ... | ... | @@ -63,7 +64,7 @@ public class CalculateImageSizeFromUrlTest { |
| 63 | 64 | int height = Math.max(10, (int) (100. * Math.random())); |
| 64 | 65 | URL imageURL = new URL("http://placehold.it/" + width + "x" + height); |
| 65 | 66 | e.addImageURL(imageURL); |
| 66 | - Dimension expectedSize = new Dimension(width, height); |
|
| 67 | + ImageSize expectedSize = new ImageSizeImpl(width, height); |
|
| 67 | 68 | assertEquals(expectedSize, e.getImageSize(imageURL)); |
| 68 | 69 | } |
| 69 | 70 | } |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConcurrentCollectionsPerformanceTest.java
| ... | ... | @@ -0,0 +1,62 @@ |
| 1 | +package com.sap.sailing.domain.test; |
|
| 2 | + |
|
| 3 | +import static org.junit.Assert.assertEquals; |
|
| 4 | + |
|
| 5 | +import java.util.ArrayList; |
|
| 6 | +import java.util.Collection; |
|
| 7 | +import java.util.Collections; |
|
| 8 | +import java.util.List; |
|
| 9 | +import java.util.concurrent.ConcurrentLinkedQueue; |
|
| 10 | + |
|
| 11 | +import org.junit.Test; |
|
| 12 | + |
|
| 13 | +import com.sap.sailing.util.impl.ConcurrentHashBag; |
|
| 14 | +import com.sap.sse.common.Util; |
|
| 15 | + |
|
| 16 | +/** |
|
| 17 | + * Measures the performance of different concurrent collections types and shows their add and remove behavior for |
|
| 18 | + * different collection sizes and different numbers of concurrently accessing threads. The collections used must be able |
|
| 19 | + * to hold the same element multiple times, keeping count, not necessarily order. |
|
| 20 | + * |
|
| 21 | + * @author Axel Uhl (D043530) |
|
| 22 | + * |
|
| 23 | + */ |
|
| 24 | +public class ConcurrentCollectionsPerformanceTest { |
|
| 25 | + private static final int COUNT = 10000; |
|
| 26 | + |
|
| 27 | + @Test |
|
| 28 | + public void testConcurrentLinkedQueue() { |
|
| 29 | + Collection<Object> c = new ConcurrentLinkedQueue<Object>(); |
|
| 30 | + System.out.println("ConcurrentLinkedQueue" + runWith(c)); |
|
| 31 | + } |
|
| 32 | + |
|
| 33 | + @Test |
|
| 34 | + public void testConcurrentHashMap() { |
|
| 35 | + Collection<Object> c = new ConcurrentHashBag<Object>(); |
|
| 36 | + System.out.println("ConcurrentHashBag" + runWith(c)); |
|
| 37 | + } |
|
| 38 | + |
|
| 39 | + private com.sap.sse.common.Util.Pair<Long, Long> runWith(Collection<Object> c) { |
|
| 40 | + List<Object> l = new ArrayList<>(COUNT); |
|
| 41 | + for (int i=0; i<COUNT; i++) { |
|
| 42 | + l.add(new Object()); |
|
| 43 | + } |
|
| 44 | + // add all objects a second time to ensure |
|
| 45 | + for (Object o : new ArrayList<Object>(l)) { |
|
| 46 | + l.add(o); |
|
| 47 | + } |
|
| 48 | + long startInsert = System.currentTimeMillis(); |
|
| 49 | + for (Object o : l) { |
|
| 50 | + c.add(o); |
|
| 51 | + } |
|
| 52 | + long endInsert = System.currentTimeMillis(); |
|
| 53 | + assertEquals(l.size(), c.size()); |
|
| 54 | + Collections.shuffle(l); |
|
| 55 | + long startRemove = System.currentTimeMillis(); |
|
| 56 | + for (Object o : l) { |
|
| 57 | + c.remove(o); |
|
| 58 | + } |
|
| 59 | + long endRemove = System.currentTimeMillis(); |
|
| 60 | + return new Util.Pair<Long, Long>(endInsert-startInsert, endRemove-startRemove); |
|
| 61 | + } |
|
| 62 | +} |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseTest.java
| ... | ... | @@ -24,6 +24,7 @@ import com.sap.sailing.domain.base.Sideline; |
| 24 | 24 | import com.sap.sailing.domain.base.Waypoint;
|
| 25 | 25 | import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
| 26 | 26 | import com.sap.sailing.domain.base.impl.CompetitorImpl;
|
| 27 | +import com.sap.sailing.domain.base.impl.ControlPointWithTwoMarksImpl;
|
|
| 27 | 28 | import com.sap.sailing.domain.base.impl.CourseImpl;
|
| 28 | 29 | import com.sap.sailing.domain.base.impl.MarkImpl;
|
| 29 | 30 | import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
| ... | ... | @@ -37,6 +38,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; |
| 37 | 38 | import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
| 38 | 39 | import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
| 39 | 40 | import com.sap.sse.common.Util;
|
| 41 | +import com.sap.sse.common.Util.Pair;
|
|
| 40 | 42 | |
| 41 | 43 | import difflib.PatchFailedException;
|
| 42 | 44 | |
| ... | ... | @@ -186,6 +188,53 @@ public class CourseTest { |
| 186 | 188 | course.update(courseToUpdate, DomainFactory.INSTANCE);
|
| 187 | 189 | assertWaypointIndexes(course);
|
| 188 | 190 | }
|
| 191 | +
|
|
| 192 | + /**
|
|
| 193 | + * This test tries to replicate the behavior described in bug 2223. The course was
|
|
| 194 | + * "RC-Black Conical -> Orange -> White Gate -> Red -> Yellow -> Finish Pole-Cylinder" and the delta was
|
|
| 195 | + * "[[DeleteDelta, position: 1, lines: [Orange, White Gate]], [InsertDelta, position: 4, lines: [White Gate, Red]]]"
|
|
| 196 | + * which was computed from the new sequence of control points
|
|
| 197 | + * "[Control, RC-Black Conical, Control, Red, Control, White Gate, Control, Red, Control, Yellow, Control, Finish Pole-Cylinder]".
|
|
| 198 | + */
|
|
| 199 | + @Test
|
|
| 200 | + public void testWaypointDeleteWithSubsequentInsertInOnePatch() throws PatchFailedException {
|
|
| 201 | + List<Waypoint> waypoints = new ArrayList<Waypoint>();
|
|
| 202 | + final WaypointImpl rcBlackConical = new WaypointImpl(new ControlPointWithTwoMarksImpl(new MarkImpl("RC"), new MarkImpl("Black Conical"), "RC-Black Conical"));
|
|
| 203 | + waypoints.add(rcBlackConical);
|
|
| 204 | + final WaypointImpl orange = new WaypointImpl(new MarkImpl("Orange"));
|
|
| 205 | + waypoints.add(orange);
|
|
| 206 | + final WaypointImpl whiteGate = new WaypointImpl(new ControlPointWithTwoMarksImpl(new MarkImpl("White L"), new MarkImpl("White R"), "White Gate"));
|
|
| 207 | + waypoints.add(whiteGate);
|
|
| 208 | + final WaypointImpl red = new WaypointImpl(new MarkImpl("Red"));
|
|
| 209 | + waypoints.add(red);
|
|
| 210 | + final WaypointImpl yellow = new WaypointImpl(new MarkImpl("Yellow"));
|
|
| 211 | + waypoints.add(yellow);
|
|
| 212 | + final WaypointImpl finishPoleCylinder = new WaypointImpl(new ControlPointWithTwoMarksImpl(new MarkImpl("Finish Pole"), new MarkImpl("Cylinder"), "Finish Pole-Cylinder"));
|
|
| 213 | + waypoints.add(finishPoleCylinder);
|
|
| 214 | + Course course = new CourseImpl("Race 24", waypoints);
|
|
| 215 | + assertWaypointIndexes(course);
|
|
| 216 | + List<com.sap.sse.common.Util.Pair<ControlPoint, PassingInstruction>> courseToUpdate = new ArrayList<com.sap.sse.common.Util.Pair<ControlPoint, PassingInstruction>>();
|
|
| 217 | + courseToUpdate.add(new Pair<>(rcBlackConical.getControlPoint(), rcBlackConical.getPassingInstructions()));
|
|
| 218 | + courseToUpdate.add(new Pair<>(red.getControlPoint(), red.getPassingInstructions()));
|
|
| 219 | + courseToUpdate.add(new Pair<>(whiteGate.getControlPoint(), whiteGate.getPassingInstructions()));
|
|
| 220 | + courseToUpdate.add(new Pair<>(red.getControlPoint(), red.getPassingInstructions()));
|
|
| 221 | + courseToUpdate.add(new Pair<>(yellow.getControlPoint(), yellow.getPassingInstructions()));
|
|
| 222 | + courseToUpdate.add(new Pair<>(finishPoleCylinder.getControlPoint(), finishPoleCylinder.getPassingInstructions()));
|
|
| 223 | + course.update(courseToUpdate, DomainFactory.INSTANCE);
|
|
| 224 | + assertWaypointIndexes(course);
|
|
| 225 | + List<ControlPoint> newControlPoints = new ArrayList<>();
|
|
| 226 | + for (Waypoint newWp : course.getWaypoints()) {
|
|
| 227 | + newControlPoints.add(newWp.getControlPoint());
|
|
| 228 | + }
|
|
| 229 | + assertTrue(Util.equals(Arrays.asList(
|
|
| 230 | + rcBlackConical.getControlPoint(),
|
|
| 231 | + red.getControlPoint(),
|
|
| 232 | + whiteGate.getControlPoint(),
|
|
| 233 | + red.getControlPoint(),
|
|
| 234 | + yellow.getControlPoint(),
|
|
| 235 | + finishPoleCylinder.getControlPoint()),
|
|
| 236 | + newControlPoints));
|
|
| 237 | + }
|
|
| 189 | 238 | |
| 190 | 239 | @Test
|
| 191 | 240 | public void testRemoveWaypointFromCourseWithThreeWaypoints() {
|
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/LockTraceTest.java
| ... | ... | @@ -16,6 +16,7 @@ import com.sap.sailing.domain.test.measurements.MeasurementCase; |
| 16 | 16 | import com.sap.sailing.domain.test.measurements.MeasurementXMLFile; |
| 17 | 17 | import com.sap.sailing.util.impl.LockUtil; |
| 18 | 18 | import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; |
| 19 | +import com.sap.sse.common.Util; |
|
| 19 | 20 | |
| 20 | 21 | public class LockTraceTest { |
| 21 | 22 | private static class LockingThread extends Thread { |
| ... | ... | @@ -47,18 +48,18 @@ public class LockTraceTest { |
| 47 | 48 | public void testReentrantReadLocking() { |
| 48 | 49 | NamedReentrantReadWriteLock lock = new NamedReentrantReadWriteLock("testReentrantReadLocking-Lock", /* fair */ true); |
| 49 | 50 | LockUtil.lockForRead(lock); |
| 50 | - assertTrue(lock.getReaders().contains(Thread.currentThread())); |
|
| 51 | - assertEquals(1, lock.getReaders().size()); |
|
| 51 | + assertTrue(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 52 | + assertEquals(1, Util.size(lock.getReaders())); |
|
| 52 | 53 | LockUtil.lockForRead(lock); |
| 53 | - assertTrue(lock.getReaders().contains(Thread.currentThread())); |
|
| 54 | - assertEquals(2, lock.getReaders().size()); |
|
| 54 | + assertTrue(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 55 | + assertEquals(2, Util.size(lock.getReaders())); |
|
| 55 | 56 | LockUtil.unlockAfterRead(lock); |
| 56 | - assertTrue(lock.getReaders().contains(Thread.currentThread())); |
|
| 57 | - assertEquals(1, lock.getReaders().size()); |
|
| 57 | + assertTrue(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 58 | + assertEquals(1, Util.size(lock.getReaders())); |
|
| 58 | 59 | LockUtil.unlockAfterRead(lock); |
| 59 | - assertFalse(lock.getReaders().contains(Thread.currentThread())); |
|
| 60 | + assertFalse(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 60 | 61 | assertEquals(0, lock.getReadHoldCount()); |
| 61 | - assertEquals(0, lock.getReaders().size()); |
|
| 62 | + assertEquals(0, Util.size(lock.getReaders())); |
|
| 62 | 63 | } |
| 63 | 64 | |
| 64 | 65 | @Test |
| ... | ... | @@ -67,15 +68,15 @@ public class LockTraceTest { |
| 67 | 68 | LockUtil.lockForWrite(lock); |
| 68 | 69 | assertTrue(lock.getWriter() == Thread.currentThread()); |
| 69 | 70 | LockUtil.lockForRead(lock); // reentrant read while holding write |
| 70 | - assertTrue(lock.getReaders().contains(Thread.currentThread())); |
|
| 71 | + assertTrue(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 71 | 72 | LockUtil.lockForWrite(lock); // reentrant write |
| 72 | 73 | assertTrue(lock.getWriter() == Thread.currentThread()); |
| 73 | - assertTrue(lock.getReaders().contains(Thread.currentThread())); |
|
| 74 | + assertTrue(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 74 | 75 | LockUtil.unlockAfterRead(lock); |
| 75 | - assertFalse(lock.getReaders().contains(Thread.currentThread())); |
|
| 76 | + assertFalse(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 76 | 77 | assertEquals(0, lock.getReadHoldCount()); |
| 77 | 78 | assertTrue(lock.getWriter() == Thread.currentThread()); |
| 78 | - assertFalse(lock.getReaders().contains(Thread.currentThread())); |
|
| 79 | + assertFalse(Util.contains(lock.getReaders(), Thread.currentThread())); |
|
| 79 | 80 | assertEquals(0, lock.getReadHoldCount()); |
| 80 | 81 | LockUtil.unlockAfterWrite(lock); |
| 81 | 82 | assertTrue(lock.getWriter() == Thread.currentThread()); |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PolarSheetGenerationTest.java
| ... | ... | @@ -278,7 +278,7 @@ public class PolarSheetGenerationTest { |
| 278 | 278 | }
|
| 279 | 279 | |
| 280 | 280 | @Override
|
| 281 | - public boolean isValid() {
|
|
| 281 | + public boolean isValidCached() {
|
|
| 282 | 282 | return true;
|
| 283 | 283 | }
|
| 284 | 284 |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/SmartFutureCacheDeadlockTest.java
| ... | ... | @@ -19,6 +19,7 @@ import com.sap.sailing.util.SmartFutureCache; |
| 19 | 19 | import com.sap.sailing.util.SmartFutureCache.EmptyUpdateInterval; |
| 20 | 20 | import com.sap.sailing.util.impl.LockUtil; |
| 21 | 21 | import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; |
| 22 | +import com.sap.sse.common.Util; |
|
| 22 | 23 | |
| 23 | 24 | /** |
| 24 | 25 | * When the thread reading a value from the cache holds a fair read lock, and another thread is trying to obtain the |
| ... | ... | @@ -88,15 +89,15 @@ public class SmartFutureCacheDeadlockTest { |
| 88 | 89 | @Test |
| 89 | 90 | public void testBasicLockingAndUnlockingWithScripts() throws InterruptedException { |
| 90 | 91 | logger.info("starting testBasicLockingAndUnlockingWithScripts"); |
| 91 | - assertFalse(lock.getReaders().contains(readerThread)); |
|
| 92 | + assertFalse(Util.contains(lock.getReaders(), readerThread)); |
|
| 92 | 93 | try { |
| 93 | 94 | reader.performAndWait(Command.LOCK_FOR_READ); |
| 94 | - assertTrue(lock.getReaders().contains(readerThread)); |
|
| 95 | + assertTrue(Util.contains(lock.getReaders(), readerThread)); |
|
| 95 | 96 | writer.perform(Command.LOCK_FOR_WRITE); |
| 96 | 97 | assertNull(lock.getWriter()); |
| 97 | 98 | } finally { |
| 98 | 99 | reader.performAndWait(Command.UNLOCK_AFTER_READ); |
| 99 | - assertFalse(lock.getReaders().contains(readerThread)); |
|
| 100 | + assertFalse(Util.contains(lock.getReaders(), readerThread)); |
|
| 100 | 101 | writer.performAndWait(Command.UNLOCK_AFTER_WRITE); |
| 101 | 102 | } |
| 102 | 103 | } |
| ... | ... | @@ -195,7 +196,7 @@ public class SmartFutureCacheDeadlockTest { |
| 195 | 196 | writerThread.join(); |
| 196 | 197 | assertFalse(reader.isRunning()); |
| 197 | 198 | assertFalse(writer.isRunning()); |
| 198 | - assertTrue(lock.getReaders().isEmpty()); |
|
| 199 | + assertTrue(Util.isEmpty(lock.getReaders())); |
|
| 199 | 200 | assertNull(lock.getWriter()); |
| 200 | 201 | } |
| 201 | 202 |
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/TrackTest.java
| ... | ... | @@ -26,6 +26,7 @@ import com.sap.sailing.domain.base.Boat; |
| 26 | 26 | import com.sap.sailing.domain.base.Timed;
|
| 27 | 27 | import com.sap.sailing.domain.base.impl.BoatClassImpl;
|
| 28 | 28 | import com.sap.sailing.domain.base.impl.BoatImpl;
|
| 29 | +import com.sap.sailing.domain.common.AbstractBearing;
|
|
| 29 | 30 | import com.sap.sailing.domain.common.Bearing;
|
| 30 | 31 | import com.sap.sailing.domain.common.Distance;
|
| 31 | 32 | import com.sap.sailing.domain.common.Position;
|
| ... | ... | @@ -43,6 +44,7 @@ import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; |
| 43 | 44 | import com.sap.sailing.domain.tracking.GPSFix;
|
| 44 | 45 | import com.sap.sailing.domain.tracking.GPSFixMoving;
|
| 45 | 46 | import com.sap.sailing.domain.tracking.GPSFixTrack;
|
| 47 | +import com.sap.sailing.domain.tracking.impl.CompactGPSFixMovingImpl;
|
|
| 46 | 48 | import com.sap.sailing.domain.tracking.impl.DistanceCache;
|
| 47 | 49 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
|
| 48 | 50 | import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
| ... | ... | @@ -852,7 +854,7 @@ public class TrackTest { |
| 852 | 854 | true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null);
|
| 853 | 855 | TimePoint now1 = MillisecondsTimePoint.now();
|
| 854 | 856 | TimePoint now2 = addMillisToTimepoint(now1, 1000); // 1s
|
| 855 | - DegreeBearingImpl bearing = new DegreeBearingImpl(90);
|
|
| 857 | + AbstractBearing bearing = new DegreeBearingImpl(90);
|
|
| 856 | 858 | Position position1 = new DegreePosition(1, 2);
|
| 857 | 859 | Position position2 = position1.translateGreatCircle(bearing, new MeterDistance(1));
|
| 858 | 860 | GPSFixMovingImpl myGpsFix1 = new GPSFixMovingImpl(position1, now1, new MeterPerSecondSpeedWithDegreeBearingImpl(1, bearing));
|
| ... | ... | @@ -873,7 +875,7 @@ public class TrackTest { |
| 873 | 875 | true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null);
|
| 874 | 876 | TimePoint now1 = MillisecondsTimePoint.now();
|
| 875 | 877 | TimePoint now2 = addMillisToTimepoint(now1, 1000); // 1s
|
| 876 | - DegreeBearingImpl bearing = new DegreeBearingImpl(90);
|
|
| 878 | + AbstractBearing bearing = new DegreeBearingImpl(90);
|
|
| 877 | 879 | Position position1 = new DegreePosition(1, 2);
|
| 878 | 880 | Position position2 = position1.translateGreatCircle(bearing, new MeterDistance(1));
|
| 879 | 881 | GPSFixMovingImpl myGpsFix1 = new GPSFixMovingImpl(position1, now1, new MeterPerSecondSpeedWithDegreeBearingImpl(10, bearing));
|
| ... | ... | @@ -894,7 +896,7 @@ public class TrackTest { |
| 894 | 896 | true), null), /* millisecondsOverWhichToAverage */5000, /* no smoothening */null);
|
| 895 | 897 | TimePoint now1 = MillisecondsTimePoint.now();
|
| 896 | 898 | TimePoint now2 = addMillisToTimepoint(now1, 1000); // 1s
|
| 897 | - DegreeBearingImpl bearing = new DegreeBearingImpl(90);
|
|
| 899 | + AbstractBearing bearing = new DegreeBearingImpl(90);
|
|
| 898 | 900 | Position position1 = new DegreePosition(1, 2);
|
| 899 | 901 | Position position2 = position1.translateGreatCircle(bearing, new MeterDistance(1));
|
| 900 | 902 | GPSFixMovingImpl myGpsFix1 = new GPSFixMovingImpl(position1, now1, new MeterPerSecondSpeedWithDegreeBearingImpl(0.1, bearing));
|
| ... | ... | @@ -1053,4 +1055,33 @@ public class TrackTest { |
| 1053 | 1055 | assertEquals(3, track.getAverageIntervalBetweenFixes().asMillis());
|
| 1054 | 1056 | assertEquals(3, track.getAverageIntervalBetweenRawFixes().asMillis());
|
| 1055 | 1057 | }
|
| 1058 | +
|
|
| 1059 | + @Test
|
|
| 1060 | + public void testEstimatedSpeedCaching() {
|
|
| 1061 | + GPSFixMoving originalFix = new GPSFixMovingImpl(new DegreePosition(12, 34), MillisecondsTimePoint.now(),
|
|
| 1062 | + new KnotSpeedWithBearingImpl(9, new DegreeBearingImpl(123)));
|
|
| 1063 | + GPSFixMoving fix = new CompactGPSFixMovingImpl(originalFix);
|
|
| 1064 | + assertFalse(fix.isEstimatedSpeedCached());
|
|
| 1065 | + final KnotSpeedWithBearingImpl estimatedSpeed = new KnotSpeedWithBearingImpl(9.1, new DegreeBearingImpl(124));
|
|
| 1066 | + fix.cacheEstimatedSpeed(estimatedSpeed);
|
|
| 1067 | + assertTrue(fix.isEstimatedSpeedCached());
|
|
| 1068 | + SpeedWithBearing cachedEstimatedSpeed = fix.getCachedEstimatedSpeed();
|
|
| 1069 | + assertEquals(estimatedSpeed, cachedEstimatedSpeed);
|
|
| 1070 | + fix.invalidateEstimatedSpeedCache();
|
|
| 1071 | + assertFalse(fix.isEstimatedSpeedCached());
|
|
| 1072 | + }
|
|
| 1073 | +
|
|
| 1074 | + @Test
|
|
| 1075 | + public void testEstimatedSpeedCachingOnTrack() {
|
|
| 1076 | + GPSFixMoving compactFix3 = track.getFirstFixAtOrAfter(gpsFix3.getTimePoint());
|
|
| 1077 | + assertFalse(compactFix3.isEstimatedSpeedCached());
|
|
| 1078 | + SpeedWithBearing fix3EstimatedSpeed = track.getEstimatedSpeed(gpsFix3.getTimePoint());
|
|
| 1079 | + assertTrue(compactFix3.isEstimatedSpeedCached());
|
|
| 1080 | + assertEquals(fix3EstimatedSpeed, compactFix3.getCachedEstimatedSpeed());
|
|
| 1081 | + assertEquals(fix3EstimatedSpeed, track.getEstimatedSpeed(gpsFix3.getTimePoint())); // fetch again from the cache
|
|
| 1082 | + // assuming that all test fixes are within a few milliseconds and the averaging interval is much larger than that,
|
|
| 1083 | + // adding a single fix in the middle should invalidate the cache
|
|
| 1084 | + track.add(new GPSFixMovingImpl(gpsFix3.getPosition(), gpsFix3.getTimePoint().plus(1), gpsFix3.getSpeed()));
|
|
| 1085 | + assertFalse(compactFix3.isEstimatedSpeedCached());
|
|
| 1086 | + }
|
|
| 1056 | 1087 | }
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Event.java
| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | 1 | package com.sap.sailing.domain.base;
|
| 2 | 2 | |
| 3 | -import java.awt.Dimension;
|
|
| 4 | 3 | import java.net.URL;
|
| 5 | 4 | import java.util.UUID;
|
| 6 | 5 | import java.util.concurrent.ExecutionException;
|
| 7 | 6 | |
| 7 | +import com.sap.sailing.domain.common.ImageSize;
|
|
| 8 | 8 | import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
| 9 | 9 | |
| 10 | 10 | /**
|
| ... | ... | @@ -59,6 +59,6 @@ public interface Event extends EventBase { |
| 59 | 59 | * Note that exceptions may result should the image be unavailable. If the image URL leads to a document that is not an image,
|
| 60 | 60 | * <code>null</code> will result.
|
| 61 | 61 | */
|
| 62 | - Dimension getImageSize(URL imageURL) throws InterruptedException, ExecutionException;
|
|
| 62 | + ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException;
|
|
| 63 | 63 | |
| 64 | 64 | }
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/DomainFactoryImpl.java
| ... | ... | @@ -47,6 +47,7 @@ import com.sap.sailing.domain.leaderboard.impl.HighPointLastBreaksTie; |
| 47 | 47 | import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsFive;
|
| 48 | 48 | import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsFiveIgnoringRaceCount;
|
| 49 | 49 | import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsSix;
|
| 50 | +import com.sap.sailing.domain.leaderboard.impl.HighPointWinnerGetsSixIgnoringRaceCount;
|
|
| 50 | 51 | import com.sap.sailing.domain.leaderboard.impl.LowPoint;
|
| 51 | 52 | import com.sap.sailing.domain.leaderboard.impl.LowPointWinnerGetsZero;
|
| 52 | 53 | import com.sap.sailing.domain.tracking.GPSFix;
|
| ... | ... | @@ -108,6 +109,8 @@ public class DomainFactoryImpl extends SharedDomainFactoryImpl implements Domain |
| 108 | 109 | return new HighPointWinnerGetsFiveIgnoringRaceCount();
|
| 109 | 110 | case HIGH_POINT_WINNER_GETS_SIX:
|
| 110 | 111 | return new HighPointWinnerGetsSix();
|
| 112 | + case HIGH_POINT_WINNER_GETS_SIX_IGNORING_RACE_COUNT:
|
|
| 113 | + return new HighPointWinnerGetsSixIgnoringRaceCount();
|
|
| 111 | 114 | case HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT:
|
| 112 | 115 | return new HighPointFirstGets10Or8AndLastBreaksTie();
|
| 113 | 116 | }
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java
| ... | ... | @@ -1,6 +1,5 @@ |
| 1 | 1 | package com.sap.sailing.domain.base.impl;
|
| 2 | 2 | |
| 3 | -import java.awt.Dimension;
|
|
| 4 | 3 | import java.io.IOException;
|
| 5 | 4 | import java.io.ObjectInputStream;
|
| 6 | 5 | import java.net.URL;
|
| ... | ... | @@ -25,7 +24,9 @@ import javax.imageio.stream.ImageInputStream; |
| 25 | 24 | import com.sap.sailing.domain.base.Event;
|
| 26 | 25 | import com.sap.sailing.domain.base.Regatta;
|
| 27 | 26 | import com.sap.sailing.domain.base.Venue;
|
| 27 | +import com.sap.sailing.domain.common.ImageSize;
|
|
| 28 | 28 | import com.sap.sailing.domain.common.TimePoint;
|
| 29 | +import com.sap.sailing.domain.common.impl.ImageSizeImpl;
|
|
| 29 | 30 | import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
| 30 | 31 | import com.sap.sse.common.Util;
|
| 31 | 32 | |
| ... | ... | @@ -36,7 +37,7 @@ public class EventImpl extends EventBaseImpl implements Event { |
| 36 | 37 | |
| 37 | 38 | private ConcurrentLinkedQueue<LeaderboardGroup> leaderboardGroups;
|
| 38 | 39 | |
| 39 | - private transient ConcurrentHashMap<URL, Future<Dimension>> imageSizeFetchers;
|
|
| 40 | + private transient ConcurrentHashMap<URL, Future<ImageSize>> imageSizeFetchers;
|
|
| 40 | 41 | private transient ExecutorService executor;
|
| 41 | 42 | |
| 42 | 43 | public EventImpl(String name, TimePoint startDate, TimePoint endDate, String venueName, boolean isPublic, UUID id) {
|
| ... | ... | @@ -103,13 +104,13 @@ public class EventImpl extends EventBaseImpl implements Event { |
| 103 | 104 | return leaderboardGroups.remove(leaderboardGroup);
|
| 104 | 105 | }
|
| 105 | 106 | |
| 106 | - private Future<Dimension> getOrCreateImageSizeCalculator(final URL imageURL) {
|
|
| 107 | - Future<Dimension> imageSizeFetcher = imageSizeFetchers.get(imageURL);
|
|
| 107 | + private Future<ImageSize> getOrCreateImageSizeCalculator(final URL imageURL) {
|
|
| 108 | + Future<ImageSize> imageSizeFetcher = imageSizeFetchers.get(imageURL);
|
|
| 108 | 109 | if (imageSizeFetcher == null) {
|
| 109 | - imageSizeFetcher = executor.submit(new Callable<Dimension>() {
|
|
| 110 | + imageSizeFetcher = executor.submit(new Callable<ImageSize>() {
|
|
| 110 | 111 | @Override
|
| 111 | - public Dimension call() throws IOException {
|
|
| 112 | - Dimension result = null;
|
|
| 112 | + public ImageSize call() throws IOException {
|
|
| 113 | + ImageSize result = null;
|
|
| 113 | 114 | ImageInputStream in = null;
|
| 114 | 115 | try {
|
| 115 | 116 | URLConnection conn = imageURL.openConnection();
|
| ... | ... | @@ -119,7 +120,7 @@ public class EventImpl extends EventBaseImpl implements Event { |
| 119 | 120 | ImageReader reader = readers.next();
|
| 120 | 121 | try {
|
| 121 | 122 | reader.setInput(in);
|
| 122 | - result = new Dimension(reader.getWidth(0), reader.getHeight(0));
|
|
| 123 | + result = new ImageSizeImpl(reader.getWidth(0), reader.getHeight(0));
|
|
| 123 | 124 | } finally {
|
| 124 | 125 | reader.dispose();
|
| 125 | 126 | }
|
| ... | ... | @@ -138,8 +139,8 @@ public class EventImpl extends EventBaseImpl implements Event { |
| 138 | 139 | }
|
| 139 | 140 | |
| 140 | 141 | @Override
|
| 141 | - public Dimension getImageSize(URL imageURL) throws InterruptedException, ExecutionException {
|
|
| 142 | - Future<Dimension> imageSizeCalculator = getOrCreateImageSizeCalculator(imageURL);
|
|
| 142 | + public ImageSize getImageSize(URL imageURL) throws InterruptedException, ExecutionException {
|
|
| 143 | + Future<ImageSize> imageSizeCalculator = getOrCreateImageSizeCalculator(imageURL);
|
|
| 143 | 144 | return imageSizeCalculator.get();
|
| 144 | 145 | }
|
| 145 | 146 | |
| ... | ... | @@ -171,8 +172,10 @@ public class EventImpl extends EventBaseImpl implements Event { |
| 171 | 172 | @Override
|
| 172 | 173 | public void setImageURLs(Iterable<URL> imageURLs) {
|
| 173 | 174 | super.setImageURLs(imageURLs);
|
| 174 | - for (URL imageURL : imageURLs) {
|
|
| 175 | - refreshImageSizeFetcher(imageURL);
|
|
| 175 | + if (imageURLs != null) {
|
|
| 176 | + for (URL imageURL : imageURLs) {
|
|
| 177 | + refreshImageSizeFetcher(imageURL);
|
|
| 178 | + }
|
|
| 176 | 179 | }
|
| 177 | 180 | }
|
| 178 | 181 | |
| ... | ... | @@ -191,8 +194,10 @@ public class EventImpl extends EventBaseImpl implements Event { |
| 191 | 194 | @Override
|
| 192 | 195 | public void setSponsorImageURLs(Iterable<URL> sponsorImageURLs) {
|
| 193 | 196 | super.setSponsorImageURLs(sponsorImageURLs);
|
| 194 | - for (URL imageURL : sponsorImageURLs) {
|
|
| 195 | - refreshImageSizeFetcher(imageURL);
|
|
| 197 | + if (sponsorImageURLs != null) {
|
|
| 198 | + for (URL imageURL : sponsorImageURLs) {
|
|
| 199 | + refreshImageSizeFetcher(imageURL);
|
|
| 200 | + }
|
|
| 196 | 201 | }
|
| 197 | 202 | }
|
| 198 | 203 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java
| ... | ... | @@ -1659,7 +1659,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race |
| 1659 | 1659 | boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
| 1660 | 1660 | LeaderboardDTOCalculationReuseCache cache) throws NoWindException {
|
| 1661 | 1661 | LegEntryDTO result;
|
| 1662 | - if (trackedLeg == null || trackedLeg.getTime(timePoint) == null) {
|
|
| 1662 | + final Duration time = trackedLeg.getTime(timePoint);
|
|
| 1663 | + if (trackedLeg == null || time == null) {
|
|
| 1663 | 1664 | result = null;
|
| 1664 | 1665 | } else {
|
| 1665 | 1666 | result = new LegEntryDTO();
|
| ... | ... | @@ -1681,7 +1682,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race |
| 1681 | 1682 | Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint);
|
| 1682 | 1683 | result.distanceTraveledInMeters = distanceTraveled == null ? null : distanceTraveled.getMeters();
|
| 1683 | 1684 | result.estimatedTimeToNextWaypointInSeconds = trackedLeg.getEstimatedTimeToNextMarkInSeconds(timePoint, WindPositionMode.EXACT, cache);
|
| 1684 | - result.timeInMilliseconds = trackedLeg.getTime(timePoint).asMillis();
|
|
| 1685 | + result.timeInMilliseconds = time.asMillis();
|
|
| 1685 | 1686 | result.finished = trackedLeg.hasFinishedLeg(timePoint);
|
| 1686 | 1687 | result.gapToLeaderInSeconds = trackedLeg.getGapToLeaderInSeconds(timePoint,
|
| 1687 | 1688 | legRanksCache.get(trackedLeg.getLeg()).entrySet().iterator().next().getKey(), WindPositionMode.LEG_MIDDLE);
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/HighPointWinnerGetsSixIgnoringRaceCount.java
| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | +package com.sap.sailing.domain.leaderboard.impl; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.common.ScoringSchemeType; |
|
| 4 | + |
|
| 5 | +public class HighPointWinnerGetsSixIgnoringRaceCount extends HighPointWinnerGetsSix { |
|
| 6 | + private static final long serialVersionUID = -6974180846946052247L; |
|
| 7 | + |
|
| 8 | + /** |
|
| 9 | + * Ignores the number of races scored, making the simple total points the first ranking criterion. |
|
| 10 | + */ |
|
| 11 | + @Override |
|
| 12 | + public int compareByNumberOfRacesScored(int competitor1NumberOfRacesScored, int competitor2NumberOfRacesScored) { |
|
| 13 | + return 0; |
|
| 14 | + } |
|
| 15 | + |
|
| 16 | + @Override |
|
| 17 | + public ScoringSchemeType getType() { |
|
| 18 | + return ScoringSchemeType.HIGH_POINT_WINNER_GETS_SIX_IGNORING_RACE_COUNT; |
|
| 19 | + } |
|
| 20 | +} |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/GPSFix.java
| ... | ... | @@ -3,6 +3,6 @@ package com.sap.sailing.domain.tracking; |
| 3 | 3 | import com.sap.sailing.domain.base.Timed;
|
| 4 | 4 | import com.sap.sailing.domain.common.SpeedWithBearing;
|
| 5 | 5 | |
| 6 | -public interface GPSFix extends Positioned, Timed, WithValidityCache {
|
|
| 6 | +public interface GPSFix extends Positioned, Timed, WithValidityCache, WithEstimatedSpeedCache {
|
|
| 7 | 7 | SpeedWithBearing getSpeedAndBearingRequiredToReach(GPSFix to);
|
| 8 | 8 | } |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/MarkPassingListener.java
| ... | ... | @@ -1,6 +0,0 @@ |
| 1 | -package com.sap.sailing.domain.tracking;
|
|
| 2 | -
|
|
| 3 | -
|
|
| 4 | -public interface MarkPassingListener {
|
|
| 5 | - void legCompleted(MarkPassing markPassing);
|
|
| 6 | -}
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/WithEstimatedSpeedCache.java
| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | +package com.sap.sailing.domain.tracking; |
|
| 2 | + |
|
| 3 | +import com.sap.sailing.domain.common.SpeedWithBearing; |
|
| 4 | + |
|
| 5 | +public interface WithEstimatedSpeedCache { |
|
| 6 | + boolean isEstimatedSpeedCached(); |
|
| 7 | + |
|
| 8 | + /** |
|
| 9 | + * Returns a valid result if {@link #isEstimatedSpeedCached()} returns <code>true</code> |
|
| 10 | + */ |
|
| 11 | + SpeedWithBearing getCachedEstimatedSpeed(); |
|
| 12 | + |
|
| 13 | + void invalidateEstimatedSpeedCache(); |
|
| 14 | + |
|
| 15 | + void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed); |
|
| 16 | + |
|
| 17 | +} |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/WithValidityCache.java
| ... | ... | @@ -3,7 +3,10 @@ package com.sap.sailing.domain.tracking; |
| 3 | 3 | public interface WithValidityCache {
|
| 4 | 4 | boolean isValidityCached();
|
| 5 | 5 | |
| 6 | - boolean isValid();
|
|
| 6 | + /**
|
|
| 7 | + * Returns a valid result if {@link #isValidCached()} returns <code>true</code>
|
|
| 8 | + */
|
|
| 9 | + boolean isValidCached();
|
|
| 7 | 10 | |
| 8 | 11 | void invalidateCache();
|
| 9 | 12 |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/AbstractGPSFixImpl.java
| ... | ... | @@ -48,14 +48,21 @@ public abstract class AbstractGPSFixImpl implements GPSFix { |
| 48 | 48 | Speed speed = distance.inTime(to.getTimePoint().asMillis()-getTimePoint().asMillis());
|
| 49 | 49 | return new KnotSpeedWithBearingImpl(speed.getKnots(), bearing);
|
| 50 | 50 | }
|
| 51 | -
|
|
| 51 | +
|
|
| 52 | + /**
|
|
| 53 | + * Subclasses overriding this method also need to override {@link #isValid} with a useful implementation.
|
|
| 54 | + */
|
|
| 52 | 55 | @Override
|
| 53 | 56 | public boolean isValidityCached() {
|
| 54 | 57 | return false;
|
| 55 | 58 | }
|
| 56 | 59 | |
| 60 | + /**
|
|
| 61 | + * Only evaluated if {@link #isValidityCached()} returns <code>true</code>. Therefore, subclassess that
|
|
| 62 | + * override {@link #isValidityCached()} also need to override this method accordingly.
|
|
| 63 | + */
|
|
| 57 | 64 | @Override
|
| 58 | - public boolean isValid() {
|
|
| 65 | + public boolean isValidCached() {
|
|
| 59 | 66 | return false;
|
| 60 | 67 | }
|
| 61 | 68 | |
| ... | ... | @@ -66,4 +73,29 @@ public abstract class AbstractGPSFixImpl implements GPSFix { |
| 66 | 73 | @Override
|
| 67 | 74 | public void cacheValidity(boolean isValid) {
|
| 68 | 75 | }
|
| 76 | +
|
|
| 77 | + /**
|
|
| 78 | + * Subclasses overriding this method also need to override {@link #getCachedEstimatedSpeed()} with a useful implementation.
|
|
| 79 | + */
|
|
| 80 | + @Override
|
|
| 81 | + public boolean isEstimatedSpeedCached() {
|
|
| 82 | + return false;
|
|
| 83 | + }
|
|
| 84 | +
|
|
| 85 | + /**
|
|
| 86 | + * Only evaluated if {@link #isEstimatedSpeedCached()} returns <code>true</code>. Therefore, subclassess that
|
|
| 87 | + * override {@link #isEstimatedSpeedCached()} also need to override this method accordingly.
|
|
| 88 | + */
|
|
| 89 | + @Override
|
|
| 90 | + public SpeedWithBearing getCachedEstimatedSpeed() {
|
|
| 91 | + return null;
|
|
| 92 | + }
|
|
| 93 | +
|
|
| 94 | + @Override
|
|
| 95 | + public void invalidateEstimatedSpeedCache() {
|
|
| 96 | + }
|
|
| 97 | +
|
|
| 98 | + @Override
|
|
| 99 | + public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
|
|
| 100 | + }
|
|
| 69 | 101 | }
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CompactGPSFixImpl.java
| ... | ... | @@ -1,8 +1,12 @@ |
| 1 | 1 | package com.sap.sailing.domain.tracking.impl;
|
| 2 | 2 | |
| 3 | +import com.sap.sailing.domain.common.AbstractBearing;
|
|
| 3 | 4 | import com.sap.sailing.domain.common.AbstractPosition;
|
| 5 | +import com.sap.sailing.domain.common.Bearing;
|
|
| 4 | 6 | import com.sap.sailing.domain.common.Position;
|
| 7 | +import com.sap.sailing.domain.common.SpeedWithBearing;
|
|
| 5 | 8 | import com.sap.sailing.domain.common.TimePoint;
|
| 9 | +import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
|
|
| 6 | 10 | import com.sap.sailing.domain.common.impl.AbstractTimePoint;
|
| 7 | 11 | import com.sap.sailing.domain.tracking.GPSFix;
|
| 8 | 12 | |
| ... | ... | @@ -19,6 +23,23 @@ import com.sap.sailing.domain.tracking.GPSFix; |
| 19 | 23 | */
|
| 20 | 24 | public class CompactGPSFixImpl extends AbstractGPSFixImpl {
|
| 21 | 25 | private static final long serialVersionUID = 8167588584536992501L;
|
| 26 | +
|
|
| 27 | + /**
|
|
| 28 | + * Bit mask for {@link #whatIsCached}, telling whether validity is currently cached
|
|
| 29 | + */
|
|
| 30 | + private static final byte IS_VALIDITY_CACHED = 1<<0;
|
|
| 31 | +
|
|
| 32 | + /**
|
|
| 33 | + * Bit mask for {@link #whatIsCached}, telling whether the estimated speed is currently cached
|
|
| 34 | + */
|
|
| 35 | + private static final byte IS_ESTIMATED_SPEED_CACHED = 1<<1;
|
|
| 36 | +
|
|
| 37 | + /**
|
|
| 38 | + * Bit mask for {@link #whatIsCached}, telling the validity of the fix; only relevant if
|
|
| 39 | + * <code>{@link #whatIsCached}&{@link #IS_VALIDITY_CACHED} != 0</code>
|
|
| 40 | + */
|
|
| 41 | + private static final byte VALIDITY = 1<<2;
|
|
| 42 | +
|
|
| 22 | 43 | private final double latDeg;
|
| 23 | 44 | private final double lngDeg;
|
| 24 | 45 | private final long timePointAsMillis;
|
| ... | ... | @@ -28,7 +49,19 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl { |
| 28 | 49 | * needs to be invalidated as soon as fixes are added to the containing track which may have an impact
|
| 29 | 50 | * on this fix's validity. -1 means "no value"; 0 means invalid, 1 means valid.
|
| 30 | 51 | */
|
| 31 | - private byte validityCache = -1;
|
|
| 52 | + private byte whatIsCached = 0;
|
|
| 53 | +
|
|
| 54 | + /**
|
|
| 55 | + * When <code>{@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed's
|
|
| 56 | + * true "bearing" (true course over ground) in degrees.
|
|
| 57 | + */
|
|
| 58 | + private double cachedEstimatedSpeedBearingInDegrees;
|
|
| 59 | +
|
|
| 60 | + /**
|
|
| 61 | + * When <code>{@link #whatIsCached}&{@link #IS_ESTIMATED_SPEED_CACHED} != 0</code>, this field tells the estimated speed
|
|
| 62 | + * in knots.
|
|
| 63 | + */
|
|
| 64 | + private double cachedEstimatedSpeedInKnots;
|
|
| 32 | 65 | |
| 33 | 66 | private class CompactPosition extends AbstractPosition {
|
| 34 | 67 | private static final long serialVersionUID = 5621506820766614178L;
|
| ... | ... | @@ -53,6 +86,29 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl { |
| 53 | 86 | }
|
| 54 | 87 | }
|
| 55 | 88 | |
| 89 | + private class CompactEstimatedSpeedBearing extends AbstractBearing {
|
|
| 90 | + private static final long serialVersionUID = 8549231429037883121L;
|
|
| 91 | +
|
|
| 92 | + @Override
|
|
| 93 | + public double getDegrees() {
|
|
| 94 | + return cachedEstimatedSpeedBearingInDegrees;
|
|
| 95 | + }
|
|
| 96 | + }
|
|
| 97 | +
|
|
| 98 | + private class CompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl {
|
|
| 99 | + private static final long serialVersionUID = -5871855443391817248L;
|
|
| 100 | +
|
|
| 101 | + @Override
|
|
| 102 | + public Bearing getBearing() {
|
|
| 103 | + return new CompactEstimatedSpeedBearing();
|
|
| 104 | + }
|
|
| 105 | +
|
|
| 106 | + @Override
|
|
| 107 | + public double getKnots() {
|
|
| 108 | + return cachedEstimatedSpeedInKnots;
|
|
| 109 | + }
|
|
| 110 | + }
|
|
| 111 | +
|
|
| 56 | 112 | public CompactGPSFixImpl(Position position, TimePoint timePoint) {
|
| 57 | 113 | latDeg = position.getLatDeg();
|
| 58 | 114 | lngDeg = position.getLngDeg();
|
| ... | ... | @@ -80,22 +136,52 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl { |
| 80 | 136 | |
| 81 | 137 | @Override
|
| 82 | 138 | public boolean isValidityCached() {
|
| 83 | - return validityCache != -1;
|
|
| 139 | + return (whatIsCached & IS_VALIDITY_CACHED) != 0;
|
|
| 84 | 140 | }
|
| 85 | 141 | |
| 86 | 142 | @Override
|
| 87 | - public boolean isValid() {
|
|
| 88 | - return validityCache == 1;
|
|
| 143 | + public boolean isValidCached() {
|
|
| 144 | + assert isValidityCached();
|
|
| 145 | + return (whatIsCached & VALIDITY) != 0;
|
|
| 89 | 146 | }
|
| 90 | 147 | |
| 91 | 148 | @Override
|
| 92 | 149 | public void invalidateCache() {
|
| 93 | - validityCache = -1;
|
|
| 150 | + whatIsCached &= ~IS_VALIDITY_CACHED;
|
|
| 94 | 151 | }
|
| 95 | 152 | |
| 96 | 153 | @Override
|
| 97 | 154 | public void cacheValidity(boolean isValid) {
|
| 98 | - validityCache = (byte) (isValid ? 1 : 0);
|
|
| 155 | + if (isValid) {
|
|
| 156 | + whatIsCached |= IS_VALIDITY_CACHED | VALIDITY;
|
|
| 157 | + } else {
|
|
| 158 | + whatIsCached |= IS_VALIDITY_CACHED;
|
|
| 159 | + whatIsCached &= ~VALIDITY;
|
|
| 160 | + }
|
|
| 161 | + }
|
|
| 162 | +
|
|
| 163 | + @Override
|
|
| 164 | + public boolean isEstimatedSpeedCached() {
|
|
| 165 | + return (whatIsCached & IS_ESTIMATED_SPEED_CACHED) != 0;
|
|
| 99 | 166 | }
|
| 167 | +
|
|
| 168 | + @Override
|
|
| 169 | + public SpeedWithBearing getCachedEstimatedSpeed() {
|
|
| 170 | + assert isEstimatedSpeedCached();
|
|
| 171 | + return new CompactEstimatedSpeed();
|
|
| 172 | + }
|
|
| 173 | +
|
|
| 174 | + @Override
|
|
| 175 | + public void invalidateEstimatedSpeedCache() {
|
|
| 176 | + whatIsCached &= ~IS_ESTIMATED_SPEED_CACHED;
|
|
| 177 | + }
|
|
| 178 | +
|
|
| 179 | + @Override
|
|
| 180 | + public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) {
|
|
| 181 | + cachedEstimatedSpeedBearingInDegrees = estimatedSpeed.getBearing().getDegrees();
|
|
| 182 | + cachedEstimatedSpeedInKnots = estimatedSpeed.getKnots();
|
|
| 183 | + whatIsCached |= IS_ESTIMATED_SPEED_CACHED;
|
|
| 184 | + }
|
|
| 185 | +
|
|
| 100 | 186 | |
| 101 | 187 | }
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicGPSFixMovingTrackImpl.java
| ... | ... | @@ -123,7 +123,7 @@ public class DynamicGPSFixMovingTrackImpl<ItemType> extends GPSFixTrackImpl<Item |
| 123 | 123 | assertReadLock();
|
| 124 | 124 | final boolean isValid;
|
| 125 | 125 | if (e.isValidityCached()) {
|
| 126 | - isValid = e.isValid();
|
|
| 126 | + isValid = e.isValidCached();
|
|
| 127 | 127 | } else {
|
| 128 | 128 | boolean fixHasValidSogAndCog = (e.getSpeed().getMetersPerSecond() != 0.0 || e.getSpeed().getBearing().getDegrees() != 0.0) &&
|
| 129 | 129 | (maxSpeedForSmoothing == null || e.getSpeed().compareTo(maxSpeedForSmoothing) <= 0);
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GPSFixTrackImpl.java
| ... | ... | @@ -166,6 +166,9 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 166 | 166 | |
| 167 | 167 | private transient MaxSpeedCache<ItemType, FixType> maxSpeedCache; |
| 168 | 168 | |
| 169 | + private int estimatedSpeedCacheHits; |
|
| 170 | + private int estimatedSpeedCacheMisses; |
|
| 171 | + |
|
| 169 | 172 | public GPSFixTrackImpl(ItemType trackedItem, long millisecondsOverWhichToAverage) { |
| 170 | 173 | this(trackedItem, millisecondsOverWhichToAverage, DEFAULT_MAX_SPEED_FOR_SMOOTHING); |
| 171 | 174 | } |
| ... | ... | @@ -230,7 +233,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 230 | 233 | } |
| 231 | 234 | |
| 232 | 235 | @Override |
| 233 | - public boolean isValid() { |
|
| 236 | + public boolean isValidCached() { |
|
| 234 | 237 | return false; |
| 235 | 238 | } |
| 236 | 239 | |
| ... | ... | @@ -241,6 +244,24 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 241 | 244 | @Override |
| 242 | 245 | public void cacheValidity(boolean isValid) { |
| 243 | 246 | } |
| 247 | + |
|
| 248 | + @Override |
|
| 249 | + public boolean isEstimatedSpeedCached() { |
|
| 250 | + return false; |
|
| 251 | + } |
|
| 252 | + |
|
| 253 | + @Override |
|
| 254 | + public SpeedWithBearing getCachedEstimatedSpeed() { |
|
| 255 | + return null; |
|
| 256 | + } |
|
| 257 | + |
|
| 258 | + @Override |
|
| 259 | + public void invalidateEstimatedSpeedCache() { |
|
| 260 | + } |
|
| 261 | + |
|
| 262 | + @Override |
|
| 263 | + public void cacheEstimatedSpeed(SpeedWithBearing estimatedSpeed) { |
|
| 264 | + } |
|
| 244 | 265 | } |
| 245 | 266 | |
| 246 | 267 | |
| ... | ... | @@ -577,12 +598,29 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 577 | 598 | @Override |
| 578 | 599 | public SpeedWithBearing getEstimatedSpeed(TimePoint at) { |
| 579 | 600 | lockForRead(); |
| 601 | + FixType ceil = getInternalFixes().ceiling(createDummyGPSFix(at)); |
|
| 580 | 602 | try { |
| 581 | - SpeedWithBearingWithConfidence<TimePoint> estimatedSpeed = getEstimatedSpeed(at, getInternalFixes(), |
|
| 603 | + final SpeedWithBearing result; |
|
| 604 | + if (ceil != null && ceil.getTimePoint().equals(at) && ceil.isEstimatedSpeedCached()) { |
|
| 605 | + estimatedSpeedCacheHits++; |
|
| 606 | + result = ceil.getCachedEstimatedSpeed(); |
|
| 607 | + } else { |
|
| 608 | + estimatedSpeedCacheMisses++; |
|
| 609 | + SpeedWithBearingWithConfidence<TimePoint> estimatedSpeed = getEstimatedSpeed(at, getInternalFixes(), |
|
| 582 | 610 | ConfidenceFactory.INSTANCE.createExponentialTimeDifferenceWeigher( |
| 583 | 611 | // use a minimum confidence to avoid the bearing to flip to 270deg in case all is zero |
| 584 | 612 | getMillisecondsOverWhichToAverageSpeed()/2, /* minimumConfidence */ 0.00000001)); // half confidence if half averaging interval apart |
| 585 | - return estimatedSpeed == null ? null : estimatedSpeed.getObject(); |
|
| 613 | + result = estimatedSpeed == null ? null : estimatedSpeed.getObject(); |
|
| 614 | + if (estimatedSpeed != null) { |
|
| 615 | + if (ceil != null && ceil.getTimePoint().equals(at)) { |
|
| 616 | + ceil.cacheEstimatedSpeed(result); |
|
| 617 | + } |
|
| 618 | + } |
|
| 619 | + } |
|
| 620 | + if (logger.isLoggable(Level.FINE) && (estimatedSpeedCacheHits + estimatedSpeedCacheMisses) % 1000 == 0) { |
|
| 621 | + logger.fine("estimated speed cache hits/misses: "+estimatedSpeedCacheHits+"/"+estimatedSpeedCacheMisses); |
|
| 622 | + } |
|
| 623 | + return result; |
|
| 586 | 624 | } finally { |
| 587 | 625 | unlockAfterRead(); |
| 588 | 626 | } |
| ... | ... | @@ -769,6 +807,9 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 769 | 807 | * |
| 770 | 808 | * But even for a track with {@link GPSFixMoving} fixes this is a good algorithm because in case the speed changes |
| 771 | 809 | * significantly between fixes, it is important to know the next fix to understand and consider the trend. |
| 810 | + * |
|
| 811 | + * @see #getMillisecondsOverWhichToAverage() |
|
| 812 | + * @see #getMillisecondsOverWhichToAverageSpeed() |
|
| 772 | 813 | */ |
| 773 | 814 | protected List<FixType> getFixesRelevantForSpeedEstimation(TimePoint at, NavigableSet<FixType> fixesToUseForSpeedEstimation) { |
| 774 | 815 | lockForRead(); |
| ... | ... | @@ -858,7 +899,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 858 | 899 | } |
| 859 | 900 | |
| 860 | 901 | /** |
| 861 | - * When redefining this method, make sure to redefine {@link #invalidateValidityAndDistanceCaches(GPSFix)} |
|
| 902 | + * When redefining this method, make sure to redefine {@link #invalidateValidityAndEstimatedSpeedAndDistanceCaches(GPSFix)} |
|
| 862 | 903 | * accordingly. This implementation checks the immediate previous and next fix for <code>e</code>. Therefore, when |
| 863 | 904 | * adding a fix, only immediately adjacent fix's validity caches need to be invalidated. |
| 864 | 905 | * <p> |
| ... | ... | @@ -875,7 +916,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 875 | 916 | isValid = true; |
| 876 | 917 | } else { |
| 877 | 918 | if (e.isValidityCached()) { |
| 878 | - isValid = e.isValid(); |
|
| 919 | + isValid = e.isValidCached(); |
|
| 879 | 920 | } else { |
| 880 | 921 | FixType previous = rawFixes.lower(e); |
| 881 | 922 | final boolean atLeastOnePreviousFixInRange = previous != null && e.getTimePoint().asMillis() - previous.getTimePoint().asMillis() <= getMillisecondsOverWhichToAverageSpeed(); |
| ... | ... | @@ -918,7 +959,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 918 | 959 | * of the <code>gpsFix</code> "upwards." However, if the adjacent earlier fixes have changed their validity by the addition |
| 919 | 960 | * of <code>gpsFix</code>, the distance cache must be invalidated starting with the first fix whose validity changed. |
| 920 | 961 | */ |
| 921 | - protected void invalidateValidityAndDistanceCaches(FixType gpsFix) { |
|
| 962 | + protected void invalidateValidityAndEstimatedSpeedAndDistanceCaches(FixType gpsFix) { |
|
| 922 | 963 | assertWriteLock(); |
| 923 | 964 | TimePoint distanceCacheInvalidationStart = gpsFix.getTimePoint(); |
| 924 | 965 | // see also bug 968: cache entries for intervals ending after the last fix need to be removed because they are |
| ... | ... | @@ -930,6 +971,10 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 930 | 971 | distanceCacheInvalidationStart = last.getTimePoint().plus(1); // add one millisecond to invalidate *after* the last fix only |
| 931 | 972 | } |
| 932 | 973 | gpsFix.invalidateCache(); |
| 974 | + for (FixType fixOnWhichToInvalidateEstimatedSpeed : getFixesRelevantForSpeedEstimation(gpsFix.getTimePoint(), |
|
| 975 | + getInternalRawFixes())) { |
|
| 976 | + fixOnWhichToInvalidateEstimatedSpeed.invalidateEstimatedSpeedCache(); |
|
| 977 | + } |
|
| 933 | 978 | Iterable<FixType> lowers = getEarlierFixesWhoseValidityMayBeAffected(gpsFix); |
| 934 | 979 | for (FixType lower : lowers) { |
| 935 | 980 | boolean lowerWasValid = isValid(getRawFixes(), lower); |
| ... | ... | @@ -1014,7 +1059,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl |
| 1014 | 1059 | try { |
| 1015 | 1060 | firstFixInTrack = getRawFixes().isEmpty(); |
| 1016 | 1061 | result = addWithoutLocking(fix); |
| 1017 | - invalidateValidityAndDistanceCaches(fix); |
|
| 1062 | + invalidateValidityAndEstimatedSpeedAndDistanceCaches(fix); |
|
| 1018 | 1063 | } finally { |
| 1019 | 1064 | unlockAfterWrite(); |
| 1020 | 1065 | } |
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java
| ... | ... | @@ -257,7 +257,7 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener { |
| 257 | 257 | @Override
|
| 258 | 258 | public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
|
| 259 | 259 | clearCaches(); // necessary because the competitor tracks ordered by rank may change for legs adjacent to the waypoint added
|
| 260 | - }
|
|
| 260 | + } // and because the leg's from/to waypoints may have changed
|
|
| 261 | 261 | |
| 262 | 262 | @Override
|
| 263 | 263 | public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) {
|
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
| ... | ... | @@ -220,7 +220,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl |
| 220 | 220 | * them. |
| 221 | 221 | */ |
| 222 | 222 | private transient SmartFutureCache<Competitor, com.sap.sse.common.Util.Triple<TimePoint, TimePoint, List<Maneuver>>, EmptyUpdateInterval> maneuverCache; |
| 223 | - |
|
| 223 | + |
|
| 224 | 224 | private transient Map<TimePoint, Future<Wind>> directionFromStartToNextMarkCache; |
| 225 | 225 | |
| 226 | 226 | private final ConcurrentHashMap<Mark, GPSFixTrack<Mark, GPSFix>> markTracks; |
| ... | ... | @@ -506,7 +506,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl |
| 506 | 506 | } |
| 507 | 507 | }, /* nameForLocks */"Maneuver cache for race " + getRace().getName()); |
| 508 | 508 | } |
| 509 | - |
|
| 509 | + |
|
| 510 | 510 | /** |
| 511 | 511 | * Precondition: race has already been set, e.g., in constructor before this method is called |
| 512 | 512 | */ |
java/com.sap.sailing.domain/src/com/sap/sailing/util/SmartFutureCache.java
| ... | ... | @@ -507,7 +507,7 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> { |
| 507 | 507 | * from the cache straight away (<code>waitForLatest==false</code>) or, if a re-calculation for the <code>key</code> is still
|
| 508 | 508 | * ongoing, the result of that ongoing re-calculation is returned.
|
| 509 | 509 | */
|
| 510 | - public V get(K key, boolean waitForLatest) {
|
|
| 510 | + public V get(final K key, boolean waitForLatest) {
|
|
| 511 | 511 | V value = null;
|
| 512 | 512 | if (waitForLatest) {
|
| 513 | 513 | FutureTaskWithCancelBlocking future;
|
| ... | ... | @@ -545,11 +545,12 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> { |
| 545 | 545 | }
|
| 546 | 546 | } // else no calculation currently going on; value has been fetched from latest cache entry
|
| 547 | 547 | } else {
|
| 548 | - LockUtil.lockForRead(getOrCreateLockForKey(key));
|
|
| 548 | + final NamedReentrantReadWriteLock lock = getOrCreateLockForKey(key);
|
|
| 549 | + LockUtil.lockForRead(lock);
|
|
| 549 | 550 | try {
|
| 550 | 551 | value = cache.get(key);
|
| 551 | 552 | } finally {
|
| 552 | - LockUtil.unlockAfterRead(getOrCreateLockForKey(key));
|
|
| 553 | + LockUtil.unlockAfterRead(lock);
|
|
| 553 | 554 | }
|
| 554 | 555 | }
|
| 555 | 556 | return value;
|
| ... | ... | @@ -576,4 +577,11 @@ public class SmartFutureCache<K, V, U extends UpdateInterval<U>> { |
| 576 | 577 | return smartFutureCacheTaskReuseCounter;
|
| 577 | 578 | }
|
| 578 | 579 | |
| 580 | + /**
|
|
| 581 | + * Removes the key from the cache. If any updates are still running, they may again insert the key into hte cache.
|
|
| 582 | + */
|
|
| 583 | + public void remove(K key) {
|
|
| 584 | + cache(key, null);
|
|
| 585 | + }
|
|
| 586 | +
|
|
| 579 | 587 | }
|
java/com.sap.sailing.gwt.ui.test/.classpath
| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | -<?xml version="1.0" encoding="UTF-8"?>
|
|
| 2 | -<classpath>
|
|
| 3 | - <classpathentry kind="src" path="src"/>
|
|
| 4 | - <classpathentry kind="src" path="resources"/>
|
|
| 5 | - <classpathentry exported="true" kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/>
|
|
| 6 | - <classpathentry exported="true" kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
|
| 7 | - <classpathentry exported="true" kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
|
|
| 8 | - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
|
| 9 | - <classpathentry kind="output" path="bin"/>
|
|
| 10 | -</classpath>
|
|
| 1 | +<?xml version="1.0" encoding="UTF-8"?> |
|
| 2 | +<classpath> |
|
| 3 | + <classpathentry kind="src" path="src"/> |
|
| 4 | + <classpathentry kind="src" path="resources"/> |
|
| 5 | + <classpathentry exported="true" kind="con" path="com.google.gwt.eclipse.core.GWT_CONTAINER"/> |
|
| 6 | + <classpathentry exported="true" kind="con" path="org.eclipse.pde.core.requiredPlugins"/> |
|
| 7 | + <classpathentry exported="true" kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/> |
|
| 8 | + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> |
|
| 9 | + <classpathentry kind="output" path="bin"/> |
|
| 10 | +</classpath> |
java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PolarSheetGenerationServiceTest.java
| ... | ... | @@ -209,7 +209,7 @@ public class PolarSheetGenerationServiceTest { |
| 209 | 209 | }
|
| 210 | 210 | |
| 211 | 211 | @Override
|
| 212 | - public boolean isValid() {
|
|
| 212 | + public boolean isValidCached() {
|
|
| 213 | 213 | return true;
|
| 214 | 214 | }
|
| 215 | 215 |
java/com.sap.sailing.gwt.ui/.settings/com.google.gwt.eclipse.core.prefs
| ... | ... | @@ -1,4 +1,3 @@ |
| 1 | 1 | eclipse.preferences.version=1 |
| 2 | 2 | entryPointModules= |
| 3 | 3 | filesCopiedToWebInfLib=gwt-servlet.jar |
| 4 | -gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0UgLWNvbXBpbGVSZXBvcnQgLVhzb3ljRGV0YWlsZWRdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5SYWNlQm9hcmQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuQWRtaW5Db25zb2xlPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLkxlYWRlcmJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLlNwZWN0YXRvcjwvZW50cnktcG9pbnQtbW9kdWxlPjwvZ3d0LWNvbXBpbGUtc2V0dGluZ3M+ |
java/com.sap.sailing.gwt.ui/Profile SailingGWT.launch
| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
| 2 | +<launchConfiguration type="com.sap.jvm.profiling.ui.launching.OnlineProfilingLaunchConfigurationType"> |
|
| 3 | +<booleanAttribute key="com.sap.jvm.profiling.ui.widgets.allowDebugging" value="false"/> |
|
| 4 | +<booleanAttribute key="com.sap.jvm.profiling.ui.widgets.allowTerminateVm" value="false"/> |
|
| 5 | +<stringAttribute key="com.sap.jvm.profiling.ui.widgets.hostName" value="localhost"/> |
|
| 6 | +<stringAttribute key="com.sap.jvm.profiling.ui.widgets.port" value="7999"/> |
|
| 7 | +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> |
|
| 8 | +<listEntry value="/com.sap.sailing.gwt.ui"/> |
|
| 9 | +</listAttribute> |
|
| 10 | +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> |
|
| 11 | +<listEntry value="4"/> |
|
| 12 | +</listAttribute> |
|
| 13 | +<booleanAttribute key="org.eclipse.jdt.launching.ALLOW_TERMINATE" value="false"/> |
|
| 14 | +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> |
|
| 15 | +<stringAttribute key="org.eclipse.jdt.launching.VM_CONNECTOR_ID" value="org.eclipse.jdt.launching.socketAttachConnector"/> |
|
| 16 | +<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%"/> |
|
| 17 | +<stringAttribute key="profilingTraceType-IO_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/> |
|
| 18 | +<stringAttribute key="profilingTraceType-METHOD_PARAMETER_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/> |
|
| 19 | +<stringAttribute key="profilingTraceType-NETWORK_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_RESOLVE_ALL_HOSTS%CTX_KEY%true%CTX_ENTRY%"/> |
|
| 20 | +<stringAttribute key="profilingTraceType-PERFORMANCE_HOTSPOT_TRACE" value="KEY_IGNORE_SLEEPING_THREADS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%true%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/> |
|
| 21 | +<stringAttribute key="profilingTraceType-SYNCHRONIZATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/> |
|
| 22 | +</launchConfiguration> |
java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch
| ... | ... | @@ -0,0 +1,60 @@ |
| 1 | +<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
| 2 | +<launchConfiguration type="com.google.gdt.eclipse.suite.webapp"> |
|
| 3 | +<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/> |
|
| 4 | +<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/> |
|
| 5 | +<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/> |
|
| 6 | +<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES"> |
|
| 7 | +<listEntry value="com.sap.sailing.gwt.ui.VideoPopup"/> |
|
| 8 | +<listEntry value="com.sap.sailing.gwt.ui.Simulator"/> |
|
| 9 | +</listAttribute> |
|
| 10 | +<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/> |
|
| 11 | +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> |
|
| 12 | +<listEntry value="/com.sap.sailing.gwt.ui"/> |
|
| 13 | +</listAttribute> |
|
| 14 | +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> |
|
| 15 | +<listEntry value="4"/> |
|
| 16 | +</listAttribute> |
|
| 17 | +<listAttribute key="org.eclipse.debug.ui.favoriteGroups"> |
|
| 18 | +<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> |
|
| 19 | +<listEntry value="org.eclipse.debug.ui.launchGroup.run"/> |
|
| 20 | +</listAttribute> |
|
| 21 | +<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> |
|
| 22 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6" javaProject="com.sap.sailing.gwt.ui" path="1" type="4"/> "/> |
|
| 23 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
|
| 24 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/> |
|
| 25 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/> |
|
| 26 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/> |
|
| 27 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/> |
|
| 28 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/> |
|
| 29 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/> |
|
| 30 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/> |
|
| 31 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/> |
|
| 32 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/> |
|
| 33 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/> |
|
| 34 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/> |
|
| 35 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.mongodb/src" path="3" type="2"/> "/> |
|
| 36 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/> |
|
| 37 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/> |
|
| 38 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/> |
|
| 39 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.operationaltransformation/src" path="3" type="2"/> "/> |
|
| 40 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/> |
|
| 41 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/> |
|
| 42 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/> |
|
| 43 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server.replication/src" path="3" type="2"/> "/> |
|
| 44 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.datamining.shared/src" path="3" type="2"/> "/> |
|
| 45 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/> |
|
| 46 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.datamining.shared/src" path="3" type="2"/> "/> |
|
| 47 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/> |
|
| 48 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/> |
|
| 49 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/> |
|
| 50 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.common/src" path="3" type="2"/> "/> |
|
| 51 | +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sse.gwt/src" path="3" type="2"/> "/> |
|
| 52 | +</listAttribute> |
|
| 53 | +<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/> |
|
| 54 | +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> |
|
| 55 | +<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sapjvm7"/> |
|
| 56 | +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> |
|
| 57 | +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.ui.Search -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.VideoPopup"/> |
|
| 58 | +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> |
|
| 59 | +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m -Xdebug -Xrunjdwp:transport=dt_socket,address=7999,server=y"/> |
|
| 60 | +</launchConfiguration> |
java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling.launch
| ... | ... | @@ -1,52 +0,0 @@ |
| 1 | -<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
| 2 | -<launchConfiguration type="com.google.gdt.eclipse.suite.webapp"> |
|
| 3 | -<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/> |
|
| 4 | -<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/> |
|
| 5 | -<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/> |
|
| 6 | -<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES"> |
|
| 7 | -<listEntry value="com.sap.sailing.gwt.ui.RegattaOverview"/> |
|
| 8 | -</listAttribute> |
|
| 9 | -<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/> |
|
| 10 | -<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> |
|
| 11 | -<listEntry value="/com.sap.sailing.gwt.ui"/> |
|
| 12 | -</listAttribute> |
|
| 13 | -<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> |
|
| 14 | -<listEntry value="4"/> |
|
| 15 | -</listAttribute> |
|
| 16 | -<listAttribute key="org.eclipse.debug.ui.favoriteGroups"> |
|
| 17 | -<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/> |
|
| 18 | -<listEntry value="org.eclipse.debug.ui.launchGroup.run"/> |
|
| 19 | -</listAttribute> |
|
| 20 | -<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> |
|
| 21 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sapjvm_7" path="1" type="4"/> "/> |
|
| 22 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/resources" path="3" type="2"/> "/> |
|
| 23 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.gwt.ui/src/main/java" path="3" type="2"/> "/> |
|
| 24 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain/src" path="3" type="2"/> "/> |
|
| 25 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.googlecode.java-diff-utils/src" path="3" type="2"/> "/> |
|
| 26 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.common/src" path="3" type="2"/> "/> |
|
| 27 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.json.simple/src" path="3" type="2"/> "/> |
|
| 28 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server/src" path="3" type="2"/> "/> |
|
| 29 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter/src" path="3" type="2"/> "/> |
|
| 30 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.expeditionconnector/src" path="3" type="2"/> "/> |
|
| 31 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/src" path="3" type="2"/> "/> |
|
| 32 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.declination/resources" path="3" type="2"/> "/> |
|
| 33 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.udpconnector/src" path="3" type="2"/> "/> |
|
| 34 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.mongodb/src" path="3" type="2"/> "/> |
|
| 35 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter/src" path="3" type="2"/> "/> |
|
| 36 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.persistence/src" path="3" type="2"/> "/> |
|
| 37 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.swisstimingadapter.persistence/src" path="3" type="2"/> "/> |
|
| 38 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.operationaltransformation/src" path="3" type="2"/> "/> |
|
| 39 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.domain.tractracadapter.persistence/src" path="3" type="2"/> "/> |
|
| 40 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/org.moxieapps.gwt.highcharts/src" path="3" type="2"/> "/> |
|
| 41 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.geocoding/src" path="3" type="2"/> "/> |
|
| 42 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.sap.sailing.server.replication/src" path="3" type="2"/> "/> |
|
| 43 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/com.google.gwt.osgi/lib/gwt-user.jar" path="3" type="2"/> "/> |
|
| 44 | -<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="com.sap.sailing.gwt.ui"/> </runtimeClasspathEntry> "/> |
|
| 45 | -</listAttribute> |
|
| 46 | -<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/> |
|
| 47 | -<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> |
|
| 48 | -<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> |
|
| 49 | -<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview"/> |
|
| 50 | -<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/> |
|
| 51 | -<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx1024m -Xdebug -Xrunjdwp:transport=dt_socket,address=7999,server=y"/> |
|
| 52 | -</launchConfiguration> |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/BoatClassImageResolver.java
| ... | ... | @@ -29,6 +29,7 @@ public class BoatClassImageResolver { |
| 29 | 29 | boatClassIconsMap.put(BoatClassMasterdata.DRAGON_INT.getDisplayName(), imageResources.DragonIcon()); |
| 30 | 30 | boatClassIconsMap.put(BoatClassMasterdata.EUROPE_INT.getDisplayName(), imageResources.EuropeIcon()); |
| 31 | 31 | boatClassIconsMap.put(BoatClassMasterdata.EXTREME_40.getDisplayName(), imageResources.Extreme40Icon()); |
| 32 | + boatClassIconsMap.put(BoatClassMasterdata.D_35.getDisplayName(), imageResources.D35Icon()); |
|
| 32 | 33 | boatClassIconsMap.put(BoatClassMasterdata.F_16.getDisplayName(), imageResources.F16Icon()); |
| 33 | 34 | boatClassIconsMap.put(BoatClassMasterdata.F_18.getDisplayName(), imageResources.F18Icon()); |
| 34 | 35 | boatClassIconsMap.put(BoatClassMasterdata.FARR_30.getDisplayName(), imageResources.Farr30Icon()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/BoatClassImageResources.java
| ... | ... | @@ -54,7 +54,10 @@ public interface BoatClassImageResources extends ClientBundle { |
| 54 | 54 | |
| 55 | 55 | @Source("com/sap/sailing/gwt/ui/client/images/boatclass/EXTREME40.png") |
| 56 | 56 | ImageResource Extreme40Icon(); |
| 57 | - |
|
| 57 | + |
|
| 58 | + @Source("com/sap/sailing/gwt/ui/client/images/boatclass/D35.png") |
|
| 59 | + ImageResource D35Icon(); |
|
| 60 | + |
|
| 58 | 61 | @Source("com/sap/sailing/gwt/ui/client/images/boatclass/F16.png") |
| 59 | 62 | ImageResource F16Icon(); |
| 60 | 63 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/app/PlaceNavigatorImpl.java
| ... | ... | @@ -17,7 +17,7 @@ import com.sap.sailing.gwt.home.client.place.start.StartPlace; |
| 17 | 17 | |
| 18 | 18 | public class PlaceNavigatorImpl implements PlaceNavigator { |
| 19 | 19 | private final PlaceController placeController; |
| 20 | - private final static String DEFAULT_SAPSAILING_SERVER = "www.sapsailing.com"; // newhome.sapsailing.com |
|
| 20 | + private final static String DEFAULT_SAPSAILING_SERVER = "www.sapsailing.com"; |
|
| 21 | 21 | private final static String DEFAULT_SAPSAILING_SERVER_URL = "http://" + DEFAULT_SAPSAILING_SERVER; |
| 22 | 22 | |
| 23 | 23 | protected PlaceNavigatorImpl(PlaceController placeController) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/TabletAndDesktopEventView.java
| ... | ... | @@ -182,7 +182,8 @@ public class TabletAndDesktopEventView extends Composite implements EventView, E |
| 182 | 182 | linkParams.put("eventId", event.id.toString()); |
| 183 | 183 | linkParams.put("leaderboardName", leaderboardName); |
| 184 | 184 | linkParams.put("raceName", raceIdentifier.getRaceName()); |
| 185 | - linkParams.put(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, "true"); |
|
| 185 | + // TODO this must only be forwarded if there is a logged-on user |
|
| 186 | +// linkParams.put(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, "true"); |
|
| 186 | 187 | linkParams.put(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_MAPCONTROLS, "true"); |
| 187 | 188 | linkParams.put(RaceBoardViewConfiguration.PARAM_VIEW_SHOW_NAVIGATION_PANEL, "true"); |
| 188 | 189 | linkParams.put("regattaName", raceIdentifier.getRegattaName()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/events/AbstractEventsView.java
| ... | ... | @@ -12,25 +12,25 @@ import com.sap.sailing.gwt.home.client.DateUtil; |
| 12 | 12 | import com.sap.sailing.gwt.ui.shared.EventBaseDTO; |
| 13 | 13 | |
| 14 | 14 | public abstract class AbstractEventsView extends Composite implements EventsView { |
| 15 | - private final Map<Integer, List<EventBaseDTO>> recentEventsOrderedByYear; |
|
| 15 | + private final Map<Integer, List<EventBaseDTO>> recentEventsByYearOrderedByEndDate; |
|
| 16 | 16 | private final List<EventBaseDTO> upcomingEvents; |
| 17 | 17 | |
| 18 | 18 | public AbstractEventsView() { |
| 19 | - recentEventsOrderedByYear = new HashMap<Integer, List<EventBaseDTO>>(); |
|
| 19 | + recentEventsByYearOrderedByEndDate = new HashMap<Integer, List<EventBaseDTO>>(); |
|
| 20 | 20 | upcomingEvents = new ArrayList<EventBaseDTO>(); |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | 23 | @Override |
| 24 | 24 | public void setEvents(List<EventBaseDTO> events) { |
| 25 | - for(EventBaseDTO event: events) { |
|
| 26 | - if(event.startDate != null && event.endDate != null) { |
|
| 27 | - if(DateUtil.isDayInPast(event.startDate) || event.isRunning()) { |
|
| 25 | + for (EventBaseDTO event : events) { |
|
| 26 | + if (event.startDate != null && event.endDate != null) { |
|
| 27 | + if (DateUtil.isDayInPast(event.startDate) || event.isRunning()) { |
|
| 28 | 28 | // recent event or live event |
| 29 | 29 | int year = DateUtil.getYear(event.startDate); |
| 30 | - List<EventBaseDTO> eventsOfYear = recentEventsOrderedByYear.get(year); |
|
| 31 | - if(eventsOfYear == null) { |
|
| 30 | + List<EventBaseDTO> eventsOfYear = recentEventsByYearOrderedByEndDate.get(year); |
|
| 31 | + if (eventsOfYear == null) { |
|
| 32 | 32 | eventsOfYear = new ArrayList<EventBaseDTO>(); |
| 33 | - recentEventsOrderedByYear.put(year, eventsOfYear); |
|
| 33 | + recentEventsByYearOrderedByEndDate.put(year, eventsOfYear); |
|
| 34 | 34 | } |
| 35 | 35 | eventsOfYear.add(event); |
| 36 | 36 | } else { |
| ... | ... | @@ -39,8 +39,7 @@ public abstract class AbstractEventsView extends Composite implements EventsView |
| 39 | 39 | } |
| 40 | 40 | } |
| 41 | 41 | } |
| 42 | - |
|
| 43 | - for(List<EventBaseDTO> eventsPerYear: recentEventsOrderedByYear.values()) { |
|
| 42 | + for (List<EventBaseDTO> eventsPerYear : recentEventsByYearOrderedByEndDate.values()) { |
|
| 44 | 43 | Collections.sort(eventsPerYear, EVENTS_BY_DESCENDING_DATE_COMPARATOR); |
| 45 | 44 | } |
| 46 | 45 | Collections.sort(upcomingEvents, EVENTS_BY_ASCENDING_DATE_COMPARATOR); |
| ... | ... | @@ -49,8 +48,13 @@ public abstract class AbstractEventsView extends Composite implements EventsView |
| 49 | 48 | |
| 50 | 49 | protected abstract void updateEventsUI(); |
| 51 | 50 | |
| 52 | - public Map<Integer, List<EventBaseDTO>> getRecentEventsOrderedByYear() { |
|
| 53 | - return recentEventsOrderedByYear; |
|
| 51 | + /** |
|
| 52 | + * Returns a map whose keys denote years and whose values are event lists ordered by descending |
|
| 53 | + * {@link EventBaseDTO#endDate end date}. Note that the iteration order for the map entries is undefined |
|
| 54 | + * and in particular is not enumerating the years in any well-defined order. |
|
| 55 | + */ |
|
| 56 | + public Map<Integer, List<EventBaseDTO>> getRecentEventsByYearOrderedByEndDate() { |
|
| 57 | + return recentEventsByYearOrderedByEndDate; |
|
| 54 | 58 | } |
| 55 | 59 | |
| 56 | 60 | public List<EventBaseDTO> getUpcomingEvents() { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/events/TabletAndDesktopEventsView.java
| ... | ... | @@ -54,7 +54,7 @@ public class TabletAndDesktopEventsView extends AbstractEventsView { |
| 54 | 54 | |
| 55 | 55 | @Override |
| 56 | 56 | protected void updateEventsUI() { |
| 57 | - recentEventsWidget.updateEvents(getRecentEventsOrderedByYear()); |
|
| 57 | + recentEventsWidget.updateEvents(getRecentEventsByYearOrderedByEndDate()); |
|
| 58 | 58 | upcomingEventsWidget.updateEvents(getUpcomingEvents()); |
| 59 | 59 | } |
| 60 | 60 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/events/recent/EventsOverviewRecent.java
| ... | ... | @@ -1,9 +1,13 @@ |
| 1 | 1 | package com.sap.sailing.gwt.home.client.place.events.recent; |
| 2 | 2 | |
| 3 | -import java.util.Date; |
|
| 3 | +import java.util.ArrayList; |
|
| 4 | +import java.util.Collections; |
|
| 4 | 5 | import java.util.HashMap; |
| 6 | +import java.util.LinkedHashMap; |
|
| 5 | 7 | import java.util.List; |
| 8 | +import java.util.ListIterator; |
|
| 6 | 9 | import java.util.Map; |
| 10 | +import java.util.Map.Entry; |
|
| 7 | 11 | |
| 8 | 12 | import com.google.gwt.core.client.GWT; |
| 9 | 13 | import com.google.gwt.uibinder.client.UiBinder; |
| ... | ... | @@ -11,7 +15,6 @@ import com.google.gwt.uibinder.client.UiField; |
| 11 | 15 | import com.google.gwt.user.client.ui.Composite; |
| 12 | 16 | import com.google.gwt.user.client.ui.HTMLPanel; |
| 13 | 17 | import com.google.gwt.user.client.ui.Widget; |
| 14 | -import com.sap.sailing.gwt.home.client.DateUtil; |
|
| 15 | 18 | import com.sap.sailing.gwt.home.client.app.PlaceNavigator; |
| 16 | 19 | import com.sap.sailing.gwt.ui.shared.EventBaseDTO; |
| 17 | 20 | |
| ... | ... | @@ -36,37 +39,37 @@ public class EventsOverviewRecent extends Composite { |
| 36 | 39 | initWidget(uiBinder.createAndBindUi(this)); |
| 37 | 40 | } |
| 38 | 41 | |
| 39 | - public void updateEvents(Map<Integer, List<EventBaseDTO>> recentEventsOrderedByYear) { |
|
| 42 | + public void updateEvents(Map<Integer, List<EventBaseDTO>> recentEventsByYearOrderedByEndDate) { |
|
| 40 | 43 | // remove old widgets |
| 41 | 44 | recentEventsPerYearPanel.clear(); |
| 42 | 45 | recentEventsComposites.clear(); |
| 43 | - |
|
| 44 | 46 | // recent events of this year |
| 45 | - Date now = new Date(); |
|
| 46 | - int currentYear = DateUtil.getYear(now); |
|
| 47 | + LinkedHashMap<Integer, List<EventBaseDTO>> recentEventsOrderedDescendingByYearOrderedDescendingByEndDate = |
|
| 48 | + sortEventsDescendingByYear(recentEventsByYearOrderedByEndDate); |
|
| 47 | 49 | boolean oneYearIsExpanded = false; |
| 48 | - |
|
| 49 | - if(recentEventsOrderedByYear.get(currentYear) != null) { |
|
| 50 | - EventsOverviewRecentYear recentEventsOfOneYear = new EventsOverviewRecentYear(currentYear, recentEventsOrderedByYear.get(currentYear), navigator); |
|
| 50 | + for (Entry<Integer, List<EventBaseDTO>> e : recentEventsOrderedDescendingByYearOrderedDescendingByEndDate.entrySet()) { |
|
| 51 | + int currentYear = e.getKey(); |
|
| 52 | + EventsOverviewRecentYear recentEventsOfOneYear = new EventsOverviewRecentYear(currentYear, e.getValue(), navigator); |
|
| 51 | 53 | recentEventsPerYearPanel.add(recentEventsOfOneYear); |
| 52 | 54 | recentEventsComposites.put(currentYear, recentEventsOfOneYear); |
| 53 | - recentEventsOfOneYear.showContent(); |
|
| 54 | - oneYearIsExpanded = true; |
|
| 55 | + if (oneYearIsExpanded == true) { |
|
| 56 | + recentEventsOfOneYear.hideContent(); |
|
| 57 | + } else { |
|
| 58 | + recentEventsOfOneYear.showContent(); |
|
| 59 | + oneYearIsExpanded = true; |
|
| 60 | + } |
|
| 55 | 61 | } |
| 56 | - currentYear--; |
|
| 57 | - while (currentYear > 2010) { |
|
| 58 | - if(recentEventsOrderedByYear.get(currentYear) != null) { |
|
| 59 | - EventsOverviewRecentYear recentEventsOfOneYear = new EventsOverviewRecentYear(currentYear, recentEventsOrderedByYear.get(currentYear), navigator); |
|
| 60 | - recentEventsPerYearPanel.add(recentEventsOfOneYear); |
|
| 61 | - recentEventsComposites.put(currentYear, recentEventsOfOneYear); |
|
| 62 | - if(oneYearIsExpanded == true) { |
|
| 63 | - recentEventsOfOneYear.hideContent(); |
|
| 64 | - } else { |
|
| 65 | - recentEventsOfOneYear.showContent(); |
|
| 66 | - oneYearIsExpanded = true; |
|
| 67 | - } |
|
| 68 | - } |
|
| 69 | - currentYear--; |
|
| 62 | + } |
|
| 63 | + |
|
| 64 | + private LinkedHashMap<Integer, List<EventBaseDTO>> sortEventsDescendingByYear( |
|
| 65 | + Map<Integer, List<EventBaseDTO>> recentEventsByYearOrderedByEndDate) { |
|
| 66 | + LinkedHashMap<Integer, List<EventBaseDTO>> result = new LinkedHashMap<>(); |
|
| 67 | + List<Integer> yearKeysInDescendingOrder = new ArrayList<>(recentEventsByYearOrderedByEndDate.keySet()); |
|
| 68 | + Collections.sort(yearKeysInDescendingOrder); |
|
| 69 | + for (ListIterator<Integer> i=yearKeysInDescendingOrder.listIterator(yearKeysInDescendingOrder.size()); i.hasPrevious(); ) { |
|
| 70 | + Integer year = i.previous(); |
|
| 71 | + result.put(year, recentEventsByYearOrderedByEndDate.get(year)); |
|
| 70 | 72 | } |
| 73 | + return result; |
|
| 71 | 74 | } |
| 72 | 75 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/events/recent/EventsOverviewRecentYear.java
| ... | ... | @@ -37,20 +37,16 @@ public class EventsOverviewRecentYear extends Composite { |
| 37 | 37 | public EventsOverviewRecentYear(Integer year, List<EventBaseDTO> events, PlaceNavigator navigator) { |
| 38 | 38 | EventsOverviewRecentResources.INSTANCE.css().ensureInjected(); |
| 39 | 39 | initWidget(uiBinder.createAndBindUi(this)); |
| 40 | - |
|
| 41 | 40 | this.year.setInnerText(String.valueOf(year)); |
| 42 | 41 | this.eventsCount.setInnerText(String.valueOf(events.size())); |
| 43 | 42 | // this.countriesCount.setInnerText("tbd."); |
| 44 | 43 | // this.sailorsCount.setInnerText("tbd."); |
| 45 | 44 | // this.trackedRacesCount.setInnerText("tbd."); |
| 46 | - |
|
| 47 | - for(EventBaseDTO eventDTO: events) { |
|
| 48 | - RecentEvent recentEvent = new RecentEvent(navigator); |
|
| 49 | - recentEvent.setEvent(eventDTO); |
|
| 45 | + for (EventBaseDTO eventDTO : events) { |
|
| 46 | + RecentEvent recentEvent = new RecentEvent(navigator, eventDTO); |
|
| 50 | 47 | recentEventsTeaserPanel.appendChild(recentEvent.getElement()); |
| 51 | 48 | } |
| 52 | 49 | isContentVisible = true; |
| 53 | - |
|
| 54 | 50 | headerDiv.addDomHandler(new ClickHandler() { |
| 55 | 51 | @Override |
| 56 | 52 | public void onClick(ClickEvent event) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/events/upcoming/EventsOverviewUpcoming.java
| ... | ... | @@ -28,7 +28,6 @@ public class EventsOverviewUpcoming extends Composite { |
| 28 | 28 | public EventsOverviewUpcoming(PlaceNavigator navigator) { |
| 29 | 29 | this.navigator = navigator; |
| 30 | 30 | upcomingEventComposites = new ArrayList<UpcomingEvent>(); |
| 31 | - |
|
| 32 | 31 | EventsOverviewUpcomingResources.INSTANCE.css().ensureInjected(); |
| 33 | 32 | initWidget(uiBinder.createAndBindUi(this)); |
| 34 | 33 | } |
| ... | ... | @@ -36,10 +35,8 @@ public class EventsOverviewUpcoming extends Composite { |
| 36 | 35 | public void updateEvents(List<EventBaseDTO> events) { |
| 37 | 36 | eventsPlaceholder.clear(); |
| 38 | 37 | upcomingEventComposites.clear(); |
| 39 | - |
|
| 40 | - for(EventBaseDTO event: events) { |
|
| 38 | + for (EventBaseDTO event : events) { |
|
| 41 | 39 | UpcomingEvent upcomingEvent = new UpcomingEvent(event, navigator); |
| 42 | - |
|
| 43 | 40 | upcomingEventComposites.add(upcomingEvent); |
| 44 | 41 | eventsPlaceholder.getElement().appendChild(upcomingEvent.getElement()); |
| 45 | 42 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/start/SmartphoneStartView.java
| ... | ... | @@ -45,4 +45,9 @@ public class SmartphoneStartView extends Composite implements StartView { |
| 45 | 45 | mainEvents.setRecentEvents(recentEvents); |
| 46 | 46 | mainMedia.setRecentEvents(recentEvents); |
| 47 | 47 | } |
| 48 | + |
|
| 49 | + @Override |
|
| 50 | + public void adjustSizes() { |
|
| 51 | + stage.adjustSize(); |
|
| 52 | + } |
|
| 48 | 53 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/start/StartActivity.java
| ... | ... | @@ -7,9 +7,12 @@ import java.util.Date; |
| 7 | 7 | import java.util.List; |
| 8 | 8 | |
| 9 | 9 | import com.google.gwt.activity.shared.AbstractActivity; |
| 10 | +import com.google.gwt.core.client.Scheduler; |
|
| 11 | +import com.google.gwt.core.client.Scheduler.ScheduledCommand; |
|
| 10 | 12 | import com.google.gwt.event.shared.EventBus; |
| 11 | 13 | import com.google.gwt.user.client.rpc.AsyncCallback; |
| 12 | 14 | import com.google.gwt.user.client.ui.AcceptsOneWidget; |
| 15 | +import com.sap.sailing.gwt.home.client.DateUtil; |
|
| 13 | 16 | import com.sap.sailing.gwt.home.client.shared.placeholder.Placeholder; |
| 14 | 17 | import com.sap.sailing.gwt.home.client.shared.stage.StageEventType; |
| 15 | 18 | import com.sap.sailing.gwt.ui.shared.EventBaseDTO; |
| ... | ... | @@ -25,13 +28,11 @@ public class StartActivity extends AbstractActivity { |
| 25 | 28 | @Override |
| 26 | 29 | public void start(final AcceptsOneWidget panel, EventBus eventBus) { |
| 27 | 30 | panel.setWidget(new Placeholder()); |
| 28 | - |
|
| 29 | 31 | clientFactory.getSailingService().getPublicEventsOfAllSailingServers(new AsyncCallback<List<EventBaseDTO>>() { |
| 30 | 32 | @Override |
| 31 | 33 | public void onSuccess(List<EventBaseDTO> result) { |
| 32 | 34 | final StartView view = clientFactory.createStartView(); |
| 33 | 35 | panel.setWidget(view.asWidget()); |
| 34 | - |
|
| 35 | 36 | fillStartPageEvents(view, result); |
| 36 | 37 | } |
| 37 | 38 | |
| ... | ... | @@ -41,71 +42,76 @@ public class StartActivity extends AbstractActivity { |
| 41 | 42 | }); |
| 42 | 43 | } |
| 43 | 44 | |
| 44 | - @SuppressWarnings("deprecation") |
|
| 45 | - protected void fillStartPageEvents(StartView view, List<EventBaseDTO> events) { |
|
| 45 | + protected void fillStartPageEvents(final StartView view, List<EventBaseDTO> events) { |
|
| 46 | 46 | List<Pair<StageEventType, EventBaseDTO>> featuredEvents = new ArrayList<Pair<StageEventType, EventBaseDTO>>(); |
| 47 | - |
|
| 48 | 47 | List<EventBaseDTO> recentEventsOfSameYear = new ArrayList<EventBaseDTO>(); |
| 49 | 48 | List<EventBaseDTO> upcomingSoonEvents = new ArrayList<EventBaseDTO>(); |
| 50 | 49 | List<EventBaseDTO> popularEvents = new ArrayList<EventBaseDTO>(); |
| 51 | 50 | Date now = new Date(); |
| 52 | - int currentYear = now.getYear(); |
|
| 53 | - final int MAX_STAGE_EVENTS = 2; |
|
| 51 | + int currentYear = DateUtil.getYear(now); |
|
| 52 | + final int MAX_STAGE_EVENTS = 5; |
|
| 54 | 53 | final long FOUR_WEEK_IN_MS = 4L * (1000 * 60 * 60 * 24 * 7); |
| 55 | - |
|
| 56 | - for(EventBaseDTO event: events) { |
|
| 57 | - if(event.startDate != null && event.endDate != null) { |
|
| 54 | + for (EventBaseDTO event : events) { |
|
| 55 | + if (event.startDate != null && event.endDate != null) { |
|
| 58 | 56 | if (now.after(event.startDate) && now.before(event.endDate)) { |
| 59 | 57 | featuredEvents.add(new Pair<StageEventType, EventBaseDTO>(StageEventType.RUNNING, event)); |
| 60 | 58 | } else if (event.startDate.after(now) && event.startDate.getTime() - now.getTime() < FOUR_WEEK_IN_MS) { |
| 61 | 59 | upcomingSoonEvents.add(event); |
| 62 | - } else if (event.endDate.before(now) && event.endDate.getYear() == currentYear) { |
|
| 60 | + } else if (event.endDate.before(now) && DateUtil.getYear(event.endDate) == currentYear) { |
|
| 63 | 61 | recentEventsOfSameYear.add(event); |
| 64 | 62 | } |
| 65 | 63 | } |
| 66 | 64 | } |
| 67 | - |
|
| 68 | - if(featuredEvents.size() < MAX_STAGE_EVENTS) { |
|
| 65 | + if (featuredEvents.size() < MAX_STAGE_EVENTS) { |
|
| 69 | 66 | fillingUpEventsList(MAX_STAGE_EVENTS, featuredEvents, StageEventType.UPCOMING_SOON, upcomingSoonEvents); |
| 70 | 67 | } |
| 71 | - if(featuredEvents.size() < MAX_STAGE_EVENTS) { |
|
| 68 | + if (featuredEvents.size() < MAX_STAGE_EVENTS) { |
|
| 72 | 69 | fillingUpEventsList(MAX_STAGE_EVENTS, featuredEvents, StageEventType.POPULAR, popularEvents); |
| 73 | 70 | } |
| 74 | 71 | // fallback for the case we did not find any events |
| 75 | - if(featuredEvents.size() < MAX_STAGE_EVENTS) { |
|
| 72 | + if (featuredEvents.size() < MAX_STAGE_EVENTS) { |
|
| 76 | 73 | fillingUpEventsList(MAX_STAGE_EVENTS, featuredEvents, StageEventType.POPULAR, recentEventsOfSameYear); |
| 77 | 74 | } |
| 78 | - |
|
| 79 | 75 | Collections.sort(featuredEvents, new FeaturedEventsComparator()); |
| 80 | - |
|
| 81 | 76 | view.setFeaturedEvents(featuredEvents); |
| 82 | 77 | view.setRecentEvents(recentEventsOfSameYear); |
| 78 | + // See bug 2232: the stage image sizes are scaled incorrectly. https://github.com/ubilabs/sap-sailing-analytics/issues/421 and |
|
| 79 | + // http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=2232 have the details. A quick fix may be to send a resize event |
|
| 80 | + // after everything has been rendered. |
|
| 81 | + Scheduler.get().scheduleDeferred(new ScheduledCommand() { |
|
| 82 | + @Override |
|
| 83 | + public void execute() { |
|
| 84 | + view.adjustSizes(); |
|
| 85 | + } |
|
| 86 | + }); |
|
| 83 | 87 | } |
| 84 | 88 | |
| 85 | - private void fillingUpEventsList(int maxAmountOfElements, List<Pair<StageEventType, EventBaseDTO>> resultList, StageEventType type, List<EventBaseDTO> listToTakeElementsFrom) { |
|
| 89 | + private void fillingUpEventsList(int maxAmountOfElements, List<Pair<StageEventType, EventBaseDTO>> resultList, |
|
| 90 | + StageEventType type, List<EventBaseDTO> listToTakeElementsFrom) { |
|
| 86 | 91 | int maxElementsToFill = maxAmountOfElements - resultList.size(); |
| 87 | - int elementsToTransfer = listToTakeElementsFrom.size() > maxElementsToFill ? maxElementsToFill : listToTakeElementsFrom.size(); |
|
| 88 | - for(int i = 0; i < elementsToTransfer; i++) { |
|
| 92 | + int elementsToTransfer = listToTakeElementsFrom.size() > maxElementsToFill ? maxElementsToFill |
|
| 93 | + : listToTakeElementsFrom.size(); |
|
| 94 | + for (int i = 0; i < elementsToTransfer; i++) { |
|
| 89 | 95 | resultList.add(new Pair<StageEventType, EventBaseDTO>(type, listToTakeElementsFrom.get(i))); |
| 90 | 96 | } |
| 91 | 97 | } |
| 92 | - |
|
| 98 | + |
|
| 93 | 99 | /** |
| 94 | - * Comparator for sorting a pair of event and stageType first by order number of stage type and then by event start date. |
|
| 100 | + * Comparator for sorting a pair of event and stageType first by order number of stage type and then by event start |
|
| 101 | + * date. |
|
| 102 | + * |
|
| 95 | 103 | * @author Frank |
| 96 | - * |
|
| 97 | 104 | */ |
| 98 | 105 | private class FeaturedEventsComparator implements Comparator<Pair<StageEventType, EventBaseDTO>> { |
| 99 | - |
|
| 100 | 106 | @Override |
| 101 | - public int compare(Pair<StageEventType, EventBaseDTO> eventAndStageType1, Pair<StageEventType, EventBaseDTO> eventAndStageType2) { |
|
| 107 | + public int compare(Pair<StageEventType, EventBaseDTO> eventAndStageType1, |
|
| 108 | + Pair<StageEventType, EventBaseDTO> eventAndStageType2) { |
|
| 102 | 109 | int result; |
| 103 | - if(eventAndStageType1.getA().ordinal() == eventAndStageType2.getA().ordinal()) { |
|
| 104 | - result = eventAndStageType1.getB().startDate.compareTo(eventAndStageType2.getB().startDate); |
|
| 110 | + if (eventAndStageType1.getA().ordinal() == eventAndStageType2.getA().ordinal()) { |
|
| 111 | + result = eventAndStageType1.getB().startDate.compareTo(eventAndStageType2.getB().startDate); |
|
| 105 | 112 | } else { |
| 106 | - result = eventAndStageType1.getA().ordinal() - eventAndStageType2.getA().ordinal(); |
|
| 113 | + result = eventAndStageType1.getA().ordinal() - eventAndStageType2.getA().ordinal(); |
|
| 107 | 114 | } |
| 108 | - |
|
| 109 | 115 | return result; |
| 110 | 116 | } |
| 111 | 117 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/start/StartView.java
| ... | ... | @@ -13,4 +13,6 @@ public interface StartView { |
| 13 | 13 | void setFeaturedEvents(List<Pair<StageEventType, EventBaseDTO>> featuredEvents); |
| 14 | 14 | |
| 15 | 15 | void setRecentEvents(List<EventBaseDTO> recentEvents); |
| 16 | + |
|
| 17 | + void adjustSizes(); |
|
| 16 | 18 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/start/TabletAndDesktopStartView.java
| ... | ... | @@ -47,4 +47,9 @@ public class TabletAndDesktopStartView extends Composite implements StartView { |
| 47 | 47 | mainMedia.setRecentEvents(recentEvents); |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | + @Override |
|
| 51 | + public void adjustSizes() { |
|
| 52 | + stage.adjustSize(); |
|
| 53 | + } |
|
| 54 | + |
|
| 50 | 55 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/leaderboard/LeaderboardViewer.java
| ... | ... | @@ -43,8 +43,8 @@ public class LeaderboardViewer extends AbstractLeaderboardViewer { |
| 43 | 43 | super(competitorSelectionModel, asyncActionsExecutor, timer, stringMessages, new LeaderboardPanel( |
| 44 | 44 | sailingService, asyncActionsExecutor, leaderboardSettings, preselectedRace, |
| 45 | 45 | competitorSelectionModel, timer, leaderboardGroupName, leaderboardName, errorReporter, |
| 46 | - stringMessages, userAgent, showRaceDetails, /* raceTimesInfoProvider */null, /*showSelectionCheckbox*/false, null, |
|
| 47 | - autoExpandLastRaceColumn, /* adjustTimerDelay */true, false, false)); |
|
| 46 | + stringMessages, userAgent, showRaceDetails, /* competitorSearchTextBox */ null, /* showSelectionCheckbox */ true, /* raceTimesInfoProvider */null, autoExpandLastRaceColumn, /* adjustTimerDelay */ |
|
| 47 | + true, false, false)); |
|
| 48 | 48 | final FlowPanel mainPanel = new FlowPanel(); |
| 49 | 49 | setWidget(mainPanel); |
| 50 | 50 | |
| ... | ... | @@ -61,8 +61,8 @@ public class LeaderboardViewer extends AbstractLeaderboardViewer { |
| 61 | 61 | overallLeaderboardPanel = new LeaderboardPanel(sailingService, asyncActionsExecutor, |
| 62 | 62 | leaderboardSettings, preselectedRace, competitorSelectionProvider, timer, |
| 63 | 63 | leaderboardGroupName, overallLeaderboardName, errorReporter, stringMessages, userAgent, |
| 64 | - /*showRaceDetails*/false, null, /*showSelectionCheckbox*/true, null, /*autoExpandLastRaceColumn*/false, |
|
| 65 | - /*adjustTimerDelay*/true, /*autoApplyTopNFilter*/false, false); |
|
| 64 | + false, /* competitorSearchTextBox */ null, /* showSelectionCheckbox */ true, /* raceTimesInfoProvider */null, false, |
|
| 65 | + /* adjustTimerDelay */ true, /*autoApplyTopNFilter*/false, false); |
|
| 66 | 66 | mainPanel.add(overallLeaderboardPanel); |
| 67 | 67 | addComponentToNavigationMenu(overallLeaderboardPanel, true, stringMessages.seriesLeaderboard(), |
| 68 | 68 | /* hasSettingsWhenComponentIsInvisible*/ true); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/leaderboard/MetaLeaderboardViewer.java
| ... | ... | @@ -42,7 +42,8 @@ public class MetaLeaderboardViewer extends AbstractLeaderboardViewer { |
| 42 | 42 | super(competitorSelectionModel, asyncActionsExecutor, timer, stringMessages, new LeaderboardPanel(sailingService, asyncActionsExecutor, |
| 43 | 43 | leaderboardSettings, preselectedRace, competitorSelectionModel, timer, |
| 44 | 44 | leaderboardGroupName, metaLeaderboardName, errorReporter, stringMessages, userAgent, |
| 45 | - showRaceDetails, /* raceTimesInfoProvider */null, false, null, autoExpandLastRaceColumn, /* adjustTimerDelay */ true, false, false)); |
|
| 45 | + showRaceDetails, /* competitorSearchTextBox */ null, /* showSelectionCheckbox */ true, /* raceTimesInfoProvider */null, autoExpandLastRaceColumn, |
|
| 46 | + /* adjustTimerDelay */ true, /*autoApplyTopNFilter*/false, false)); |
|
| 46 | 47 | final FlowPanel mainPanel = new FlowPanel(); |
| 47 | 48 | setWidget(mainPanel); |
| 48 | 49 | final Label overallStandingsLabel = new Label(stringMessages.overallStandings()); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainevents/MainEvents.java
| ... | ... | @@ -39,25 +39,17 @@ public class MainEvents extends Composite { |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | 41 | public void setRecentEvents(List<EventBaseDTO> theRecentEvents) { |
| 42 | + final int MAX_RECENT_EVENTS_ON_HOME_PAGE = 3; |
|
| 42 | 43 | recentEventsDiv.removeAllChildren(); |
| 43 | 44 | recentEvents.clear(); |
| 44 | 45 | recentEvents.addAll(theRecentEvents); |
| 45 | - |
|
| 46 | - int size = recentEvents.size(); |
|
| 47 | - if(size > 0) { |
|
| 48 | - createRecentEvent(recentEvents.get(0)); |
|
| 49 | - } |
|
| 50 | - if(size > 1) { |
|
| 51 | - createRecentEvent(recentEvents.get(1)); |
|
| 52 | - } |
|
| 53 | - if(size > 2) { |
|
| 54 | - createRecentEvent(recentEvents.get(2)); |
|
| 46 | + for (int i=0; i<recentEvents.size() && i<MAX_RECENT_EVENTS_ON_HOME_PAGE; i++) { |
|
| 47 | + createRecentEvent(recentEvents.get(i)); |
|
| 55 | 48 | } |
| 56 | 49 | } |
| 57 | 50 | |
| 58 | 51 | private void createRecentEvent(EventBaseDTO eventBase) { |
| 59 | - RecentEvent event = new RecentEvent(navigator); |
|
| 60 | - event.setEvent(eventBase); |
|
| 52 | + RecentEvent event = new RecentEvent(navigator, eventBase); |
|
| 61 | 53 | recentEventsDiv.appendChild(event.getElement()); |
| 62 | 54 | } |
| 63 | 55 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java
| ... | ... | @@ -6,6 +6,8 @@ import java.util.List; |
| 6 | 6 | import java.util.Random; |
| 7 | 7 | |
| 8 | 8 | import com.google.gwt.core.client.GWT; |
| 9 | +import com.google.gwt.core.client.Scheduler; |
|
| 10 | +import com.google.gwt.core.client.Scheduler.ScheduledCommand; |
|
| 9 | 11 | import com.google.gwt.dom.client.DivElement; |
| 10 | 12 | import com.google.gwt.event.dom.client.ClickEvent; |
| 11 | 13 | import com.google.gwt.uibinder.client.UiBinder; |
| ... | ... | @@ -24,6 +26,8 @@ import com.sap.sse.common.Util.Pair; |
| 24 | 26 | |
| 25 | 27 | public class MainMedia extends Composite { |
| 26 | 28 | |
| 29 | + private static final int MAX_VIDEO_COUNT = 3; |
|
| 30 | + |
|
| 27 | 31 | private static final MainMediaResources.LocalCss STYLES = MainMediaResources.INSTANCE.css(); |
| 28 | 32 | |
| 29 | 33 | @UiField HTMLPanel videosPanel; |
| ... | ... | @@ -48,7 +52,7 @@ public class MainMedia extends Composite { |
| 48 | 52 | |
| 49 | 53 | public void setFeaturedEvents(List<Pair<StageEventType, EventBaseDTO>> featuredEvents) { |
| 50 | 54 | for(Pair<StageEventType, EventBaseDTO> featuredEventTypeAndEvent: featuredEvents) { |
| 51 | - if(featuredEventTypeAndEvent.getB().getVideoURLs().size() > 0 && videoCounter < 3) { |
|
| 55 | + if(featuredEventTypeAndEvent.getB().getVideoURLs().size() > 0 && videoCounter < MAX_VIDEO_COUNT) { |
|
| 52 | 56 | addVideoToVideoPanel(featuredEventTypeAndEvent.getB()); |
| 53 | 57 | } |
| 54 | 58 | } |
| ... | ... | @@ -56,31 +60,35 @@ public class MainMedia extends Composite { |
| 56 | 60 | |
| 57 | 61 | public void setRecentEvents(List<EventBaseDTO> recentEvents) { |
| 58 | 62 | List<String> photoGalleryUrls = new ArrayList<String>(); |
| 59 | - |
|
| 60 | - for(EventBaseDTO event: recentEvents) { |
|
| 63 | + for (EventBaseDTO event : recentEvents) { |
|
| 61 | 64 | photoGalleryUrls.addAll(event.getPhotoGalleryImageURLs()); |
| 62 | - |
|
| 63 | - if(event.getVideoURLs().size() > 0 && videoCounter < 3) { |
|
| 65 | + if (!event.getVideoURLs().isEmpty() && videoCounter < MAX_VIDEO_COUNT) { |
|
| 64 | 66 | addVideoToVideoPanel(event); |
| 65 | 67 | } |
| 66 | 68 | } |
| 67 | - |
|
| 68 | 69 | // shuffle the image url list (Remark: Collections.shuffle() is not implemented in GWT) |
| 69 | 70 | int gallerySize = photoGalleryUrls.size(); |
| 70 | - Random random = new Random(gallerySize); |
|
| 71 | - for(int i = 0; i < gallerySize; i++) { |
|
| 72 | - Collections.swap(photoGalleryUrls, i, random.nextInt(gallerySize)); |
|
| 71 | + Random random = new Random(gallerySize); |
|
| 72 | + for (int i = 0; i < gallerySize; i++) { |
|
| 73 | + Collections.swap(photoGalleryUrls, i, random.nextInt(gallerySize)); |
|
| 73 | 74 | } |
| 74 | - |
|
| 75 | - for(String url : photoGalleryUrls) { |
|
| 75 | + for (String url : photoGalleryUrls) { |
|
| 76 | 76 | SimplePanel imageContainer = new SimplePanel(); |
| 77 | 77 | imageContainer.addStyleName(STYLES.media_swiperslide()); |
| 78 | 78 | String image = "url(" + url + ")"; |
| 79 | 79 | imageContainer.getElement().getStyle().setBackgroundImage(image); |
| 80 | 80 | mediaSlides.add(imageContainer); |
| 81 | 81 | } |
| 82 | - |
|
| 83 | 82 | this.swiper = Swiper.createWithLoopOption(STYLES.media_swipecontainer(), STYLES.media_swipewrapper(), STYLES.media_swiperslide()); |
| 83 | + // See bug 2232: the stage image sizes are scaled incorrectly. https://github.com/ubilabs/sap-sailing-analytics/issues/421 and |
|
| 84 | + // http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=2232 have the details. A quick fix may be to send a resize event |
|
| 85 | + // after everything has been rendered. |
|
| 86 | + Scheduler.get().scheduleDeferred(new ScheduledCommand() { |
|
| 87 | + @Override |
|
| 88 | + public void execute() { |
|
| 89 | + swiper.reInit(); |
|
| 90 | + } |
|
| 91 | + }); |
|
| 84 | 92 | } |
| 85 | 93 | |
| 86 | 94 | private void addVideoToVideoPanel(EventBaseDTO event) { |
| ... | ... | @@ -95,19 +103,12 @@ public class MainMedia extends Composite { |
| 95 | 103 | } |
| 96 | 104 | |
| 97 | 105 | private String getRandomVideoURL(EventBaseDTO event) { |
| 98 | - String result = null; |
|
| 99 | - int videosCount = event.getVideoURLs().size(); |
|
| 100 | - if(videosCount > 0) { |
|
| 101 | - if(videosCount == 1) { |
|
| 102 | - result = event.getVideoURLs().get(0); |
|
| 103 | - } else { |
|
| 104 | - List<String> videoUrls = new ArrayList<String>(event.getVideoURLs()); |
|
| 105 | - Random random = new Random(videosCount); |
|
| 106 | - for(int i = 0; i < videosCount; i++) { |
|
| 107 | - Collections.swap(videoUrls, i, random.nextInt(videosCount)); |
|
| 108 | - } |
|
| 109 | - result = videoUrls.get(0); |
|
| 110 | - } |
|
| 106 | + final String result; |
|
| 107 | + List<String> videoURLs = event.getVideoURLs(); |
|
| 108 | + if (!videoURLs.isEmpty()) { |
|
| 109 | + result = event.getVideoURLs().get(new Random(videoURLs.size()).nextInt(videoURLs.size())); |
|
| 110 | + } else { |
|
| 111 | + result = null; |
|
| 111 | 112 | } |
| 112 | 113 | return result; |
| 113 | 114 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java
| ... | ... | @@ -29,7 +29,7 @@ public class RecentEvent extends UIObject { |
| 29 | 29 | @UiField ImageElement eventImage; |
| 30 | 30 | @UiField DivElement isLiveDiv; |
| 31 | 31 | |
| 32 | - private EventBaseDTO event; |
|
| 32 | + private final EventBaseDTO event; |
|
| 33 | 33 | |
| 34 | 34 | private final String defaultImageUrl = "http://static.sapsailing.com/ubilabsimages/default/default_event_photo.jpg"; |
| 35 | 35 | |
| ... | ... | @@ -38,11 +38,10 @@ public class RecentEvent extends UIObject { |
| 38 | 38 | |
| 39 | 39 | private static RecentEventUiBinder uiBinder = GWT.create(RecentEventUiBinder.class); |
| 40 | 40 | |
| 41 | - public RecentEvent(final PlaceNavigator navigator) { |
|
| 41 | + public RecentEvent(final PlaceNavigator navigator, final EventBaseDTO event) { |
|
| 42 | + this.event = event; |
|
| 42 | 43 | RecentEventResources.INSTANCE.css().ensureInjected(); |
| 43 | - |
|
| 44 | 44 | setElement(uiBinder.createAndBindUi(this)); |
| 45 | - |
|
| 46 | 45 | Event.sinkEvents(eventOverviewLink, Event.ONCLICK); |
| 47 | 46 | Event.setEventListener(eventOverviewLink, new EventListener() { |
| 48 | 47 | @Override |
| ... | ... | @@ -54,27 +53,19 @@ public class RecentEvent extends UIObject { |
| 54 | 53 | } |
| 55 | 54 | } |
| 56 | 55 | }); |
| 57 | - |
|
| 58 | - } |
|
| 59 | - |
|
| 60 | - public void setEvent(EventBaseDTO event) { |
|
| 61 | - this.event = event; |
|
| 62 | 56 | updateUI(); |
| 63 | 57 | } |
| 64 | 58 | |
| 65 | 59 | private void updateUI() { |
| 66 | 60 | SafeHtml safeHtmlEventName = LongNamesUtil.breakLongName(event.getName()); |
| 67 | - eventName.setInnerSafeHtml(safeHtmlEventName); |
|
| 68 | - |
|
| 69 | - if(!event.isRunning()) { |
|
| 61 | + eventName.setInnerSafeHtml(safeHtmlEventName); |
|
| 62 | + if (!event.isRunning()) { |
|
| 70 | 63 | isLiveDiv.getStyle().setDisplay(Display.NONE); |
| 71 | 64 | } |
| 72 | - |
|
| 73 | 65 | venueName.setInnerText(event.venue.getName()); |
| 74 | 66 | eventStartDate.setInnerText(EventDatesFormatterUtil.formatDateRangeWithoutYear(event.startDate, event.endDate)); |
| 75 | - |
|
| 76 | 67 | List<String> photoGalleryImageURLs = event.getPhotoGalleryImageURLs(); |
| 77 | - if(photoGalleryImageURLs.size() == 0) { |
|
| 68 | + if (photoGalleryImageURLs.isEmpty()) { |
|
| 78 | 69 | eventImage.setSrc(defaultImageUrl); |
| 79 | 70 | } else { |
| 80 | 71 | eventImage.setSrc(photoGalleryImageURLs.get(0)); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.java
| ... | ... | @@ -87,6 +87,10 @@ public class Stage extends Composite { |
| 87 | 87 | prevStageTeaserLink.setVisible(false); |
| 88 | 88 | } |
| 89 | 89 | } |
| 90 | + |
|
| 91 | + public void adjustSize() { |
|
| 92 | + swiper.reInit(); |
|
| 93 | + } |
|
| 90 | 94 | |
| 91 | 95 | @UiHandler("nextStageTeaserLink") |
| 92 | 96 | public void nextStageTeaserLinkClicked(ClickEvent e) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/idangerous/Swiper.java
| ... | ... | @@ -31,7 +31,7 @@ public final class Swiper extends JavaScriptObject { |
| 31 | 31 | var options = { |
| 32 | 32 | loop: loop, |
| 33 | 33 | wrapperClass: wrapperClass, |
| 34 | - slideClass: slideClass |
|
| 34 | + slideClass: slideClass, |
|
| 35 | 35 | }; |
| 36 | 36 | if (pageChangeListener) { |
| 37 | 37 | options.onSlideChangeEnd = function(swiper) { |
| ... | ... | @@ -43,6 +43,10 @@ public final class Swiper extends JavaScriptObject { |
| 43 | 43 | return new $wnd.Swiper('.'+containerClass, options); |
| 44 | 44 | }-*/; |
| 45 | 45 | |
| 46 | + public native void reInit() /*-{ |
|
| 47 | + this.reInit(true); |
|
| 48 | + }-*/; |
|
| 49 | + |
|
| 46 | 50 | public native void swipeNext() /*-{ |
| 47 | 51 | this.swipeNext(); |
| 48 | 52 | }-*/; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractLeaderboardConfigPanel.java
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | package com.sap.sailing.gwt.ui.adminconsole; |
| 2 | 2 | |
| 3 | 3 | import java.util.ArrayList; |
| 4 | +import java.util.Comparator; |
|
| 4 | 5 | import java.util.List; |
| 5 | 6 | import java.util.Map; |
| 6 | 7 | |
| ... | ... | @@ -8,6 +9,7 @@ import com.google.gwt.core.client.GWT; |
| 8 | 9 | import com.google.gwt.event.dom.client.ClickEvent; |
| 9 | 10 | import com.google.gwt.event.dom.client.ClickHandler; |
| 10 | 11 | import com.google.gwt.user.cellview.client.CellTable; |
| 12 | +import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler; |
|
| 11 | 13 | import com.google.gwt.user.client.Window; |
| 12 | 14 | import com.google.gwt.user.client.rpc.AsyncCallback; |
| 13 | 15 | import com.google.gwt.user.client.ui.Button; |
| ... | ... | @@ -37,6 +39,7 @@ import com.sap.sailing.gwt.ui.client.RegattaRefresher; |
| 37 | 39 | import com.sap.sailing.gwt.ui.client.RegattasDisplayer; |
| 38 | 40 | import com.sap.sailing.gwt.ui.client.SailingServiceAsync; |
| 39 | 41 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| 42 | +import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn; |
|
| 40 | 43 | import com.sap.sailing.gwt.ui.shared.RegattaDTO; |
| 41 | 44 | import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; |
| 42 | 45 | import com.sap.sse.common.Util; |
| ... | ... | @@ -157,17 +160,14 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement |
| 157 | 160 | } |
| 158 | 161 | }; |
| 159 | 162 | leaderboardConfigControlsPanel.add(filterLeaderboardPanel); |
| 160 | - |
|
| 161 | 163 | addLeaderboardConfigControls(leaderboardConfigControlsPanel); |
| 162 | - |
|
| 163 | 164 | leaderboardsPanel.add(leaderboardConfigControlsPanel); |
| 164 | 165 | leaderboardTable.ensureDebugId("AvailableLeaderboardsTable"); |
| 165 | - |
|
| 166 | - addColumnsToLeaderboardTable(leaderboardTable); |
|
| 167 | - |
|
| 166 | + addColumnsToLeaderboardTableAndSetSelectionModel(leaderboardTable, tableRes); |
|
| 167 | + @SuppressWarnings("unchecked") |
|
| 168 | + MultiSelectionModel<StrippedLeaderboardDTO> multiSelectionModel = (MultiSelectionModel<StrippedLeaderboardDTO>) leaderboardTable.getSelectionModel(); |
|
| 169 | + leaderboardSelectionModel = multiSelectionModel; |
|
| 168 | 170 | leaderboardTable.setWidth("100%"); |
| 169 | - leaderboardSelectionModel = new MultiSelectionModel<StrippedLeaderboardDTO>(); |
|
| 170 | - leaderboardTable.setSelectionModel(leaderboardSelectionModel); |
|
| 171 | 171 | leaderboardSelectionModel.addSelectionChangeHandler(new SelectionChangeEvent.Handler() { |
| 172 | 172 | public void onSelectionChange(SelectionChangeEvent event) { |
| 173 | 173 | leaderboardSelectionChanged(); |
| ... | ... | @@ -179,10 +179,7 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement |
| 179 | 179 | HorizontalPanel leaderboardButtonPanel = new HorizontalPanel(); |
| 180 | 180 | leaderboardButtonPanel.setSpacing(5); |
| 181 | 181 | leaderboardsPanel.add(leaderboardButtonPanel); |
| 182 | - |
|
| 183 | 182 | addLeaderboardCreateControls(leaderboardButtonPanel); |
| 184 | - |
|
| 185 | - |
|
| 186 | 183 | mainPanel.add(new Grid(1, 1)); |
| 187 | 184 | |
| 188 | 185 | // caption panels for the selected leaderboard and tracked races |
| ... | ... | @@ -257,9 +254,33 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement |
| 257 | 254 | protected abstract void addLeaderboardConfigControls(Panel configPanel); |
| 258 | 255 | protected abstract void addLeaderboardCreateControls(Panel createPanel); |
| 259 | 256 | protected abstract void addSelectedLeaderboardRacesControls(Panel racesPanel); |
| 260 | - protected abstract void addColumnsToLeaderboardTable(CellTable<StrippedLeaderboardDTO> leaderboardTable); |
|
| 257 | + protected abstract void addColumnsToLeaderboardTableAndSetSelectionModel(CellTable<StrippedLeaderboardDTO> leaderboardTable, AdminConsoleTableResources tableRes); |
|
| 261 | 258 | protected abstract void addColumnsToRacesTable(CellTable<RaceColumnDTOAndFleetDTOWithNameBasedEquality> racesTable); |
| 262 | 259 | |
| 260 | + protected SelectionCheckboxColumn<StrippedLeaderboardDTO> createSortableSelectionCheckboxColumn( |
|
| 261 | + final CellTable<StrippedLeaderboardDTO> leaderboardTable, AdminConsoleTableResources tableResources, |
|
| 262 | + ListHandler<StrippedLeaderboardDTO> leaderboardColumnListHandler) { |
|
| 263 | + SelectionCheckboxColumn<StrippedLeaderboardDTO> selectionCheckboxColumn = new SelectionCheckboxColumn<StrippedLeaderboardDTO>(tableResources.cellTableStyle().cellTableCheckboxSelected(), |
|
| 264 | + tableResources.cellTableStyle().cellTableCheckboxDeselected(), tableResources.cellTableStyle().cellTableCheckboxColumnCell()) { |
|
| 265 | + @Override |
|
| 266 | + protected ListDataProvider<StrippedLeaderboardDTO> getListDataProvider() { |
|
| 267 | + return leaderboardList; |
|
| 268 | + } |
|
| 269 | + |
|
| 270 | + @Override |
|
| 271 | + public Boolean getValue(StrippedLeaderboardDTO row) { |
|
| 272 | + return leaderboardTable.getSelectionModel().isSelected(row); |
|
| 273 | + } |
|
| 274 | + }; |
|
| 275 | + selectionCheckboxColumn.setSortable(true); |
|
| 276 | + leaderboardColumnListHandler.setComparator(selectionCheckboxColumn, new Comparator<StrippedLeaderboardDTO>() { |
|
| 277 | + @Override |
|
| 278 | + public int compare(StrippedLeaderboardDTO o1, StrippedLeaderboardDTO o2) { |
|
| 279 | + return (leaderboardTable.getSelectionModel().isSelected(o1) ? 1 : 0) - (leaderboardTable.getSelectionModel().isSelected(o2) ? 1 : 0); |
|
| 280 | + } |
|
| 281 | + }); |
|
| 282 | + return selectionCheckboxColumn; |
|
| 283 | + } |
|
| 263 | 284 | |
| 264 | 285 | @Override |
| 265 | 286 | public void fillLeaderboards(Iterable<StrippedLeaderboardDTO> leaderboards) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/EventManagementPanel.java
| ... | ... | @@ -832,8 +832,10 @@ public class EventManagementPanel extends SimplePanel implements EventsRefresher |
| 832 | 832 | for (CourseAreaDTO courseAreaDTO : newEvent.venue.getCourseAreas()) {
|
| 833 | 833 | courseAreaNames.add(courseAreaDTO.getName());
|
| 834 | 834 | }
|
| 835 | - sailingService.createEvent(newEvent.getName(), newEvent.startDate, newEvent.endDate, newEvent.venue.getName(),
|
|
| 836 | - newEvent.isPublic, courseAreaNames, newEvent.getImageURLs(), newEvent.getVideoURLs(), new AsyncCallback<EventDTO>() {
|
|
| 835 | + sailingService.createEvent(newEvent.getName(), newEvent.getDescription(), newEvent.startDate, newEvent.endDate,
|
|
| 836 | + newEvent.venue.getName(), newEvent.isPublic, courseAreaNames, newEvent.getImageURLs(),
|
|
| 837 | + newEvent.getVideoURLs(), newEvent.getSponsorImageURLs(), newEvent.getLogoImageURL(),
|
|
| 838 | + newEvent.getOfficialWebsiteURL(), new AsyncCallback<EventDTO>() {
|
|
| 837 | 839 | @Override
|
| 838 | 840 | public void onFailure(Throwable t) {
|
| 839 | 841 | errorReporter.reportError("Error trying to create new event " + newEvent.getName() + ": " + t.getMessage());
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java
| ... | ... | @@ -48,6 +48,7 @@ import com.sap.sailing.gwt.ui.client.RegattasDisplayer; |
| 48 | 48 | import com.sap.sailing.gwt.ui.client.SailingServiceAsync; |
| 49 | 49 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| 50 | 50 | import com.sap.sailing.gwt.ui.client.URLEncoder; |
| 51 | +import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn; |
|
| 51 | 52 | import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint; |
| 52 | 53 | import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter; |
| 53 | 54 | import com.sap.sailing.gwt.ui.shared.EventDTO; |
| ... | ... | @@ -113,10 +114,11 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { |
| 113 | 114 | } |
| 114 | 115 | |
| 115 | 116 | @Override |
| 116 | - protected void addColumnsToLeaderboardTable(final CellTable<StrippedLeaderboardDTO> leaderboardTable) { |
|
| 117 | + protected void addColumnsToLeaderboardTableAndSetSelectionModel(final CellTable<StrippedLeaderboardDTO> leaderboardTable, AdminConsoleTableResources tableResources) { |
|
| 117 | 118 | ListHandler<StrippedLeaderboardDTO> leaderboardColumnListHandler = new ListHandler<StrippedLeaderboardDTO>( |
| 118 | 119 | leaderboardList.getList()); |
| 119 | - |
|
| 120 | + SelectionCheckboxColumn<StrippedLeaderboardDTO> selectionCheckboxColumn = createSortableSelectionCheckboxColumn( |
|
| 121 | + leaderboardTable, tableResources, leaderboardColumnListHandler); |
|
| 120 | 122 | AnchorCell anchorCell = new AnchorCell(); |
| 121 | 123 | Column<StrippedLeaderboardDTO, SafeHtml> linkColumn = new Column<StrippedLeaderboardDTO, SafeHtml>(anchorCell) { |
| 122 | 124 | @Override |
| ... | ... | @@ -262,6 +264,7 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { |
| 262 | 264 | } |
| 263 | 265 | } |
| 264 | 266 | }); |
| 267 | + leaderboardTable.addColumn(selectionCheckboxColumn, selectionCheckboxColumn.getHeader()); |
|
| 265 | 268 | leaderboardTable.addColumn(linkColumn, stringMessages.name()); |
| 266 | 269 | leaderboardTable.addColumn(leaderboardDisplayNameColumn, stringMessages.displayName()); |
| 267 | 270 | leaderboardTable.addColumn(discardingOptionsColumn, stringMessages.discarding()); |
| ... | ... | @@ -270,8 +273,9 @@ TrackedRaceChangedListener, LeaderboardsDisplayer { |
| 270 | 273 | leaderboardTable.addColumn(courseAreaColumn, stringMessages.courseArea()); |
| 271 | 274 | leaderboardTable.addColumn(leaderboardActionColumn, stringMessages.actions()); |
| 272 | 275 | leaderboardTable.addColumnSortHandler(leaderboardColumnListHandler); |
| 276 | + leaderboardTable.setSelectionModel(selectionCheckboxColumn.getSelectionModel(), selectionCheckboxColumn.getSelectionManager()); |
|
| 273 | 277 | } |
| 274 | - |
|
| 278 | + |
|
| 275 | 279 | protected void addLeaderboardCreateControls(Panel createPanel) { |
| 276 | 280 | Button createFlexibleLeaderboardBtn = new Button(stringMessages.createFlexibleLeaderboard() + "..."); |
| 277 | 281 | createFlexibleLeaderboardBtn.ensureDebugId("CreateFlexibleLeaderboardButton"); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/MediaPanel.java
| ... | ... | @@ -2,7 +2,6 @@ package com.sap.sailing.gwt.ui.adminconsole; |
| 2 | 2 | |
| 3 | 3 | import java.util.Collection;
|
| 4 | 4 | import java.util.Comparator;
|
| 5 | -import java.util.Date;
|
|
| 6 | 5 | |
| 7 | 6 | import com.google.gwt.cell.client.EditTextCell;
|
| 8 | 7 | import com.google.gwt.cell.client.FieldUpdater;
|
| ... | ... | @@ -23,6 +22,8 @@ import com.google.gwt.view.client.DefaultSelectionEventManager; |
| 23 | 22 | import com.google.gwt.view.client.ListDataProvider;
|
| 24 | 23 | import com.google.gwt.view.client.SelectionModel;
|
| 25 | 24 | import com.google.gwt.view.client.SingleSelectionModel;
|
| 25 | +import com.sap.sailing.domain.common.TimePoint;
|
|
| 26 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
|
| 26 | 27 | import com.sap.sailing.domain.common.media.MediaTrack;
|
| 27 | 28 | import com.sap.sailing.domain.common.media.MediaUtil;
|
| 28 | 29 | import com.sap.sailing.gwt.ui.client.ErrorReporter;
|
| ... | ... | @@ -225,7 +226,7 @@ public class MediaPanel extends FlowPanel { |
| 225 | 226 | Column<MediaTrack, String> startTimeColumn = new Column<MediaTrack, String>(new EditTextCell()) {
|
| 226 | 227 | @Override
|
| 227 | 228 | public String getValue(MediaTrack mediaTrack) {
|
| 228 | - return mediaTrack.startTime == null ? "" : TimeFormatUtil.DATETIME_FORMAT.format(mediaTrack.startTime);
|
|
| 229 | + return mediaTrack.startTime == null ? "" : TimeFormatUtil.DATETIME_FORMAT.format(mediaTrack.startTime.asDate());
|
|
| 229 | 230 | }
|
| 230 | 231 | };
|
| 231 | 232 | startTimeColumn.setSortable(true);
|
| ... | ... | @@ -242,7 +243,7 @@ public class MediaPanel extends FlowPanel { |
| 242 | 243 | mediaTrack.startTime = null;
|
| 243 | 244 | } else {
|
| 244 | 245 | try {
|
| 245 | - mediaTrack.startTime = TimeFormatUtil.DATETIME_FORMAT.parse(newStartTime);
|
|
| 246 | + mediaTrack.startTime = new MillisecondsTimePoint(TimeFormatUtil.DATETIME_FORMAT.parse(newStartTime));
|
|
| 246 | 247 | } catch (IllegalArgumentException e) {
|
| 247 | 248 | errorReporter.reportError(stringMessages.mediaDateFormatError(TimeFormatUtil.DATETIME_FORMAT.toString()));
|
| 248 | 249 | }
|
| ... | ... | @@ -268,19 +269,19 @@ public class MediaPanel extends FlowPanel { |
| 268 | 269 | Column<MediaTrack, String> durationColumn = new Column<MediaTrack, String>(new EditTextCell()) {
|
| 269 | 270 | @Override
|
| 270 | 271 | public String getValue(MediaTrack mediaTrack) {
|
| 271 | - return TimeFormatUtil.milliSecondsToHrsMinSec(mediaTrack.durationInMillis);
|
|
| 272 | + return TimeFormatUtil.durationToHrsMinSec(mediaTrack.duration);
|
|
| 272 | 273 | }
|
| 273 | 274 | };
|
| 274 | 275 | durationColumn.setSortable(true);
|
| 275 | 276 | sortHandler.setComparator(durationColumn, new Comparator<MediaTrack>() {
|
| 276 | 277 | public int compare(MediaTrack mediaTrack1, MediaTrack mediaTrack2) {
|
| 277 | - return Integer.valueOf(mediaTrack1.durationInMillis).compareTo(Integer.valueOf(mediaTrack2.durationInMillis));
|
|
| 278 | + return mediaTrack1.duration.compareTo(mediaTrack2.duration);
|
|
| 278 | 279 | }
|
| 279 | 280 | });
|
| 280 | 281 | durationColumn.setFieldUpdater(new FieldUpdater<MediaTrack, String>() {
|
| 281 | 282 | public void update(int index, MediaTrack mediaTrack, String newDuration) {
|
| 282 | 283 | // Called when the user changes the value.
|
| 283 | - mediaTrack.durationInMillis = TimeFormatUtil.hrsMinSecToMilliSeconds(newDuration);
|
|
| 284 | + mediaTrack.duration = TimeFormatUtil.hrsMinSecToMilliSeconds(newDuration);
|
|
| 284 | 285 | mediaService.updateDuration(mediaTrack, new AsyncCallback<Void>() {
|
| 285 | 286 | |
| 286 | 287 | @Override
|
| ... | ... | @@ -332,7 +333,7 @@ public class MediaPanel extends FlowPanel { |
| 332 | 333 | }
|
| 333 | 334 | |
| 334 | 335 | private void addUrlMediaTrack() {
|
| 335 | - Date defaultStartTime = new Date();
|
|
| 336 | + TimePoint defaultStartTime = MillisecondsTimePoint.now();
|
|
| 336 | 337 | NewMediaDialog dialog = new NewMediaDialog(defaultStartTime , stringMessages, new DialogCallback<MediaTrack>() {
|
| 337 | 338 | |
| 338 | 339 | @Override
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceLogTrackingEventManagementPanel.java
| ... | ... | @@ -30,6 +30,7 @@ import com.sap.sailing.gwt.ui.client.LeaderboardsRefresher; |
| 30 | 30 | import com.sap.sailing.gwt.ui.client.RegattaRefresher; |
| 31 | 31 | import com.sap.sailing.gwt.ui.client.SailingServiceAsync; |
| 32 | 32 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| 33 | +import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn; |
|
| 33 | 34 | import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO; |
| 34 | 35 | import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO; |
| 35 | 36 | import com.sap.sse.common.Util; |
| ... | ... | @@ -62,24 +63,23 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf |
| 62 | 63 | } |
| 63 | 64 | |
| 64 | 65 | @Override |
| 65 | - protected void addColumnsToLeaderboardTable(CellTable<StrippedLeaderboardDTO> leaderboardTable) { |
|
| 66 | + protected void addColumnsToLeaderboardTableAndSetSelectionModel(CellTable<StrippedLeaderboardDTO> leaderboardTable, AdminConsoleTableResources tableResources) { |
|
| 66 | 67 | ListHandler<StrippedLeaderboardDTO> leaderboardColumnListHandler = new ListHandler<StrippedLeaderboardDTO>( |
| 67 | 68 | leaderboardList.getList()); |
| 68 | - |
|
| 69 | + SelectionCheckboxColumn<StrippedLeaderboardDTO> selectionCheckboxColumn = createSortableSelectionCheckboxColumn( |
|
| 70 | + leaderboardTable, tableResources, leaderboardColumnListHandler); |
|
| 69 | 71 | TextColumn<StrippedLeaderboardDTO> leaderboardNameColumn = new TextColumn<StrippedLeaderboardDTO>() { |
| 70 | 72 | @Override |
| 71 | 73 | public String getValue(StrippedLeaderboardDTO leaderboard) { |
| 72 | 74 | return leaderboard.name; |
| 73 | 75 | } |
| 74 | 76 | }; |
| 75 | - |
|
| 76 | 77 | TextColumn<StrippedLeaderboardDTO> leaderboardDisplayNameColumn = new TextColumn<StrippedLeaderboardDTO>() { |
| 77 | 78 | @Override |
| 78 | 79 | public String getValue(StrippedLeaderboardDTO leaderboard) { |
| 79 | 80 | return leaderboard.getDisplayName() != null ? leaderboard.getDisplayName() : ""; |
| 80 | 81 | } |
| 81 | 82 | }; |
| 82 | - |
|
| 83 | 83 | ImagesBarColumn<StrippedLeaderboardDTO, RaceLogTrackingEventManagementImagesBarCell> leaderboardActionColumn = |
| 84 | 84 | new ImagesBarColumn<StrippedLeaderboardDTO, RaceLogTrackingEventManagementImagesBarCell>( |
| 85 | 85 | new RaceLogTrackingEventManagementImagesBarCell(stringMessages)); |
| ... | ... | @@ -91,11 +91,13 @@ public class RaceLogTrackingEventManagementPanel extends AbstractLeaderboardConf |
| 91 | 91 | } |
| 92 | 92 | } |
| 93 | 93 | }); |
| 94 | - |
|
| 94 | + leaderboardTable.addColumn(selectionCheckboxColumn, selectionCheckboxColumn.getHeader()); |
|
| 95 | 95 | leaderboardTable.addColumn(leaderboardNameColumn, stringMessages.name()); |
| 96 | 96 | leaderboardTable.addColumn(leaderboardDisplayNameColumn, stringMessages.displayName()); |
| 97 | 97 | leaderboardTable.addColumn(leaderboardActionColumn, stringMessages.actions()); |
| 98 | 98 | leaderboardTable.addColumnSortHandler(leaderboardColumnListHandler); |
| 99 | + leaderboardTable.setSelectionModel(new MultiSelectionModel<StrippedLeaderboardDTO>()); |
|
| 100 | + leaderboardTable.setSelectionModel(selectionCheckboxColumn.getSelectionModel(), selectionCheckboxColumn.getSelectionManager()); |
|
| 99 | 101 | } |
| 100 | 102 | |
| 101 | 103 | private RaceLogTrackingState getTrackingState(RaceColumnDTOAndFleetDTOWithNameBasedEquality race) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java
| ... | ... | @@ -285,8 +285,9 @@ public interface SailingService extends RemoteService { |
| 285 | 285 | String logoImageURL, Iterable<String> imageURLs, Iterable<String> videoURLs,
|
| 286 | 286 | Iterable<String> sponsorImageURLs) throws Exception;
|
| 287 | 287 | |
| 288 | - EventDTO createEvent(String eventName, Date startDate, Date endDate, String description, boolean isPublic,
|
|
| 289 | - List<String> courseAreaNames, Iterable<String> imageURLs, Iterable<String> videoURLs) throws Exception;
|
|
| 288 | + EventDTO createEvent(String eventName, String eventDescription, Date startDate, Date endDate, String venue,
|
|
| 289 | + boolean isPublic, List<String> courseAreaNames, Iterable<String> imageURLs,
|
|
| 290 | + Iterable<String> videoURLs, Iterable<String> sponsorImageURLs, String logoImageURL, String officialWebsiteURL) throws Exception;
|
|
| 290 | 291 | |
| 291 | 292 | void removeEvent(UUID eventId);
|
| 292 | 293 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java
| ... | ... | @@ -321,6 +321,11 @@ public interface SailingServiceAsync { |
| 321 | 321 | void setWindSourcesToExclude(RegattaAndRaceIdentifier raceIdentifier, Iterable<WindSource> windSourcesToExclude,
|
| 322 | 322 | AsyncCallback<Void> callback);
|
| 323 | 323 | |
| 324 | + /**
|
|
| 325 | + * @param date
|
|
| 326 | + * use <code>null</code> to indicate "live" in which case the server live time stamp for the race
|
|
| 327 | + * identified by <code>raceIdentifier</code> will be used, considering that race's delay.
|
|
| 328 | + */
|
|
| 324 | 329 | void getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<String, Date> fromPerCompetitorIdAsString,
|
| 325 | 330 | Map<String, Date> toPerCompetitorIdAsString, boolean extrapolate, AsyncCallback<CompactRaceMapDataDTO> callback);
|
| 326 | 331 | |
| ... | ... | @@ -347,9 +352,9 @@ public interface SailingServiceAsync { |
| 347 | 352 | |
| 348 | 353 | void removeEvents(Collection<UUID> eventIds, AsyncCallback<Void> asyncCallback);
|
| 349 | 354 | |
| 350 | - void createEvent(String eventName, Date startDate, Date endDate, String description, boolean isPublic,
|
|
| 355 | + void createEvent(String eventName, String eventDescription, Date startDate, Date endDate, String venue, boolean isPublic,
|
|
| 351 | 356 | List<String> courseAreaNames, Iterable<String> imageURLs, Iterable<String> videoURLs,
|
| 352 | - AsyncCallback<EventDTO> callback);
|
|
| 357 | + Iterable<String> sponsorImageURLs, String logoImageURL, String officialWebsiteURL, AsyncCallback<EventDTO> callback);
|
|
| 353 | 358 | |
| 354 | 359 | void updateEvent(UUID eventId, String eventName, String eventDescription, Date startDate, Date endDate,
|
| 355 | 360 | VenueDTO venue, boolean isPublic, Iterable<UUID> leaderboardGroupIds, String officialWebsiteURL,
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
| ... | ... | @@ -387,6 +387,7 @@ public interface StringMessages extends Messages { |
| 387 | 387 | String scoringSchemeWinnerGetsFive();
|
| 388 | 388 | String scoringSchemeWinnerGetsFiveIgnoringRaceCount();
|
| 389 | 389 | String scoringSchemeWinnerGetsSix();
|
| 390 | + String scoringSchemeWinnerGetsSixIgnoringRaceCount();
|
|
| 390 | 391 | String scoringSystem();
|
| 391 | 392 | String createFlexibleLeaderboard();
|
| 392 | 393 | String createRegattaLeaderboard();
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
| ... | ... | @@ -16,7 +16,7 @@ race=Race |
| 16 | 16 | races=Races
|
| 17 | 17 | tracked=tracked
|
| 18 | 18 | time=Time
|
| 19 | -timeTooltip=Time the competitor sailed on this leg. For the first leg this is counted from the start, not when the competitor passed the start line.
|
|
| 19 | +timeTooltip=Time the competitor sailed on this leg. For the first leg this is counted from when the competitor passed the start line.
|
|
| 20 | 20 | playSpeed=Play speed
|
| 21 | 21 | playSpeedHelp=Use "1" for regular play-back speed, "2" for double-speed playback and so on
|
| 22 | 22 | playModeLive=Live
|
| ... | ... | @@ -411,6 +411,7 @@ scoringSchemeLowPointWinnerGetsZero=Low Point System, Winner Gets Zero |
| 411 | 411 | scoringSchemeWinnerGetsFive=High Point System, Winner Gets 5 Points
|
| 412 | 412 | scoringSchemeWinnerGetsFiveIgnoringRaceCount=High Point System, Winner Gets 5 Points, Ignoring Race Count
|
| 413 | 413 | scoringSchemeWinnerGetsSix=High Point System, Winner Gets 6 Points
|
| 414 | +scoringSchemeWinnerGetsSixIgnoringRaceCount=High Point System, Winner Gets 6 Points, Ignoring Race Count
|
|
| 414 | 415 | scoringSystem=Scoring system
|
| 415 | 416 | createFlexibleLeaderboard=Create flexible leaderboard
|
| 416 | 417 | createRegattaLeaderboard=Create regatta leaderboard
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
| ... | ... | @@ -16,7 +16,7 @@ race=Wettfahrt |
| 16 | 16 | races=Wettfahrten
|
| 17 | 17 | tracked=getracked
|
| 18 | 18 | time=Zeit
|
| 19 | -timeTooltip=Zeit, die der Teilnehmer auf diesem Schenkel gesegelt ist.
|
|
| 19 | +timeTooltip=Zeit, die der Teilnehmer auf diesem Schenkel gesegelt ist. Beim ersten Schenkel zählt die Zeit ab Überquerung der Startlinie.
|
|
| 20 | 20 | playSpeed=Abspielgeschwindigkeit
|
| 21 | 21 | playSpeedHelp="1" für normale Abspielgeschwindigkeit, "2" für doppelte Geschwindigkeit usw.
|
| 22 | 22 | playModeLive=Live
|
| ... | ... | @@ -412,6 +412,7 @@ scoringSchemeLowPointWinnerGetsZero=Low Point System, Gewinner erhält 0 Punkte |
| 412 | 412 | scoringSchemeWinnerGetsFive=High Point System, Gewinner erhält 5 Punkte
|
| 413 | 413 | scoringSchemeWinnerGetsFiveIgnoringRaceCount=High Point System, Gewinner erhält 5 Punkte, Anzahl Wettfahrten egal
|
| 414 | 414 | scoringSchemeWinnerGetsSix=High Point System, Gewinner erhält 6 Punkte
|
| 415 | +scoringSchemeWinnerGetsSixIgnoringRaceCount=High Point System, Gewinner erhält 6 Punkte, Anzahl Wettfahrten egal
|
|
| 415 | 416 | scoringSystem=Bepunktungssystem
|
| 416 | 417 | createFlexibleLeaderboard=Flexible Rangliste anlegen
|
| 417 | 418 | createRegattaLeaderboard=Rangliste für Regatta anlegen
|
| ... | ... | @@ -1047,7 +1048,7 @@ currentOrAverageSpeedOverGroundInKnots=FüG (\u2205 nach Abschluss) |
| 1047 | 1048 | scoringSchemeHighPointFirstGetsTenOrEight=High Point, Gewinner bekommt 10 Punkte (oder 8)
|
| 1048 | 1049 | videoComponentShortName=Video
|
| 1049 | 1050 | competitorSearchFilter=Teilnehmer-Suchfilter
|
| 1050 | -searchCompetitorsBySailNumberOrName=Suche
|
|
| 1051 | +searchCompetitorsBySailNumberOrName=Suchen
|
|
| 1051 | 1052 | goToEventOverview=Zur Veranstaltungsübersicht
|
| 1052 | 1053 | goToOverviewAndSeeLeaderboard=Zur Ergebnisübersicht mit allen Wettfahrten der Regatta
|
| 1053 | 1054 | noSuchEvent=Veranstaltung nicht gefunden
|
| ... | ... | @@ -1060,4 +1061,4 @@ showVideoPopup=Video zeigen |
| 1060 | 1061 | hideVideoPopup=Video verstecken
|
| 1061 | 1062 | manageMedia=Audio/Video verwalten
|
| 1062 | 1063 | manageMediaTooltip=Audio- und Video-Clips verwalten, Zeiten synchronisieren
|
| 1063 | -showAll=Alle zeigen |
|
| ... | ... | \ No newline at end of file |
| 0 | +showAll=Alle zeigen |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties
| ... | ... | @@ -17,7 +17,8 @@ race=Гонка |
| 17 | 17 | races=Гонки |
| 18 | 18 | tracked=Отслежена |
| 19 | 19 | time=Время |
| 20 | -timeTooltip=Учет времени участника, выступавшего на этом этапе. На первом этапе учет времени ведется с начала гонки, а не при пересечении участником линии старта. |
|
| 20 | +timeTooltip=Time the competitor sailed on this leg. For the first leg this is counted from when the competitor passed the start line. |
|
| 21 | +# timeTooltip=Учет времени участника, выступавшего на этом этапе. На первом этапе учет времени ведется с начала гонки, а не при пересечении участником линии старта. |
|
| 21 | 22 | playSpeed=Скорость воспроизведения |
| 22 | 23 | playSpeedHelp=Используйте "1" для обычной скорости воспроизведения, "2" для удвоенной скорости воспроизведения и т. д. |
| 23 | 24 | playModeLive=В прямом эфире |
| ... | ... | @@ -402,6 +403,7 @@ scoringSchemeLowPointWinnerGetsZero=Низкобальная система по |
| 402 | 403 | scoringSchemeWinnerGetsFive=Высокобальная система подсчета, победитель получает 5 очков |
| 403 | 404 | scoringSchemeWinnerGetsFiveIgnoringRaceCount=Высокобальная система подсчета, победитель получает 5 очков, Ignoring Race Count |
| 404 | 405 | scoringSchemeWinnerGetsSix=Высокобальная система подсчета, победитель получает 6 очков |
| 406 | +scoringSchemeWinnerGetsSixIgnoringRaceCount=Высокобальная система подсчета, победитель получает 6 очков, Ignoring Race Count |
|
| 405 | 407 | scoringSystem=Система подсчета очков |
| 406 | 408 | createFlexibleLeaderboard=Создание адаптивной таблицы лидеров |
| 407 | 409 | createRegattaLeaderboard=Создание таблицы лидеров регаты |
| ... | ... | @@ -1002,7 +1004,7 @@ currentOrAverageSpeedOverGroundInKnots=SOG (\u2205 at end) |
| 1002 | 1004 | scoringSchemeHighPointFirstGetsTenOrEight=High Point, winner gets 10 points (or 8) |
| 1003 | 1005 | videoComponentShortName=Video |
| 1004 | 1006 | competitorSearchFilter=Competitor Search Filter |
| 1005 | -searchCompetitorsBySailNumberOrName=Search by Sail# / Name |
|
| 1007 | +searchCompetitorsBySailNumberOrName=Search |
|
| 1006 | 1008 | goToEventOverview=Go to the event overview |
| 1007 | 1009 | goToOverviewAndSeeLeaderboard=Go to the overview and see all Races in one Leaderboard |
| 1008 | 1010 | noSuchEvent=No such event |
| ... | ... | @@ -1015,4 +1017,4 @@ showVideoPopup=Show Video |
| 1015 | 1017 | hideVideoPopup=Hide Video |
| 1016 | 1018 | manageMedia=Manage Media |
| 1017 | 1019 | manageMediaTooltip=Configure audio and video clips, synchronize times |
| 1018 | -showAll=Show all |
|
| ... | ... | \ No newline at end of file |
| 0 | +showAll=Show all |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaPlayerManagerComponent.java
| ... | ... | @@ -21,6 +21,8 @@ import com.google.gwt.user.client.rpc.AsyncCallback; |
| 21 | 21 | import com.google.gwt.user.client.ui.SimplePanel; |
| 22 | 22 | import com.google.gwt.user.client.ui.Widget; |
| 23 | 23 | import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; |
| 24 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 25 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 24 | 26 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 25 | 27 | import com.sap.sailing.domain.common.media.MediaTrack.MediaType; |
| 26 | 28 | import com.sap.sailing.domain.common.media.MediaTrack.Status; |
| ... | ... | @@ -180,7 +182,8 @@ public class MediaPlayerManagerComponent implements Component<Void>, PlayStateLi |
| 180 | 182 | @Override |
| 181 | 183 | public void playStateChanged(PlayStates playState, PlayModes playMode) { |
| 182 | 184 | this.currentPlayState = playState; |
| 183 | - if (PlayModes.Replay.equals(playMode)) { |
|
| 185 | + switch (playMode) { |
|
| 186 | + case Replay: |
|
| 184 | 187 | switch (this.currentPlayState) { |
| 185 | 188 | case Playing: |
| 186 | 189 | startPlaying(); |
| ... | ... | @@ -190,8 +193,13 @@ public class MediaPlayerManagerComponent implements Component<Void>, PlayStateLi |
| 190 | 193 | default: |
| 191 | 194 | break; |
| 192 | 195 | } |
| 193 | - } else { |
|
| 196 | + break; |
|
| 197 | + case Live: |
|
| 194 | 198 | // TODO: Live mode not supported, yet. |
| 199 | + startPlaying(); |
|
| 200 | + break; |
|
| 201 | + default: |
|
| 202 | + break; |
|
| 195 | 203 | } |
| 196 | 204 | } |
| 197 | 205 | |
| ... | ... | @@ -445,8 +453,8 @@ public class MediaPlayerManagerComponent implements Component<Void>, PlayStateLi |
| 445 | 453 | notifyStateChange(); |
| 446 | 454 | } |
| 447 | 455 | |
| 448 | - private long getRaceStartTime() { |
|
| 449 | - return raceTimesInfoProvider.getRaceTimesInfo(raceIdentifier).startOfRace.getTime(); |
|
| 456 | + private TimePoint getRaceStartTime() { |
|
| 457 | + return new MillisecondsTimePoint(raceTimesInfoProvider.getRaceTimesInfo(raceIdentifier).startOfRace); |
|
| 450 | 458 | } |
| 451 | 459 | |
| 452 | 460 | @Override |
| ... | ... | @@ -526,7 +534,7 @@ public class MediaPlayerManagerComponent implements Component<Void>, PlayStateLi |
| 526 | 534 | |
| 527 | 535 | @Override |
| 528 | 536 | public void addMediaTrack() { |
| 529 | - Date defaultStartTime = new Date(getRaceStartTime()); |
|
| 537 | + TimePoint defaultStartTime = getRaceStartTime(); |
|
| 530 | 538 | NewMediaDialog dialog = new NewMediaDialog(defaultStartTime, MediaPlayerManagerComponent.this.stringMessages, new DialogCallback<MediaTrack>() { |
| 531 | 539 | |
| 532 | 540 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaSynchControl.java
| ... | ... | @@ -38,7 +38,7 @@ public class MediaSynchControl implements EditFlag { |
| 38 | 38 | this.mediaSynchAdapter = mediaSynchAdapter; |
| 39 | 39 | this.errorReporter = errorReporter; |
| 40 | 40 | MediaTrack videoTrack = this.mediaSynchAdapter.getMediaTrack(); |
| 41 | - backupVideoTrack = new MediaTrack(null, videoTrack.title, videoTrack.url, videoTrack.startTime, videoTrack.durationInMillis, videoTrack.mimeType); |
|
| 41 | + backupVideoTrack = new MediaTrack(null, videoTrack.title, videoTrack.url, videoTrack.startTime, videoTrack.duration, videoTrack.mimeType); |
|
| 42 | 42 | mainPanel = new FlowPanel(); |
| 43 | 43 | fineTuningPanel = new FlowPanel(); |
| 44 | 44 | fineTuningPanel.addStyleName("finetuning-panel"); |
| ... | ... | @@ -144,7 +144,7 @@ public class MediaSynchControl implements EditFlag { |
| 144 | 144 | // For now, only start time can be changed. |
| 145 | 145 | // getMediaTrack().title = backupVideoTrack.title; |
| 146 | 146 | // getMediaTrack().url = backupVideoTrack.url; |
| 147 | -// getMediaTrack().durationInMillis = backupVideoTrack.durationInMillis; |
|
| 147 | +// getMediaTrack().duration = backupVideoTrack.duration; |
|
| 148 | 148 | } |
| 149 | 149 | |
| 150 | 150 | private void save() { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java
| ... | ... | @@ -1,7 +1,5 @@ |
| 1 | 1 | package com.sap.sailing.gwt.ui.client.media; |
| 2 | 2 | |
| 3 | -import java.util.Date; |
|
| 4 | - |
|
| 5 | 3 | import com.google.gwt.dom.client.MediaElement; |
| 6 | 4 | import com.google.gwt.dom.client.Style; |
| 7 | 5 | import com.google.gwt.event.dom.client.ChangeEvent; |
| ... | ... | @@ -13,6 +11,8 @@ import com.google.gwt.user.client.ui.Label; |
| 13 | 11 | import com.google.gwt.user.client.ui.TextBox; |
| 14 | 12 | import com.google.gwt.user.client.ui.VerticalPanel; |
| 15 | 13 | import com.google.gwt.user.client.ui.Widget; |
| 14 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 15 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 16 | 16 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 17 | 17 | import com.sap.sailing.domain.common.media.MediaTrack.MimeType; |
| 18 | 18 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| ... | ... | @@ -46,11 +46,11 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> { |
| 46 | 46 | |
| 47 | 47 | private Label durationLabel; |
| 48 | 48 | |
| 49 | - private Date defaultStartTime; |
|
| 49 | + private TimePoint defaultStartTime; |
|
| 50 | 50 | |
| 51 | 51 | private Label infoLabelLabel; |
| 52 | 52 | |
| 53 | - public NewMediaDialog(Date defaultStartTime, StringMessages stringMessages, DialogCallback<MediaTrack> dialogCallback) { |
|
| 53 | + public NewMediaDialog(TimePoint defaultStartTime, StringMessages stringMessages, DialogCallback<MediaTrack> dialogCallback) { |
|
| 54 | 54 | super(stringMessages.addMediaTrack(), "", stringMessages.ok(), stringMessages.cancel(), MEDIA_TRACK_VALIDATOR, dialogCallback); |
| 55 | 55 | this.defaultStartTime = defaultStartTime; |
| 56 | 56 | this.stringMessages = stringMessages; |
| ... | ... | @@ -84,7 +84,7 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> { |
| 84 | 84 | |
| 85 | 85 | public void loadedmetadata(MediaElement mediaElement) { |
| 86 | 86 | mediaTrack.startTime = this.defaultStartTime; |
| 87 | - mediaTrack.durationInMillis = (int) Math.round(mediaElement.getDuration() * 1000); |
|
| 87 | + mediaTrack.duration = new MillisecondsDurationImpl((long) Math.round(mediaElement.getDuration() * 1000)); |
|
| 88 | 88 | refreshUI(); |
| 89 | 89 | } |
| 90 | 90 | |
| ... | ... | @@ -203,9 +203,9 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> { |
| 203 | 203 | setUiEnabled(true); |
| 204 | 204 | mediaTrack.title = title; |
| 205 | 205 | try { |
| 206 | - mediaTrack.durationInMillis = (int) (1000 * Double.valueOf(durationInSeconds)); |
|
| 206 | + mediaTrack.duration = new MillisecondsDurationImpl((long) Math.round(1000 * Double.valueOf(durationInSeconds))); |
|
| 207 | 207 | } catch (NumberFormatException ex) { |
| 208 | - mediaTrack.durationInMillis = 0; |
|
| 208 | + mediaTrack.duration = null; |
|
| 209 | 209 | } |
| 210 | 210 | mediaTrack.startTime = this.defaultStartTime; |
| 211 | 211 | refreshUI(); |
| ... | ... | @@ -220,9 +220,9 @@ public class NewMediaDialog extends DataEntryDialog<MediaTrack> { |
| 220 | 220 | infoLabelLabel.setText(stringMessages.mimeType() + ":"); |
| 221 | 221 | infoLabel.setText(mediaTrack.typeToString()); |
| 222 | 222 | } |
| 223 | - String startTimeText = mediaTrack.startTime == null ? "undefined" : TimeFormatUtil.DATETIME_FORMAT.format(mediaTrack.startTime); |
|
| 223 | + String startTimeText = mediaTrack.startTime == null ? "undefined" : TimeFormatUtil.DATETIME_FORMAT.format(mediaTrack.startTime.asDate()); |
|
| 224 | 224 | startTimeLabel.setText(startTimeText); |
| 225 | - durationLabel.setText(TimeFormatUtil.milliSecondsToHrsMinSec(mediaTrack.durationInMillis)); |
|
| 225 | + durationLabel.setText(TimeFormatUtil.durationToHrsMinSec(mediaTrack.duration)); |
|
| 226 | 226 | } |
| 227 | 227 | |
| 228 | 228 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/TimeFormatUtil.java
| ... | ... | @@ -3,6 +3,8 @@ package com.sap.sailing.gwt.ui.client.media; |
| 3 | 3 | import com.google.gwt.i18n.client.DateTimeFormat; |
| 4 | 4 | import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; |
| 5 | 5 | import com.google.gwt.i18n.client.NumberFormat; |
| 6 | +import com.sap.sailing.domain.common.Duration; |
|
| 7 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 6 | 8 | import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; |
| 7 | 9 | |
| 8 | 10 | public class TimeFormatUtil { |
| ... | ... | @@ -16,9 +18,9 @@ public class TimeFormatUtil { |
| 16 | 18 | private static NumberFormat zeroPaddingNumberFormat_Min = NumberFormatterFactory.getDecimalFormat(2, 0); |
| 17 | 19 | private static NumberFormat zeroPaddingNumberFormat_Sec = NumberFormatterFactory.getDecimalFormat(2, 3); |
| 18 | 20 | |
| 19 | - public static int hrsMinSecToMilliSeconds(String hrsMinSec) { |
|
| 21 | + public static Duration hrsMinSecToMilliSeconds(String hrsMinSec) { |
|
| 20 | 22 | String[] segements = hrsMinSec.split(":"); |
| 21 | - int milliseconds = 0; |
|
| 23 | + long milliseconds = 0; |
|
| 22 | 24 | boolean isNegative = false; |
| 23 | 25 | switch (segements.length) { |
| 24 | 26 | case 3: |
| ... | ... | @@ -38,34 +40,39 @@ public class TimeFormatUtil { |
| 38 | 40 | milliseconds = milliseconds + Math.round(seconds * 1000); |
| 39 | 41 | } |
| 40 | 42 | if (isNegative) { |
| 41 | - return -milliseconds; |
|
| 43 | + return new MillisecondsDurationImpl(-milliseconds); |
|
| 42 | 44 | } else { |
| 43 | - return milliseconds; |
|
| 45 | + return new MillisecondsDurationImpl(milliseconds); |
|
| 44 | 46 | } |
| 45 | 47 | } |
| 46 | 48 | |
| 47 | - public static String milliSecondsToHrsMinSec(long milliseconds) { |
|
| 48 | - int signum = Long.signum(milliseconds); |
|
| 49 | - milliseconds = Math.abs(milliseconds); |
|
| 50 | - |
|
| 51 | - StringBuilder result = new StringBuilder(); |
|
| 52 | - |
|
| 53 | - long hours = (milliseconds / MILLISECONDS_PER_HOUR); |
|
| 54 | - if (hours > 0) { |
|
| 55 | - result.append(String.valueOf(signum * hours) + ':'); |
|
| 56 | - signum = 1; |
|
| 57 | - } |
|
| 58 | - milliseconds = Math.abs(milliseconds); |
|
| 59 | - long rest = (milliseconds % MILLISECONDS_PER_HOUR); |
|
| 60 | - long minutes = (rest / MILLISECONDS_PER_MINUTE); |
|
| 61 | - if (minutes > 0 || result.length() > 0) { |
|
| 62 | - result.append(zeroPaddingNumberFormat_Min.format(signum * minutes) + ':'); |
|
| 63 | - signum = 1; |
|
| 49 | + public static String durationToHrsMinSec(Duration duration) { |
|
| 50 | + if (duration != null) { |
|
| 51 | + long milliseconds = duration.asMillis(); |
|
| 52 | + int signum = Long.signum(milliseconds); |
|
| 53 | + milliseconds = Math.abs(milliseconds); |
|
| 54 | + |
|
| 55 | + StringBuilder result = new StringBuilder(); |
|
| 56 | + |
|
| 57 | + long hours = (milliseconds / MILLISECONDS_PER_HOUR); |
|
| 58 | + if (hours > 0) { |
|
| 59 | + result.append(String.valueOf(signum * hours) + ':'); |
|
| 60 | + signum = 1; |
|
| 61 | + } |
|
| 62 | + milliseconds = Math.abs(milliseconds); |
|
| 63 | + long rest = (milliseconds % MILLISECONDS_PER_HOUR); |
|
| 64 | + long minutes = (rest / MILLISECONDS_PER_MINUTE); |
|
| 65 | + if (minutes > 0 || result.length() > 0) { |
|
| 66 | + result.append(zeroPaddingNumberFormat_Min.format(signum * minutes) + ':'); |
|
| 67 | + signum = 1; |
|
| 68 | + } |
|
| 69 | + rest = (rest % MILLISECONDS_PER_MINUTE); |
|
| 70 | + double seconds = rest / 1000d; |
|
| 71 | + result.append(zeroPaddingNumberFormat_Sec.format(signum * seconds)); |
|
| 72 | + return result.toString(); |
|
| 73 | + } else { |
|
| 74 | + return ""; |
|
| 64 | 75 | } |
| 65 | - rest = (rest % MILLISECONDS_PER_MINUTE); |
|
| 66 | - double seconds = rest / 1000d; |
|
| 67 | - result.append(zeroPaddingNumberFormat_Sec.format(signum * seconds)); |
|
| 68 | - return result.toString(); |
|
| 69 | 76 | } |
| 70 | 77 | |
| 71 | 78 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoHtmlPlayer.java
| ... | ... | @@ -1,10 +1,10 @@ |
| 1 | 1 | package com.sap.sailing.gwt.ui.client.media; |
| 2 | 2 | |
| 3 | -import java.util.Date; |
|
| 4 | - |
|
| 5 | 3 | import com.google.gwt.media.client.MediaBase; |
| 6 | 4 | import com.google.gwt.media.client.Video; |
| 7 | 5 | import com.google.gwt.user.client.ui.Widget; |
| 6 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 7 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 8 | 8 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 9 | 9 | import com.sap.sailing.gwt.ui.client.media.shared.VideoSynchPlayer; |
| 10 | 10 | import com.sap.sailing.gwt.ui.client.media.shared.WithWidget; |
| ... | ... | @@ -12,14 +12,14 @@ import com.sap.sse.gwt.client.player.Timer; |
| 12 | 12 | |
| 13 | 13 | public class VideoHtmlPlayer extends AbstractHtmlMediaPlayer implements VideoSynchPlayer, MediaSynchAdapter, WithWidget { |
| 14 | 14 | |
| 15 | - private final long raceStartTimeMillis; |
|
| 15 | + private final TimePoint raceStartTime; |
|
| 16 | 16 | private final Timer raceTimer; |
| 17 | 17 | private EditFlag editFlag; |
| 18 | 18 | |
| 19 | - public VideoHtmlPlayer(final MediaTrack videoTrack, long raceStartTimeMillis, boolean showSynchControls, Timer raceTimer) { |
|
| 19 | + public VideoHtmlPlayer(final MediaTrack videoTrack, TimePoint raceStartTime, boolean showSynchControls, Timer raceTimer) { |
|
| 20 | 20 | super(videoTrack); |
| 21 | 21 | this.raceTimer = raceTimer; |
| 22 | - this.raceStartTimeMillis = raceStartTimeMillis; |
|
| 22 | + this.raceStartTime = raceStartTime; |
|
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | @Override |
| ... | ... | @@ -29,18 +29,18 @@ public class VideoHtmlPlayer extends AbstractHtmlMediaPlayer implements VideoSyn |
| 29 | 29 | |
| 30 | 30 | @Override |
| 31 | 31 | public long getOffset() { |
| 32 | - return getMediaTrack().startTime.getTime() - raceStartTimeMillis; |
|
| 32 | + return getMediaTrack().startTime.asMillis() - raceStartTime.asMillis(); |
|
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | @Override |
| 36 | 36 | public void changeOffsetBy(long delta) { |
| 37 | - getMediaTrack().startTime = new Date(getMediaTrack().startTime.getTime() + delta); |
|
| 37 | + getMediaTrack().startTime = getMediaTrack().startTime.plus(delta); |
|
| 38 | 38 | forceAlign(); |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | 41 | @Override |
| 42 | 42 | public void updateOffset() { |
| 43 | - getMediaTrack().startTime = new Date(raceTimer.getTime().getTime() - getCurrentMediaTimeMillis()); |
|
| 43 | + getMediaTrack().startTime = new MillisecondsTimePoint(raceTimer.getTime().getTime() - getCurrentMediaTimeMillis()); |
|
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 46 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoYoutubePlayer.java
| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.gwt.ui.client.media; |
| 2 | 2 | |
| 3 | 3 | import java.util.ArrayList; |
| 4 | -import java.util.Date; |
|
| 5 | 4 | import java.util.List; |
| 6 | 5 | |
| 7 | 6 | import com.google.gwt.event.logical.shared.AttachEvent; |
| ... | ... | @@ -9,6 +8,8 @@ import com.google.gwt.event.logical.shared.AttachEvent.Handler; |
| 9 | 8 | import com.google.gwt.user.client.ui.Panel; |
| 10 | 9 | import com.google.gwt.user.client.ui.SimplePanel; |
| 11 | 10 | import com.google.gwt.user.client.ui.Widget; |
| 11 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 12 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 12 | 13 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 13 | 14 | import com.sap.sailing.gwt.ui.client.media.shared.AbstractMediaPlayer; |
| 14 | 15 | import com.sap.sailing.gwt.ui.client.media.shared.VideoSynchPlayer; |
| ... | ... | @@ -25,7 +26,7 @@ public class VideoYoutubePlayer extends AbstractMediaPlayer implements VideoSync |
| 25 | 26 | |
| 26 | 27 | private static int videoCounter; |
| 27 | 28 | |
| 28 | - private final long raceStartTimeMillis; |
|
| 29 | + private final TimePoint raceStartTime; |
|
| 29 | 30 | |
| 30 | 31 | private final Timer raceTimer; |
| 31 | 32 | |
| ... | ... | @@ -44,10 +45,10 @@ public class VideoYoutubePlayer extends AbstractMediaPlayer implements VideoSync |
| 44 | 45 | private final List<DeferredAction> deferredActions = new ArrayList<DeferredAction>(); |
| 45 | 46 | |
| 46 | 47 | |
| 47 | - public VideoYoutubePlayer(final MediaTrack videoTrack, long raceStartTimeMillis, final boolean showControls, Timer raceTimer) { |
|
| 48 | + public VideoYoutubePlayer(final MediaTrack videoTrack, TimePoint raceStartTime, final boolean showControls, Timer raceTimer) { |
|
| 48 | 49 | super(videoTrack); |
| 49 | 50 | this.raceTimer = raceTimer; |
| 50 | - this.raceStartTimeMillis = raceStartTimeMillis; |
|
| 51 | + this.raceStartTime = raceStartTime; |
|
| 51 | 52 | |
| 52 | 53 | this.videoContainer = new SimplePanel(); |
| 53 | 54 | final String videoContainerId = "videoContainer-" + videoTrack.url + ++videoCounter; |
| ... | ... | @@ -85,18 +86,18 @@ public class VideoYoutubePlayer extends AbstractMediaPlayer implements VideoSync |
| 85 | 86 | |
| 86 | 87 | @Override |
| 87 | 88 | public long getOffset() { |
| 88 | - return getMediaTrack().startTime.getTime() - raceStartTimeMillis; |
|
| 89 | + return getMediaTrack().startTime.asMillis() - raceStartTime.asMillis(); |
|
| 89 | 90 | } |
| 90 | 91 | |
| 91 | 92 | @Override |
| 92 | 93 | public void changeOffsetBy(long delta) { |
| 93 | - getMediaTrack().startTime = new Date(getMediaTrack().startTime.getTime() + delta); |
|
| 94 | + getMediaTrack().startTime = getMediaTrack().startTime.plus(delta); |
|
| 94 | 95 | forceAlign(); |
| 95 | 96 | } |
| 96 | 97 | |
| 97 | 98 | @Override |
| 98 | 99 | public void updateOffset() { |
| 99 | - getMediaTrack().startTime = new Date(raceTimer.getTime().getTime() - getCurrentMediaTimeMillis()); |
|
| 100 | + getMediaTrack().startTime = new MillisecondsTimePoint(raceTimer.getTime().getTime() - getCurrentMediaTimeMillis()); |
|
| 100 | 101 | } |
| 101 | 102 | |
| 102 | 103 | @Override |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/shared/AbstractMediaPlayer.java
| ... | ... | @@ -24,7 +24,7 @@ public abstract class AbstractMediaPlayer implements MediaPlayer { |
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | public void forceAlign() { |
| 27 | - forceAlign(mediaTrack.startTime.getTime()); |
|
| 27 | + forceAlign(mediaTrack.startTime.asMillis()); |
|
| 28 | 28 | } |
| 29 | 29 | |
| 30 | 30 | public void raceTimeChanged(Date raceTime) { |
| ... | ... | @@ -39,15 +39,15 @@ public abstract class AbstractMediaPlayer implements MediaPlayer { |
| 39 | 39 | |
| 40 | 40 | @Override |
| 41 | 41 | public boolean isCoveringCurrentRaceTime() { |
| 42 | - double mediaTime = (raceTimeInMillis - mediaTrack.startTime.getTime()) / 1000d; |
|
| 42 | + double mediaTime = (raceTimeInMillis - mediaTrack.startTime.asMillis()) / 1000d; |
|
| 43 | 43 | return (mediaTime >= 0) && (mediaTime <= getDuration()); |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 46 | protected void alignTime() { |
| 47 | - long mediaStartTimeInMillis = mediaTrack.startTime.getTime(); |
|
| 47 | + long mediaStartTimeInMillis = mediaTrack.startTime.asMillis(); |
|
| 48 | 48 | long mediaTimeInMillis = mediaStartTimeInMillis + getCurrentMediaTimeMillis(); |
| 49 | 49 | long mediaLaggingBehindRaceInMillis = raceTimeInMillis - mediaTimeInMillis; |
| 50 | - if (Math.abs(mediaLaggingBehindRaceInMillis) > TOLERATED_LAG_IN_MILLISECONDS) { |
|
| 50 | + if (mediaLaggingBehindRaceInMillis > TOLERATED_LAG_IN_MILLISECONDS) { |
|
| 51 | 51 | forceAlign(mediaStartTimeInMillis); |
| 52 | 52 | } |
| 53 | 53 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CourseMarkOverlay.java
| ... | ... | @@ -17,7 +17,7 @@ import com.sap.sailing.gwt.ui.shared.racemap.MarkVectorGraphics; |
| 17 | 17 | import com.sap.sse.common.Util;
|
| 18 | 18 | |
| 19 | 19 | /**
|
| 20 | - * A google map overlay based on a HTML5 canvas for drawing course marks (images) and the buoy zone if the mark is a buoy.
|
|
| 20 | + * A google map overlay based on a HTML5 canvas for drawing course marks (canvases) and the buoy zone if the mark is a buoy.
|
|
| 21 | 21 | */
|
| 22 | 22 | public class CourseMarkOverlay extends CanvasOverlayV3 {
|
| 23 | 23 | |
| ... | ... | @@ -37,6 +37,12 @@ public class CourseMarkOverlay extends CanvasOverlayV3 { |
| 37 | 37 | private final MarkVectorGraphics markVectorGraphics;
|
| 38 | 38 | |
| 39 | 39 | private Map<Integer, Util.Pair<Double, Size>> markScaleAndSizePerZoomCache;
|
| 40 | +
|
|
| 41 | + private Double lastWidth;
|
|
| 42 | + private Double lastHeight;
|
|
| 43 | + private Double lastScaleFactor;
|
|
| 44 | + private Boolean lastShowBuoyZone;
|
|
| 45 | + private Double lastBuoyZoneRadiusInMeter;
|
|
| 40 | 46 | |
| 41 | 47 | public CourseMarkOverlay(MapWidget map, int zIndex, MarkDTO markDTO) {
|
| 42 | 48 | super(map, zIndex);
|
| ... | ... | @@ -44,68 +50,78 @@ public class CourseMarkOverlay extends CanvasOverlayV3 { |
| 44 | 50 | this.position = markDTO.position;
|
| 45 | 51 | this.buoyZoneRadiusInMeter = 0.0;
|
| 46 | 52 | this.showBuoyZone = false;
|
| 47 | -
|
|
| 48 | 53 | markVectorGraphics = new MarkVectorGraphics(markDTO.type, markDTO.color, markDTO.shape, markDTO.pattern);
|
| 49 | 54 | markScaleAndSizePerZoomCache = new HashMap<Integer, Util.Pair<Double,Size>>();
|
| 50 | -
|
|
| 51 | 55 | setCanvasSize(50, 50);
|
| 52 | 56 | }
|
| 53 | 57 | |
| 54 | -
|
|
| 55 | 58 | @Override
|
| 56 | 59 | protected void draw() {
|
| 57 | 60 | if (mapProjection != null && mark != null && position != null) {
|
| 58 | 61 | int zoom = map.getZoom();
|
| 59 | -
|
|
| 60 | 62 | Util.Pair<Double, Size> markScaleAndSize = markScaleAndSizePerZoomCache.get(zoom);
|
| 61 | - if(markScaleAndSize == null) {
|
|
| 63 | + if (markScaleAndSize == null) {
|
|
| 62 | 64 | markScaleAndSize = getMarkScaleAndSize(position);
|
| 63 | 65 | markScaleAndSizePerZoomCache.put(zoom, markScaleAndSize);
|
| 64 | 66 | }
|
| 65 | 67 | double markSizeScaleFactor = markScaleAndSize.getA();
|
| 66 | -
|
|
| 67 | 68 | getCanvas().setTitle(getTitle());
|
| 68 | -
|
|
| 69 | 69 | LatLng latLngPosition = LatLng.newInstance(position.latDeg, position.lngDeg);
|
| 70 | -
|
|
| 71 | 70 | // calculate canvas size
|
| 72 | 71 | double canvasWidth = markScaleAndSize.getB().getWidth();
|
| 73 | 72 | double canvasHeight = markScaleAndSize.getB().getHeight();
|
| 74 | 73 | double buoyZoneRadiusInPixel = -1;
|
| 75 | - if(showBuoyZone && mark.type == MarkType.BUOY) {
|
|
| 76 | - buoyZoneRadiusInPixel = calculateRadiusOfBoundingBox(mapProjection, latLngPosition, buoyZoneRadiusInMeter);
|
|
| 77 | - if(buoyZoneRadiusInPixel > MIN_BUOYZONE_RADIUS_IN_PX) {
|
|
| 78 | - canvasWidth = (buoyZoneRadiusInPixel + 1)* 2;
|
|
| 74 | + if (showBuoyZone && mark.type == MarkType.BUOY) {
|
|
| 75 | + buoyZoneRadiusInPixel = calculateRadiusOfBoundingBox(mapProjection, latLngPosition,
|
|
| 76 | + buoyZoneRadiusInMeter);
|
|
| 77 | + if (buoyZoneRadiusInPixel > MIN_BUOYZONE_RADIUS_IN_PX) {
|
|
| 78 | + canvasWidth = (buoyZoneRadiusInPixel + 1) * 2;
|
|
| 79 | 79 | canvasHeight = (buoyZoneRadiusInPixel + 1) * 2;
|
| 80 | 80 | }
|
| 81 | 81 | }
|
| 82 | - setCanvasSize((int) canvasWidth, (int) canvasHeight);
|
|
| 83 | -
|
|
| 84 | - Context2d context2d = getCanvas().getContext2d();
|
|
| 85 | -
|
|
| 86 | - // draw the course mark
|
|
| 87 | - markVectorGraphics.drawMarkToCanvas(context2d, showBuoyZone, canvasWidth, canvasHeight, markSizeScaleFactor);
|
|
| 88 | -
|
|
| 82 | + if (needToDraw(showBuoyZone, buoyZoneRadiusInMeter, canvasWidth, canvasHeight, markSizeScaleFactor)) {
|
|
| 83 | + setCanvasSize((int) canvasWidth, (int) canvasHeight);
|
|
| 84 | + Context2d context2d = getCanvas().getContext2d();
|
|
| 85 | + // draw the course mark
|
|
| 86 | + markVectorGraphics.drawMarkToCanvas(context2d, showBuoyZone, canvasWidth, canvasHeight, markSizeScaleFactor);
|
|
| 87 | + // draw the buoy zone
|
|
| 88 | + if (showBuoyZone && mark.type == MarkType.BUOY && buoyZoneRadiusInPixel > MIN_BUOYZONE_RADIUS_IN_PX) {
|
|
| 89 | + CssColor grayTransparentColor = CssColor.make("rgba(50,90,135,0.75)");
|
|
| 90 | + // this translation is important for drawing lines with a real line width of 1 pixel
|
|
| 91 | + context2d.setStrokeStyle(grayTransparentColor);
|
|
| 92 | + context2d.setLineWidth(1.0);
|
|
| 93 | + context2d.beginPath();
|
|
| 94 | + context2d.arc(buoyZoneRadiusInPixel + 1, buoyZoneRadiusInPixel + 1, buoyZoneRadiusInPixel, 0,
|
|
| 95 | + Math.PI * 2, true);
|
|
| 96 | + context2d.closePath();
|
|
| 97 | + context2d.stroke();
|
|
| 98 | + }
|
|
| 99 | + lastBuoyZoneRadiusInMeter = buoyZoneRadiusInMeter;
|
|
| 100 | + lastScaleFactor = markSizeScaleFactor;
|
|
| 101 | + lastShowBuoyZone = showBuoyZone;
|
|
| 102 | + lastWidth = canvasWidth;
|
|
| 103 | + lastHeight = canvasHeight;
|
|
| 104 | + }
|
|
| 89 | 105 | Point buoyPositionInPx = mapProjection.fromLatLngToDivPixel(latLngPosition);
|
| 90 | -
|
|
| 91 | - // draw the buoy zone
|
|
| 92 | - if(showBuoyZone && mark.type == MarkType.BUOY && buoyZoneRadiusInPixel > MIN_BUOYZONE_RADIUS_IN_PX) {
|
|
| 93 | - CssColor grayTransparentColor = CssColor.make("rgba(50,90,135,0.75)");
|
|
| 94 | -
|
|
| 95 | - // this translation is important for drawing lines with a real line width of 1 pixel
|
|
| 96 | - context2d.setStrokeStyle(grayTransparentColor);
|
|
| 97 | - context2d.setLineWidth(1.0);
|
|
| 98 | - context2d.beginPath();
|
|
| 99 | - context2d.arc(buoyZoneRadiusInPixel+1, buoyZoneRadiusInPixel+1, buoyZoneRadiusInPixel, 0, Math.PI*2, true);
|
|
| 100 | - context2d.closePath();
|
|
| 101 | - context2d.stroke();
|
|
| 102 | -
|
|
| 106 | + if (showBuoyZone && mark.type == MarkType.BUOY && buoyZoneRadiusInPixel > MIN_BUOYZONE_RADIUS_IN_PX) {
|
|
| 103 | 107 | setCanvasPosition(buoyPositionInPx.getX() - buoyZoneRadiusInPixel, buoyPositionInPx.getY() - buoyZoneRadiusInPixel);
|
| 104 | 108 | } else {
|
| 105 | 109 | setCanvasPosition(buoyPositionInPx.getX() - canvasWidth / 2.0, buoyPositionInPx.getY() - canvasHeight / 2.0);
|
| 106 | 110 | }
|
| 107 | 111 | }
|
| 108 | 112 | }
|
| 113 | +
|
|
| 114 | + /**
|
|
| 115 | + * Compares the drawing parameters to {@link #lastLegType} and the other <code>last...</code>. If anything has
|
|
| 116 | + * changed, the result is <code>true</code>.
|
|
| 117 | + */
|
|
| 118 | + private boolean needToDraw(boolean showBuoyZone, double buoyZoneRadiusInMeters, double width, double height, double scaleFactor) {
|
|
| 119 | + return lastShowBuoyZone == null || lastShowBuoyZone != showBuoyZone ||
|
|
| 120 | + lastBuoyZoneRadiusInMeter == null || lastBuoyZoneRadiusInMeter != buoyZoneRadiusInMeters ||
|
|
| 121 | + lastScaleFactor == null || lastScaleFactor != scaleFactor ||
|
|
| 122 | + lastWidth == null || lastWidth != width ||
|
|
| 123 | + lastHeight == null || lastHeight != height;
|
|
| 124 | + }
|
|
| 109 | 125 | |
| 110 | 126 | public Util.Pair<Double, Size> getMarkScaleAndSize(PositionDTO markPosition) {
|
| 111 | 127 | double minMarkHeight = 20;
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java
| ... | ... | @@ -124,6 +124,7 @@ import com.sap.sse.common.filter.FilterSet; |
| 124 | 124 | import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
| 125 | 125 | import com.sap.sse.gwt.client.player.TimeListener;
|
| 126 | 126 | import com.sap.sse.gwt.client.player.Timer;
|
| 127 | +import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
|
| 127 | 128 | import com.sap.sse.gwt.client.player.Timer.PlayStates;
|
| 128 | 129 | |
| 129 | 130 | public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSelectionChangeListener, RaceSelectionChangeListener,
|
| ... | ... | @@ -469,7 +470,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe |
| 469 | 470 | |
| 470 | 471 | LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey);
|
| 471 | 472 | }
|
| 472 | -
|
|
| 473 | +
|
|
| 473 | 474 | /**
|
| 474 | 475 | * Creates a header panel where additional information can be displayed by using
|
| 475 | 476 | * {@link #getLeftHeaderPanel()} or {@link #getRightHeaderPanel()}.
|
| ... | ... | @@ -498,7 +499,6 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe |
| 498 | 499 | // controls at RIGHT would not get the correct top setting
|
| 499 | 500 | map.setControls(ControlPosition.TOP_RIGHT, headerPanel);
|
| 500 | 501 | }
|
| 501 | -
|
|
| 502 | 502 | private void createSettingsButton(MapWidget map) {
|
| 503 | 503 | final Component<RaceMapSettings> component = this;
|
| 504 | 504 | Button settingsButton = new Button();
|
| ... | ... | @@ -512,7 +512,6 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe |
| 512 | 512 | });
|
| 513 | 513 | map.setControls(ControlPosition.RIGHT_TOP, settingsButton);
|
| 514 | 514 | }
|
| 515 | -
|
|
| 516 | 515 | |
| 517 | 516 | private void removeTransitions() {
|
| 518 | 517 | // remove the canvas animations for boats
|
| ... | ... | @@ -561,6 +560,16 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe |
| 561 | 560 | this.lastRaceTimesInfo = raceTimesInfos.get(selectedRaces.get(0));
|
| 562 | 561 | }
|
| 563 | 562 | |
| 563 | + /**
|
|
| 564 | + * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard contents}, <code>null</code>
|
|
| 565 | + * is used as time point. The condition for this is encapsulated in this method so others can find out. For example, when a time change
|
|
| 566 | + * is signaled due to local offset / delay adjustments, no additional call to {@link #loadCompleteLeaderboard(Date)} would be required
|
|
| 567 | + * as <code>null</code> will be passed in any case, not being affected by local time offsets.
|
|
| 568 | + */
|
|
| 569 | + private boolean useNullAsTimePoint() {
|
|
| 570 | + return timer.getPlayMode() == PlayModes.Live;
|
|
| 571 | + }
|
|
| 572 | +
|
|
| 564 | 573 | @Override
|
| 565 | 574 | public void timeChanged(final Date newTime, final Date oldTime) {
|
| 566 | 575 | if (newTime != null && isMapInitialized) {
|
| ... | ... | @@ -584,7 +593,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe |
| 584 | 593 | // next, do the full thing; being the later call, if request throttling kicks in, the later call
|
| 585 | 594 | // supersedes the earlier call which may get dropped then
|
| 586 | 595 | GetRaceMapDataAction getRaceMapDataAction = new GetRaceMapDataAction(sailingService, competitorSelection.getAllCompetitors(), race,
|
| 587 | - newTime, fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), /* extrapolate */ true);
|
|
| 596 | + useNullAsTimePoint() ? null : newTime, fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), /* extrapolate */ true);
|
|
| 588 | 597 | asyncActionsExecutor.execute(getRaceMapDataAction, GET_RACE_MAP_DATA_CATEGORY,
|
| 589 | 598 | getRaceMapDataCallback(oldTime, newTime, fromAndToAndOverlap.getC(), competitorsToShow, requestID));
|
| 590 | 599 | |
| ... | ... | @@ -662,7 +671,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe |
| 662 | 671 | final GetRaceMapDataAction result;
|
| 663 | 672 | if (!fromTimes.isEmpty()) {
|
| 664 | 673 | result = new GetRaceMapDataAction(sailingService, competitorSelection.getAllCompetitors(),
|
| 665 | - race, newTime, fromTimes, toTimes, /* extrapolate */true);
|
|
| 674 | + race, useNullAsTimePoint() ? null : newTime, fromTimes, toTimes, /* extrapolate */true);
|
|
| 666 | 675 | } else {
|
| 667 | 676 | result = null;
|
| 668 | 677 | }
|
| ... | ... | @@ -787,7 +796,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe |
| 787 | 796 | }
|
| 788 | 797 | }
|
| 789 | 798 | }
|
| 790 | -
|
|
| 799 | +
|
|
| 791 | 800 | protected void showCourseMarksOnMap(CoursePositionsDTO courseDTO) {
|
| 792 | 801 | if (map != null && courseDTO != null) {
|
| 793 | 802 | Map<String, CourseMarkOverlay> toRemoveCourseMarks = new HashMap<String, CourseMarkOverlay>(courseMarkOverlays);
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java
| ... | ... | @@ -212,7 +212,10 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<R |
| 212 | 212 | result.setTailLengthInMilliseconds(tailLengthBox.getValue() == null ? -1 : tailLengthBox.getValue() * 1000l);
|
| 213 | 213 | }
|
| 214 | 214 | if (helpLinesSettings.isVisible(HelpLineTypes.BUOYZONE)) {
|
| 215 | - result.setBuoyZoneRadiusInMeters(buoyZoneRadiusBox.getValue());
|
|
| 215 | + final Double value = buoyZoneRadiusBox.getValue();
|
|
| 216 | + if (value != null) {
|
|
| 217 | + result.setBuoyZoneRadiusInMeters(value);
|
|
| 218 | + }
|
|
| 216 | 219 | }
|
| 217 | 220 | return result;
|
| 218 | 221 | }
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompetitorFilterPanel.java
| ... | ... | @@ -31,6 +31,7 @@ import com.sap.sailing.gwt.ui.client.shared.filter.FilterUIFactory; |
| 31 | 31 | import com.sap.sailing.gwt.ui.client.shared.filter.FilterWithUI; |
| 32 | 32 | import com.sap.sailing.gwt.ui.client.shared.filter.LeaderboardFetcher; |
| 33 | 33 | import com.sap.sailing.gwt.ui.client.shared.filter.LeaderboardFilterContext; |
| 34 | +import com.sap.sailing.gwt.ui.client.shared.filter.QuickRankProvider; |
|
| 34 | 35 | import com.sap.sailing.gwt.ui.client.shared.filter.SelectedCompetitorsFilter; |
| 35 | 36 | import com.sap.sailing.gwt.ui.client.shared.filter.SelectedRaceFilterContext; |
| 36 | 37 | import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap; |
| ... | ... | @@ -367,6 +368,13 @@ public class CompetitorFilterPanel extends FlowPanel implements KeyUpHandler, Fi |
| 367 | 368 | public Button getSettingsButton() { |
| 368 | 369 | return settingsButton; |
| 369 | 370 | } |
| 371 | + |
|
| 372 | + /** |
|
| 373 | + * @return the {@link QuickRankProvider} if set or <code>null</code> if there is none |
|
| 374 | + */ |
|
| 375 | + public QuickRankProvider getQuickRankProvider() { |
|
| 376 | + return this.raceMap; |
|
| 377 | + } |
|
| 370 | 378 | |
| 371 | 379 | @Override |
| 372 | 380 | public void filterChanged(FilterSet<CompetitorDTO, ? extends Filter<CompetitorDTO>> oldFilterSet, |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardEntryPoint.java
| ... | ... | @@ -4,6 +4,7 @@ import java.util.ArrayList; |
| 4 | 4 | import java.util.Collections;
|
| 5 | 5 | import java.util.List;
|
| 6 | 6 | import java.util.Map;
|
| 7 | +import java.util.UUID;
|
|
| 7 | 8 | import java.util.logging.Logger;
|
| 8 | 9 | |
| 9 | 10 | import com.google.gwt.core.client.GWT;
|
| ... | ... | @@ -62,32 +63,56 @@ public class LeaderboardEntryPoint extends AbstractEntryPoint { |
| 62 | 63 | final boolean showRaceDetails = GwtHttpRequestUtils.getBooleanParameter(LeaderboardUrlSettings.PARAM_SHOW_RACE_DETAILS, false /* default*/);
|
| 63 | 64 | final boolean embedded = GwtHttpRequestUtils.getBooleanParameter(LeaderboardUrlSettings.PARAM_EMBEDDED, false /* default*/);
|
| 64 | 65 | final boolean hideToolbar = GwtHttpRequestUtils.getBooleanParameter(LeaderboardUrlSettings.PARAM_HIDE_TOOLBAR, false /* default*/);
|
| 66 | + final String eventIdAsString = GwtHttpRequestUtils.getStringParameter(LeaderboardUrlSettings.PARAM_EVENT_ID, null /* default*/);
|
|
| 67 | + final UUID eventId = eventIdAsString == null ? null : UUID.fromString(eventIdAsString);
|
|
| 65 | 68 | |
| 66 | 69 | leaderboardName = Window.Location.getParameter("name");
|
| 67 | 70 | leaderboardGroupName = Window.Location.getParameter(LeaderboardUrlSettings.PARAM_LEADERBOARD_GROUP_NAME);
|
| 68 | 71 | |
| 69 | 72 | if (leaderboardName != null) {
|
| 70 | - sailingService.checkLeaderboardName(leaderboardName,
|
|
| 71 | - new MarkedAsyncCallback<Util.Pair<String, LeaderboardType>>(
|
|
| 72 | - new AsyncCallback<Util.Pair<String, LeaderboardType>>() {
|
|
| 73 | - @Override
|
|
| 74 | - public void onSuccess(Util.Pair<String, LeaderboardType> leaderboardNameAndType) {
|
|
| 75 | - if (leaderboardNameAndType != null
|
|
| 76 | - && leaderboardName.equals(leaderboardNameAndType.getA())) {
|
|
| 77 | - Window.setTitle(leaderboardName);
|
|
| 78 | - leaderboardType = leaderboardNameAndType.getB();
|
|
| 79 | - createUI(showRaceDetails, embedded, hideToolbar, event);
|
|
| 80 | - } else {
|
|
| 81 | - RootPanel.get().add(new Label(stringMessages.noSuchLeaderboard()));
|
|
| 82 | - }
|
|
| 83 | - }
|
|
| 73 | + final Runnable checkLeaderboardNameAndCreateUI = new Runnable() {
|
|
| 74 | + @Override
|
|
| 75 | + public void run() {
|
|
| 76 | + sailingService.checkLeaderboardName(leaderboardName,
|
|
| 77 | + new MarkedAsyncCallback<Util.Pair<String, LeaderboardType>>(
|
|
| 78 | + new AsyncCallback<Util.Pair<String, LeaderboardType>>() {
|
|
| 79 | + @Override
|
|
| 80 | + public void onSuccess(Util.Pair<String, LeaderboardType> leaderboardNameAndType) {
|
|
| 81 | + if (leaderboardNameAndType != null
|
|
| 82 | + && leaderboardName.equals(leaderboardNameAndType.getA())) {
|
|
| 83 | + Window.setTitle(leaderboardName);
|
|
| 84 | + leaderboardType = leaderboardNameAndType.getB();
|
|
| 85 | + createUI(showRaceDetails, embedded, hideToolbar, event);
|
|
| 86 | + } else {
|
|
| 87 | + RootPanel.get().add(new Label(stringMessages.noSuchLeaderboard()));
|
|
| 88 | + }
|
|
| 89 | + }
|
|
| 84 | 90 | |
| 85 | - @Override
|
|
| 86 | - public void onFailure(Throwable t) {
|
|
| 87 | - reportError("Error trying to obtain list of leaderboard names: "
|
|
| 88 | - + t.getMessage());
|
|
| 89 | - }
|
|
| 90 | - }));
|
|
| 91 | + @Override
|
|
| 92 | + public void onFailure(Throwable t) {
|
|
| 93 | + reportError("Error trying to obtain list of leaderboard names: "
|
|
| 94 | + + t.getMessage());
|
|
| 95 | + }
|
|
| 96 | + }));
|
|
| 97 | + }
|
|
| 98 | + };
|
|
| 99 | + if (eventId == null) {
|
|
| 100 | + checkLeaderboardNameAndCreateUI.run(); // use null-initialized event field
|
|
| 101 | + } else {
|
|
| 102 | + sailingService.getEventById(eventId, /* withStatisticalData */false, new MarkedAsyncCallback<EventDTO>(
|
|
| 103 | + new AsyncCallback<EventDTO>() {
|
|
| 104 | + @Override
|
|
| 105 | + public void onFailure(Throwable caught) {
|
|
| 106 | + reportError("Error trying to obtain event "+eventId+": " + caught.getMessage());
|
|
| 107 | + }
|
|
| 108 | +
|
|
| 109 | + @Override
|
|
| 110 | + public void onSuccess(EventDTO result) {
|
|
| 111 | + event = result;
|
|
| 112 | + checkLeaderboardNameAndCreateUI.run();
|
|
| 113 | + }
|
|
| 114 | + }));
|
|
| 115 | + }
|
|
| 91 | 116 | } else {
|
| 92 | 117 | RootPanel.get().add(new Label(stringMessages.noSuchLeaderboard()));
|
| 93 | 118 | }
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java
| ... | ... | @@ -2150,6 +2150,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS |
| 2150 | 2150 | CompetitorRaceRankFilter raceRankFilter = new CompetitorRaceRankFilter();
|
| 2151 | 2151 | raceRankFilter.setLeaderboardFetcher(this);
|
| 2152 | 2152 | raceRankFilter.setSelectedRace(preSelectedRace);
|
| 2153 | + raceRankFilter.setQuickRankProvider(this.competitorFilterPanel.getQuickRankProvider());
|
|
| 2153 | 2154 | raceRankFilter.setOperator(new BinaryOperator<Integer>(BinaryOperator.Operators.LessThanEquals));
|
| 2154 | 2155 | raceRankFilter.setValue(maxRaceRank);
|
| 2155 | 2156 | FilterSet<CompetitorDTO, Filter<CompetitorDTO>> activeFilterSet =
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardUrlSettings.java
| ... | ... | @@ -6,6 +6,7 @@ import com.sap.sailing.gwt.ui.client.URLEncoder; |
| 6 | 6 | |
| 7 | 7 | public class LeaderboardUrlSettings { |
| 8 | 8 | public static final String PARAM_LEADERBOARD_GROUP_NAME = "leaderboardGroupName"; |
| 9 | + public static final String PARAM_EVENT_ID = "eventId"; |
|
| 9 | 10 | public static final String PARAM_EMBEDDED = "embedded"; |
| 10 | 11 | public static final String PARAM_HIDE_TOOLBAR = "hideToolbar"; |
| 11 | 12 | public static final String PARAM_SHOW_RACE_DETAILS = "showRaceDetails"; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ScoringSchemeTypeFormatter.java
| ... | ... | @@ -26,6 +26,8 @@ public class ScoringSchemeTypeFormatter { |
| 26 | 26 | return stringMessages.scoringSchemeWinnerGetsFiveIgnoringRaceCount();
|
| 27 | 27 | case HIGH_POINT_WINNER_GETS_SIX:
|
| 28 | 28 | return stringMessages.scoringSchemeWinnerGetsSix();
|
| 29 | + case HIGH_POINT_WINNER_GETS_SIX_IGNORING_RACE_COUNT:
|
|
| 30 | + return stringMessages.scoringSchemeWinnerGetsSixIgnoringRaceCount();
|
|
| 29 | 31 | case HIGH_POINT_FIRST_GETS_TEN_OR_EIGHT:
|
| 30 | 32 | return stringMessages.scoringSchemeHighPointFirstGetsTenOrEight();
|
| 31 | 33 | }
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditCompetitorNameDialog.java
| ... | ... | @@ -0,0 +1,40 @@ |
| 1 | +package com.sap.sailing.gwt.ui.leaderboardedit;
|
|
| 2 | +
|
|
| 3 | +import com.google.gwt.user.client.ui.Grid;
|
|
| 4 | +import com.google.gwt.user.client.ui.Label;
|
|
| 5 | +import com.google.gwt.user.client.ui.TextBox;
|
|
| 6 | +import com.google.gwt.user.client.ui.Widget;
|
|
| 7 | +import com.sap.sailing.gwt.ui.client.StringMessages;
|
|
| 8 | +import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
|
| 9 | +
|
|
| 10 | +public class EditCompetitorNameDialog extends DataEntryDialog<String> {
|
|
| 11 | + private final TextBox competitorNameBox;
|
|
| 12 | + private final StringMessages stringMessages;
|
|
| 13 | +
|
|
| 14 | + public EditCompetitorNameDialog(StringMessages stringMessages, String competitorName,
|
|
| 15 | + DialogCallback<String> callback) {
|
|
| 16 | + super(stringMessages.competitor(), stringMessages.competitor(),
|
|
| 17 | + stringMessages.ok(), stringMessages.cancel(), /* validator */ null, /* animationEnabled */ true,
|
|
| 18 | + callback);
|
|
| 19 | + this.stringMessages = stringMessages;
|
|
| 20 | + this.competitorNameBox = createTextBox(competitorName);
|
|
| 21 | + }
|
|
| 22 | +
|
|
| 23 | + @Override
|
|
| 24 | + protected String getResult() {
|
|
| 25 | + return this.competitorNameBox.getValue();
|
|
| 26 | + }
|
|
| 27 | +
|
|
| 28 | + @Override
|
|
| 29 | + protected Widget getAdditionalWidget() {
|
|
| 30 | + Grid grid = new Grid(2, 2);
|
|
| 31 | + grid.setWidget(0, 0, new Label(stringMessages.competitor()));
|
|
| 32 | + grid.setWidget(0, 1, competitorNameBox);
|
|
| 33 | + return grid;
|
|
| 34 | + }
|
|
| 35 | +
|
|
| 36 | + @Override
|
|
| 37 | + public void show() {
|
|
| 38 | + super.show();
|
|
| 39 | + }
|
|
| 40 | +}
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java
| ... | ... | @@ -820,49 +820,49 @@ public class EditableLeaderboardPanel extends LeaderboardPanel { |
| 820 | 820 | return getStringMessages().suppress();
|
| 821 | 821 | }
|
| 822 | 822 | });
|
| 823 | - result.add(new HasCell<LeaderboardRowDTO, String>() {
|
|
| 824 | - final class OptionalBoldRenderer implements SafeHtmlRenderer<String> {
|
|
| 825 | - private LeaderboardRowDTO currentRow;
|
|
| 823 | + final class OptionalBoldRenderer implements SafeHtmlRenderer<String> {
|
|
| 824 | + private LeaderboardRowDTO currentRow;
|
|
| 826 | 825 | |
| 827 | - @Override
|
|
| 828 | - public SafeHtml render(String object) {
|
|
| 829 | - SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
|
| 830 | - render(object, builder);
|
|
| 831 | - return builder.toSafeHtml();
|
|
| 832 | - }
|
|
| 826 | + @Override
|
|
| 827 | + public SafeHtml render(String object) {
|
|
| 828 | + SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
|
| 829 | + render(object, builder);
|
|
| 830 | + return builder.toSafeHtml();
|
|
| 831 | + }
|
|
| 833 | 832 | |
| 834 | - private boolean isDisplayNameSet() {
|
|
| 835 | - return currentRow != null && getLeaderboard().isDisplayNameSet(currentRow.competitor);
|
|
| 836 | - }
|
|
| 833 | + private boolean isDisplayNameSet() {
|
|
| 834 | + return currentRow != null && getLeaderboard().isDisplayNameSet(currentRow.competitor);
|
|
| 835 | + }
|
|
| 837 | 836 | |
| 838 | - @Override
|
|
| 839 | - public void render(String value, SafeHtmlBuilder builder) {
|
|
| 840 | - if (isDisplayNameSet()) {
|
|
| 841 | - builder.appendHtmlConstant("<b>");
|
|
| 842 | - }
|
|
| 843 | - builder.appendEscaped(value);
|
|
| 844 | - if (isDisplayNameSet()) {
|
|
| 845 | - builder.appendHtmlConstant("</b>");
|
|
| 846 | - }
|
|
| 837 | + @Override
|
|
| 838 | + public void render(String value, SafeHtmlBuilder builder) {
|
|
| 839 | + if (isDisplayNameSet()) {
|
|
| 840 | + builder.appendHtmlConstant("<b>");
|
|
| 847 | 841 | }
|
| 848 | -
|
|
| 849 | - public void setCurrentRow(LeaderboardRowDTO currentRow) {
|
|
| 850 | - this.currentRow = currentRow;
|
|
| 842 | + builder.appendEscaped(value);
|
|
| 843 | + if (isDisplayNameSet()) {
|
|
| 844 | + builder.appendHtmlConstant("</b>");
|
|
| 851 | 845 | }
|
| 852 | 846 | }
|
| 853 | 847 | |
| 854 | - private final OptionalBoldRenderer renderer = new OptionalBoldRenderer();
|
|
| 848 | + public void setCurrentRow(LeaderboardRowDTO currentRow) {
|
|
| 849 | + this.currentRow = currentRow;
|
|
| 850 | + }
|
|
| 851 | + }
|
|
| 855 | 852 | |
| 856 | - private final EditTextCell cell = new EditTextCell(renderer) {
|
|
| 857 | - @Override
|
|
| 858 | - public void render(Context context, String value, SafeHtmlBuilder sb) {
|
|
| 859 | - renderer.setCurrentRow((LeaderboardRowDTO) context.getKey());
|
|
| 860 | - super.render(context, value, sb);
|
|
| 861 | - }
|
|
| 862 | - };
|
|
| 853 | + final OptionalBoldRenderer renderer = new OptionalBoldRenderer();
|
|
| 854 | +
|
|
| 855 | + final EditTextCell cellForCompetitorName = new EditTextCell(renderer) {
|
|
| 856 | + @Override
|
|
| 857 | + public void render(Context context, String value, SafeHtmlBuilder sb) {
|
|
| 858 | + renderer.setCurrentRow((LeaderboardRowDTO) context.getKey());
|
|
| 859 | + super.render(context, value, sb);
|
|
| 860 | + }
|
|
| 861 | + };
|
|
| 862 | + result.add(new HasCell<LeaderboardRowDTO, String>() {
|
|
| 863 | 863 | @Override
|
| 864 | 864 | public EditTextCell getCell() {
|
| 865 | - return cell;
|
|
| 865 | + return cellForCompetitorName;
|
|
| 866 | 866 | }
|
| 867 | 867 | |
| 868 | 868 | @Override
|
| ... | ... | @@ -875,6 +875,56 @@ public class EditableLeaderboardPanel extends LeaderboardPanel { |
| 875 | 875 | return getLeaderboard().getDisplayName(row.competitor);
|
| 876 | 876 | }
|
| 877 | 877 | });
|
| 878 | + result.add(new HasCell<LeaderboardRowDTO, String>() {
|
|
| 879 | + private final ButtonCell cell = new ButtonCell();
|
|
| 880 | + @Override
|
|
| 881 | + public Cell<String> getCell() {
|
|
| 882 | + return cell;
|
|
| 883 | + }
|
|
| 884 | +
|
|
| 885 | + @Override
|
|
| 886 | + public FieldUpdater<LeaderboardRowDTO, String> getFieldUpdater() {
|
|
| 887 | + return new FieldUpdater<LeaderboardRowDTO, String>() {
|
|
| 888 | + @Override
|
|
| 889 | + public void update(int index, final LeaderboardRowDTO row, String valueToUpdate) {
|
|
| 890 | + new EditCompetitorNameDialog(getStringMessages(), row.competitor.getName(), new DialogCallback<String>() {
|
|
| 891 | + @Override
|
|
| 892 | + public void ok(final String value) {
|
|
| 893 | + getSailingService().updateCompetitorDisplayNameInLeaderboard(getLeaderboardName(), row.competitor.getIdAsString(),
|
|
| 894 | + value == null || value.length() == 0 ? null : value.trim(),
|
|
| 895 | + new AsyncCallback<Void>() {
|
|
| 896 | + @Override
|
|
| 897 | + public void onFailure(Throwable t) {
|
|
| 898 | + EditableLeaderboardPanel.this.getErrorReporter().reportError("Error trying to update display name for competitor "+
|
|
| 899 | + row.competitor.getName()+" in leaderboard "+getLeaderboardName()+": "+t.getMessage()+
|
|
| 900 | + "\nYou may have to refresh your view.");
|
|
| 901 | + }
|
|
| 902 | +
|
|
| 903 | + @Override
|
|
| 904 | + public void onSuccess(Void v) {
|
|
| 905 | + if (getLeaderboard().competitorDisplayNames == null) {
|
|
| 906 | + getLeaderboard().competitorDisplayNames = new HashMap<CompetitorDTO, String>();
|
|
| 907 | + }
|
|
| 908 | + getLeaderboard().competitorDisplayNames.put(row.competitor, value == null || value.trim().length() == 0 ? null : value.trim());
|
|
| 909 | + cellForCompetitorName.setViewData(row, null); // ensure that getValue() is called again
|
|
| 910 | + EditableLeaderboardPanel.this.getData().getList().set(
|
|
| 911 | + EditableLeaderboardPanel.this.getData().getList().indexOf(row), row);
|
|
| 912 | + }
|
|
| 913 | + });
|
|
| 914 | + }
|
|
| 915 | + @Override
|
|
| 916 | + public void cancel() {
|
|
| 917 | + }
|
|
| 918 | + }).show();
|
|
| 919 | + }
|
|
| 920 | + };
|
|
| 921 | + }
|
|
| 922 | +
|
|
| 923 | + @Override
|
|
| 924 | + public String getValue(LeaderboardRowDTO object) {
|
|
| 925 | + return getStringMessages().edit();
|
|
| 926 | + }
|
|
| 927 | + });
|
|
| 878 | 928 | return result;
|
| 879 | 929 | }
|
| 880 | 930 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/RaceBoardPanel.java
| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.gwt.ui.raceboard; |
| 2 | 2 | |
| 3 | 3 | import java.util.ArrayList; |
| 4 | -import java.util.Collection; |
|
| 5 | 4 | import java.util.Collections; |
| 6 | 5 | import java.util.HashMap; |
| 7 | 6 | import java.util.List; |
| ... | ... | @@ -11,7 +10,6 @@ import com.google.gwt.dom.client.Document; |
| 11 | 10 | import com.google.gwt.dom.client.Style.Unit; |
| 12 | 11 | import com.google.gwt.i18n.client.DateTimeFormat; |
| 13 | 12 | import com.google.gwt.user.client.Command; |
| 14 | -import com.google.gwt.user.client.rpc.AsyncCallback; |
|
| 15 | 13 | import com.google.gwt.user.client.ui.Anchor; |
| 16 | 14 | import com.google.gwt.user.client.ui.FlowPanel; |
| 17 | 15 | import com.google.gwt.user.client.ui.Label; |
| ... | ... | @@ -25,7 +23,6 @@ import com.sap.sailing.domain.common.dto.FleetDTO; |
| 25 | 23 | import com.sap.sailing.domain.common.dto.LeaderboardDTO; |
| 26 | 24 | import com.sap.sailing.domain.common.dto.RaceColumnDTO; |
| 27 | 25 | import com.sap.sailing.domain.common.dto.RaceDTO; |
| 28 | -import com.sap.sailing.domain.common.media.MediaTrack; |
|
| 29 | 26 | import com.sap.sailing.gwt.ui.client.CompetitorSelectionModel; |
| 30 | 27 | import com.sap.sailing.gwt.ui.client.ErrorReporter; |
| 31 | 28 | import com.sap.sailing.gwt.ui.client.GlobalNavigationPanel; |
| ... | ... | @@ -38,7 +35,6 @@ import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; |
| 38 | 35 | import com.sap.sailing.gwt.ui.client.RegattasDisplayer; |
| 39 | 36 | import com.sap.sailing.gwt.ui.client.SailingServiceAsync; |
| 40 | 37 | import com.sap.sailing.gwt.ui.client.StringMessages; |
| 41 | -import com.sap.sailing.gwt.ui.client.media.MediaComponent; |
|
| 42 | 38 | import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent; |
| 43 | 39 | import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorRaceChart; |
| 44 | 40 | import com.sap.sailing.gwt.ui.client.shared.charts.WindChart; |
| ... | ... | @@ -215,7 +211,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra |
| 215 | 211 | windChart.getEntryWidget().setTitle(stringMessages.windChart()); |
| 216 | 212 | components.add(windChart); |
| 217 | 213 | boolean autoSelectMedia = getConfiguration().isAutoSelectMedia(); |
| 218 | - MediaPlayerManagerComponent mediaPlayerManagerComponent = new MediaPlayerManagerComponent(selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, stringMessages, errorReporter, userAgent, user, autoSelectMedia) |
|
| 214 | + MediaPlayerManagerComponent mediaPlayerManagerComponent = new MediaPlayerManagerComponent(selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, stringMessages, errorReporter, userAgent, user, autoSelectMedia); |
|
| 219 | 215 | leaderboardAndMapViewer = new SideBySideComponentViewer(leaderboardPanel, raceMap, mediaPlayerManagerComponent, components, stringMessages, this.user); |
| 220 | 216 | componentViewers.add(leaderboardAndMapViewer); |
| 221 | 217 | for (ComponentViewer componentViewer : componentViewers) { |
| ... | ... | @@ -235,32 +231,6 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra |
| 235 | 231 | } |
| 236 | 232 | } |
| 237 | 233 | |
| 238 | - private MediaComponent createMediaComponent() { |
|
| 239 | - boolean autoSelectMedia = getConfiguration().isAutoSelectMedia(); |
|
| 240 | - String defaultMedia = getConfiguration().getDefaultMedia(); |
|
| 241 | - final MediaComponent mediaComponent = new MediaComponent(selectedRaceIdentifier, raceTimesInfoProvider, timer, mediaService, stringMessages, errorReporter, userAgent, this.user, autoSelectMedia, defaultMedia); |
|
| 242 | - timer.addPlayStateListener(mediaComponent); |
|
| 243 | - timer.addTimeListener(mediaComponent); |
|
| 244 | - mediaComponent.setVisible(false); |
|
| 245 | - mediaService.getMediaTracksForRace(selectedRaceIdentifier, new AsyncCallback<Collection<MediaTrack>>() { |
|
| 246 | - @Override |
|
| 247 | - public void onSuccess(Collection<MediaTrack> result) { |
|
| 248 | - mediaComponent.getMediaLibraryCallback().onSuccess(result); |
|
| 249 | - if (mediaComponent.isPotentiallyPlayable(mediaComponent.getDefaultVideo())) { |
|
| 250 | - if (leaderboardAndMapViewer != null) { |
|
| 251 | - leaderboardAndMapViewer.getVideoControlButton().setVisible(true); |
|
| 252 | - } |
|
| 253 | - } |
|
| 254 | - } |
|
| 255 | - |
|
| 256 | - @Override |
|
| 257 | - public void onFailure(Throwable caught) { |
|
| 258 | - mediaComponent.getMediaLibraryCallback().onFailure(caught); |
|
| 259 | - } |
|
| 260 | - }); // load media tracks |
|
| 261 | - return mediaComponent; |
|
| 262 | - } |
|
| 263 | - |
|
| 264 | 234 | @SuppressWarnings("unused") |
| 265 | 235 | private <SettingsType> void addSettingsMenuItem(MenuBar settingsMenu, final Component<SettingsType> component) { |
| 266 | 236 | if (component.hasSettings()) { |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/SideBySideComponentViewer.java
| ... | ... | @@ -5,24 +5,17 @@ import java.util.List; |
| 5 | 5 | |
| 6 | 6 | import com.google.gwt.dom.client.Document;
|
| 7 | 7 | import com.google.gwt.dom.client.Style.Unit;
|
| 8 | -import com.google.gwt.event.dom.client.ClickEvent;
|
|
| 9 | -import com.google.gwt.event.dom.client.ClickHandler;
|
|
| 10 | -import com.google.gwt.event.logical.shared.CloseEvent;
|
|
| 11 | -import com.google.gwt.event.logical.shared.CloseHandler;
|
|
| 12 | 8 | import com.google.gwt.safehtml.shared.SafeHtml;
|
| 13 | -import com.google.gwt.user.client.Window;
|
|
| 14 | 9 | import com.google.gwt.user.client.ui.AbsolutePanel;
|
| 15 | 10 | import com.google.gwt.user.client.ui.Button;
|
| 16 | 11 | import com.google.gwt.user.client.ui.DockLayoutPanel.Direction;
|
| 17 | 12 | import com.google.gwt.user.client.ui.LayoutPanel;
|
| 18 | 13 | import com.google.gwt.user.client.ui.Panel;
|
| 19 | -import com.google.gwt.user.client.ui.PopupPanel;
|
|
| 20 | 14 | import com.google.gwt.user.client.ui.RequiresResize;
|
| 21 | 15 | import com.google.gwt.user.client.ui.ScrollPanel;
|
| 22 | 16 | import com.google.gwt.user.client.ui.Widget;
|
| 23 | 17 | import com.google.gwt.user.client.ui.WidgetCollection;
|
| 24 | 18 | import com.sap.sailing.gwt.ui.client.StringMessages;
|
| 25 | -import com.sap.sailing.gwt.ui.client.media.MediaComponent;
|
|
| 26 | 19 | import com.sap.sailing.gwt.ui.client.media.MediaPlayerManagerComponent;
|
| 27 | 20 | import com.sap.sailing.gwt.ui.client.shared.components.Component;
|
| 28 | 21 | import com.sap.sailing.gwt.ui.client.shared.components.ComponentViewer;
|
| ... | ... | @@ -30,7 +23,6 @@ import com.sap.sailing.gwt.ui.client.shared.components.SettingsDialog; |
| 30 | 23 | import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
| 31 | 24 | import com.sap.sailing.gwt.ui.shared.UserDTO;
|
| 32 | 25 | import com.sap.sse.common.Util.Pair;
|
| 33 | -import com.sap.sse.gwt.client.dialog.WindowBox;
|
|
| 34 | 26 | |
| 35 | 27 | /**
|
| 36 | 28 | * Component Viewer that uses a {@link TouchSplitLayoutPanel}
|
| ... | ... | @@ -63,8 +55,10 @@ public class SideBySideComponentViewer implements ComponentViewer { |
| 63 | 55 | private final Component<?> rightComponent;
|
| 64 | 56 | private final List<Component<?>> components;
|
| 65 | 57 | private final ScrollPanel leftScrollPanel;
|
| 66 | - private final Button videoControlButton;
|
|
| 67 | 58 | private final StringMessages stringMessages;
|
| 59 | + private final MediaPlayerManagerComponent mediaPlayerManagerComponent;
|
|
| 60 | + private final Button videoToggleButton;
|
|
| 61 | + private final Button mediaManagementButton;
|
|
| 68 | 62 | |
| 69 | 63 | private LayoutPanel mainPanel;
|
| 70 | 64 | |
| ... | ... | @@ -76,8 +70,10 @@ public class SideBySideComponentViewer implements ComponentViewer { |
| 76 | 70 | this.stringMessages = stringMessages;
|
| 77 | 71 | this.leftComponent = leftComponentP;
|
| 78 | 72 | this.rightComponent = rightComponentP;
|
| 73 | + this.mediaPlayerManagerComponent = mediaPlayerManagerComponent;
|
|
| 79 | 74 | this.components = components;
|
| 80 | - this.videoControlButton = createVideoControlButton(mediaPlayerManagerComponent);
|
|
| 75 | + this.videoToggleButton = createVideoToggleButton(mediaPlayerManagerComponent);
|
|
| 76 | + this.mediaManagementButton = createMediaManagementButton(mediaPlayerManagerComponent);
|
|
| 81 | 77 | this.leftScrollPanel = new ScrollPanel();
|
| 82 | 78 | this.leftScrollPanel.add(leftComponentP.getEntryWidget());
|
| 83 | 79 | this.leftScrollPanel.setTitle(leftComponentP.getEntryWidget().getTitle());
|
| ... | ... | @@ -102,9 +98,9 @@ public class SideBySideComponentViewer implements ComponentViewer { |
| 102 | 98 | |
| 103 | 99 | // add additional toggle buttons panel that currently only contains the video button
|
| 104 | 100 | List<Pair<Button, Component<?>>> additionalVerticalButtons = new ArrayList<Pair<Button,Component<?>>>();
|
| 105 | - additionalVerticalButtons.add(new Pair<Button, Component<?>>(videoControlButton, mediaComponent));
|
|
| 101 | + additionalVerticalButtons.add(new Pair<Button, Component<?>>(videoToggleButton, this.mediaPlayerManagerComponent));
|
|
| 106 | 102 | if (user != null) {
|
| 107 | - additionalVerticalButtons.add(new Pair<Button, Component<?>>(mediaComponent.getMediaSelectionButton(), mediaComponent));
|
|
| 103 | + additionalVerticalButtons.add(new Pair<Button, Component<?>>(mediaManagementButton, this.mediaPlayerManagerComponent));
|
|
| 108 | 104 | }
|
| 109 | 105 | |
| 110 | 106 | // ensure that toggle buttons are positioned right
|
| ... | ... | @@ -112,10 +108,10 @@ public class SideBySideComponentViewer implements ComponentViewer { |
| 112 | 108 | }
|
| 113 | 109 | |
| 114 | 110 | /**
|
| 115 | - * Create the video control button that shows or hides the video popup
|
|
| 111 | + * Create the video toggle button that shows or hides the video popup
|
|
| 116 | 112 | */
|
| 117 | - private Button createVideoControlButton(final MediaPlayerManagerComponent mediaPlayerManagerComponent) {
|
|
| 118 | - final Button videoControlButton = new Button(new SafeHtml() {
|
|
| 113 | + private Button createVideoToggleButton(final MediaPlayerManagerComponent mediaPlayerManagerComponent) {
|
|
| 114 | + final Button videoToggleButton = new Button(new SafeHtml() {
|
|
| 119 | 115 | private static final long serialVersionUID = 8679639887708833213L;
|
| 120 | 116 | @Override
|
| 121 | 117 | public String asString() {
|
| ... | ... | @@ -126,46 +122,23 @@ public class SideBySideComponentViewer implements ComponentViewer { |
| 126 | 122 | }
|
| 127 | 123 | }
|
| 128 | 124 | });
|
| 129 | - videoControlButton.setTitle(stringMessages.showVideoPopup());
|
|
| 130 | - Button closeButton = new Button();
|
|
| 131 | - closeButton.setStyleName("VideoPopup-Close-Button");
|
|
| 132 | - final WindowBox dialog = new WindowBox(stringMessages.videoComponentShortName(), stringMessages.videoComponentShortName(), mediaComponent.getEntryWidget(), null);
|
|
| 133 | - dialog.addCloseHandler(new CloseHandler<PopupPanel>() {
|
|
| 134 | - @Override
|
|
| 135 | - public void onClose(CloseEvent<PopupPanel> event) {
|
|
| 136 | - mediaPlayerManagerComponent.setVisible(false);
|
|
| 137 | - if (Document.get().getClientWidth() > 1024) {
|
|
| 138 | - videoControlButton.setText(stringMessages.showVideoPopup());
|
|
| 139 | - }
|
|
| 140 | - }
|
|
| 141 | - });
|
|
| 142 | - videoControlButton.addClickHandler(new ClickHandler() {
|
|
| 143 | - @Override
|
|
| 144 | - public void onClick(ClickEvent event) {
|
|
| 145 | - if (!dialog.isShowing()) {
|
|
| 146 | - if (mediaComponent.isPotentiallyPlayable(mediaComponent.getDefaultVideo())) {
|
|
| 147 | - mediaComponent.setVisible(true);
|
|
| 148 | - dialog.setPopupPosition(47, Document.get().getClientHeight()-355);
|
|
| 149 | - dialog.show();
|
|
| 150 | - if (Document.get().getClientWidth() > 1024) {
|
|
| 151 | - videoControlButton.setText(stringMessages.hideVideoPopup());
|
|
| 152 | - }
|
|
| 153 | - } else {
|
|
| 154 | - Window.alert("This race has no default video associated.");
|
|
| 155 | - }
|
|
| 156 | - } else {
|
|
| 157 | - mediaComponent.setVisible(false);
|
|
| 158 | - dialog.hide();
|
|
| 159 | - if (Document.get().getClientWidth() > 1024) {
|
|
| 160 | - videoControlButton.setText(stringMessages.showVideoPopup());
|
|
| 161 | - }
|
|
| 162 | - }
|
|
| 163 | - }
|
|
| 164 | - });
|
|
| 125 | + videoToggleButton.setTitle(stringMessages.showVideoPopup());
|
|
| 126 | + // hide button initially as we defer showing the button to the asynchroneous
|
|
| 127 | + // task that gets launched by the media service to get video tracks
|
|
| 128 | + videoToggleButton.setVisible(false);
|
|
| 129 | + return videoToggleButton;
|
|
| 130 | + }
|
|
| 131 | +
|
|
| 132 | + /**
|
|
| 133 | + * Create the video control button that shows or hides the video popup
|
|
| 134 | + */
|
|
| 135 | + private Button createMediaManagementButton(final MediaPlayerManagerComponent mediaPlayerManagerComponent) {
|
|
| 136 | + final Button mediaManagementButton = new Button(stringMessages.mediaPanel());
|
|
| 137 | + mediaManagementButton.setTitle(stringMessages.showVideoPopup());
|
|
| 165 | 138 | // hide button initially as we defer showing the button to the asynchroneous
|
| 166 | 139 | // task that gets launched by the media service to get video tracks
|
| 167 | - videoControlButton.setVisible(false);
|
|
| 168 | - return videoControlButton;
|
|
| 140 | + mediaManagementButton.setVisible(false);
|
|
| 141 | + return mediaManagementButton;
|
|
| 169 | 142 | }
|
| 170 | 143 | |
| 171 | 144 | private void initializeComponents() {
|
| ... | ... | @@ -181,7 +154,7 @@ public class SideBySideComponentViewer implements ComponentViewer { |
| 181 | 154 | }
|
| 182 | 155 | |
| 183 | 156 | public Button getVideoControlButton() {
|
| 184 | - return videoControlButton;
|
|
| 157 | + return videoToggleButton;
|
|
| 185 | 158 | }
|
| 186 | 159 | |
| 187 | 160 | /**
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/Activator.java
| ... | ... | @@ -5,6 +5,12 @@ import org.osgi.framework.BundleContext; |
| 5 | 5 | |
| 6 | 6 | public class Activator implements BundleActivator {
|
| 7 | 7 | private static BundleContext context;
|
| 8 | + private SailingServiceImpl sailingServiceToStopWhenStopping;
|
|
| 9 | + private static Activator INSTANCE;
|
|
| 10 | +
|
|
| 11 | + public Activator() {
|
|
| 12 | + INSTANCE = this;
|
|
| 13 | + }
|
|
| 8 | 14 | |
| 9 | 15 | @Override
|
| 10 | 16 | public void start(BundleContext context) throws Exception {
|
| ... | ... | @@ -13,10 +19,24 @@ public class Activator implements BundleActivator { |
| 13 | 19 | |
| 14 | 20 | @Override
|
| 15 | 21 | public void stop(BundleContext context) throws Exception {
|
| 22 | + if (sailingServiceToStopWhenStopping != null) {
|
|
| 23 | + sailingServiceToStopWhenStopping.stop();
|
|
| 24 | + }
|
|
| 25 | + }
|
|
| 26 | +
|
|
| 27 | + public static Activator getInstance() {
|
|
| 28 | + if (INSTANCE == null) {
|
|
| 29 | + INSTANCE = new Activator();
|
|
| 30 | + }
|
|
| 31 | + return INSTANCE;
|
|
| 16 | 32 | }
|
| 17 | 33 | |
| 18 | 34 | public static BundleContext getDefault() {
|
| 19 | 35 | return context;
|
| 20 | 36 | }
|
| 21 | 37 | |
| 38 | + public void setSailingService(SailingServiceImpl sailingServiceImpl) {
|
|
| 39 | + sailingServiceToStopWhenStopping = sailingServiceImpl;
|
|
| 40 | + }
|
|
| 41 | +
|
|
| 22 | 42 | }
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/QuickRanksLiveCache.java
| ... | ... | @@ -0,0 +1,224 @@ |
| 1 | +package com.sap.sailing.gwt.ui.server; |
|
| 2 | + |
|
| 3 | +import java.lang.ref.Reference; |
|
| 4 | +import java.lang.ref.ReferenceQueue; |
|
| 5 | +import java.lang.ref.WeakReference; |
|
| 6 | +import java.util.HashMap; |
|
| 7 | +import java.util.List; |
|
| 8 | +import java.util.Map; |
|
| 9 | +import java.util.logging.Level; |
|
| 10 | +import java.util.logging.Logger; |
|
| 11 | + |
|
| 12 | +import com.sap.sailing.domain.base.Competitor; |
|
| 13 | +import com.sap.sailing.domain.base.Mark; |
|
| 14 | +import com.sap.sailing.domain.base.Waypoint; |
|
| 15 | +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; |
|
| 16 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 17 | +import com.sap.sailing.domain.common.WindSource; |
|
| 18 | +import com.sap.sailing.domain.tracking.GPSFix; |
|
| 19 | +import com.sap.sailing.domain.tracking.GPSFixMoving; |
|
| 20 | +import com.sap.sailing.domain.tracking.MarkPassing; |
|
| 21 | +import com.sap.sailing.domain.tracking.RaceChangeListener; |
|
| 22 | +import com.sap.sailing.domain.tracking.TrackedRace; |
|
| 23 | +import com.sap.sailing.domain.tracking.TrackedRaceStatus; |
|
| 24 | +import com.sap.sailing.domain.tracking.Wind; |
|
| 25 | +import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; |
|
| 26 | +import com.sap.sailing.gwt.ui.shared.QuickRankDTO; |
|
| 27 | +import com.sap.sailing.server.masterdata.DummyTrackedRace; |
|
| 28 | +import com.sap.sailing.util.SmartFutureCache; |
|
| 29 | +import com.sap.sailing.util.SmartFutureCache.AbstractCacheUpdater; |
|
| 30 | +import com.sap.sailing.util.SmartFutureCache.UpdateInterval; |
|
| 31 | + |
|
| 32 | +/** |
|
| 33 | + * Calculating the quick ranks for many clients for a live race is expensive and therefore benefits from consolidation |
|
| 34 | + * in a single cache. This cache needs to listen for changes in the races for which it manages those {@link QuickRankDTO} objects |
|
| 35 | + * and trigger a re-calculation. It uses a {@link SmartFutureCache} to store and update the cache entries. The keys of the |
|
| 36 | + * {@link SmartFutureCache} are {@link RegattaAndRaceIdentifier}s. In order to properly evict cache entries when the race |
|
| 37 | + * is no longer reachable, each {@link TrackedRace} is referenced by a {@link WeakReference} which has a queue associated. |
|
| 38 | + * The cache runs a thread that fetches collected references from the queue and evicts the cache entries for the respective |
|
| 39 | + * race identifiers. |
|
| 40 | + * |
|
| 41 | + * @author Axel Uhl (D043530) |
|
| 42 | + * |
|
| 43 | + */ |
|
| 44 | +public class QuickRanksLiveCache extends AbstractRaceChangeListener { |
|
| 45 | + private static final Logger logger = Logger.getLogger(QuickRanksLiveCache.class.getName()); |
|
| 46 | + /** |
|
| 47 | + * For each weak reference to a tracked race, remembers the race's {@link RegattaAndRaceIdentifier} which is then the key |
|
| 48 | + * into the {@link SmartFutureCache} from which entries that are no longer referenced shall be removed. |
|
| 49 | + */ |
|
| 50 | + private final Map<WeakReference<? extends TrackedRace>, RegattaAndRaceIdentifier> fromRefToRaceIdentifier; |
|
| 51 | + |
|
| 52 | + private final ReferenceQueue<? extends TrackedRace> referencesToGarbageCollectedRaces; |
|
| 53 | + |
|
| 54 | + /** |
|
| 55 | + * To reliably stop the thread we need a specific reference getting enqueued that we can recognize. Therefore, |
|
| 56 | + * we create a dummy tracked race here and release the reference to it as soon as the {@link #stop} method is called. |
|
| 57 | + * When this reference is later enqueued, the thread will terminate. |
|
| 58 | + */ |
|
| 59 | + private TrackedRace dummyTrackedRace = new DummyTrackedRace("Dummy for QuickRanksLiveCache stopping", /* raceId */ |
|
| 60 | + "Dummy for QuickRanksLiveCache stopping"); |
|
| 61 | + |
|
| 62 | + private final WeakReference<? extends TrackedRace> stopRef = new WeakReference<TrackedRace>(dummyTrackedRace); |
|
| 63 | + |
|
| 64 | + private final SmartFutureCache<RegattaAndRaceIdentifier, List<QuickRankDTO>, CalculateOrPurge> cache; |
|
| 65 | + |
|
| 66 | + private final SailingServiceImpl service; |
|
| 67 | + |
|
| 68 | + private static class CalculateOrPurge implements UpdateInterval<CalculateOrPurge> { |
|
| 69 | + private static final CalculateOrPurge CALCULATE = new CalculateOrPurge(); |
|
| 70 | + private static final CalculateOrPurge PURGE = new CalculateOrPurge(); |
|
| 71 | + |
|
| 72 | + @Override |
|
| 73 | + public CalculateOrPurge join(CalculateOrPurge otherUpdateInterval) { |
|
| 74 | + final CalculateOrPurge result; |
|
| 75 | + if (this == PURGE || otherUpdateInterval == PURGE) { |
|
| 76 | + result = PURGE; |
|
| 77 | + } else { |
|
| 78 | + result = CALCULATE; |
|
| 79 | + } |
|
| 80 | + return result; |
|
| 81 | + } |
|
| 82 | + } |
|
| 83 | + |
|
| 84 | + public QuickRanksLiveCache(final SailingServiceImpl service) { |
|
| 85 | + this.service = service; |
|
| 86 | + cache = new SmartFutureCache<RegattaAndRaceIdentifier, List<QuickRankDTO>, CalculateOrPurge>( |
|
| 87 | + new AbstractCacheUpdater<RegattaAndRaceIdentifier, List<QuickRankDTO>, CalculateOrPurge>() { |
|
| 88 | + @Override |
|
| 89 | + public List<QuickRankDTO> computeCacheUpdate(RegattaAndRaceIdentifier key, |
|
| 90 | + CalculateOrPurge updateInterval) throws Exception { |
|
| 91 | + logger.fine("Computing cache update for live QuickRanks of race "+key); |
|
| 92 | + final List<QuickRankDTO> result; |
|
| 93 | + if (updateInterval == CalculateOrPurge.PURGE) { |
|
| 94 | + result = null; |
|
| 95 | + } else { |
|
| 96 | + result = service.computeQuickRanks(key, /* time point; null means live */ null); |
|
| 97 | + } |
|
| 98 | + return result; |
|
| 99 | + } |
|
| 100 | + }, getClass().getName()); |
|
| 101 | + fromRefToRaceIdentifier = new HashMap<>(); |
|
| 102 | + new Thread("QuickRanksLiveCache garbage collector") { |
|
| 103 | + @Override |
|
| 104 | + public void run() { |
|
| 105 | + Reference<?> ref; |
|
| 106 | + do { |
|
| 107 | + try { |
|
| 108 | + ref = referencesToGarbageCollectedRaces.remove(); |
|
| 109 | + if (ref != stopRef) { |
|
| 110 | + RegattaAndRaceIdentifier raceIdentifier = fromRefToRaceIdentifier.get(ref); |
|
| 111 | + remove(raceIdentifier); |
|
| 112 | + } |
|
| 113 | + } catch (InterruptedException e) { |
|
| 114 | + logger.log(Level.INFO, "Interrupted while waiting for reference in reference queue; quitting", e); |
|
| 115 | + break; |
|
| 116 | + } |
|
| 117 | + } while (ref != stopRef); |
|
| 118 | + logger.info("Received stop in QuickRanksLiveCache garbage collector; terminating"); |
|
| 119 | + } |
|
| 120 | + }.start(); |
|
| 121 | + referencesToGarbageCollectedRaces = new ReferenceQueue<>(); |
|
| 122 | + } |
|
| 123 | + |
|
| 124 | + private void remove(RegattaAndRaceIdentifier raceIdentifier) { |
|
| 125 | + cache.remove(raceIdentifier); |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + public void stop() { |
|
| 129 | + dummyTrackedRace = null; // release the dummy tracked race, causing the stopRef to be enqueued |
|
| 130 | + } |
|
| 131 | + |
|
| 132 | + public List<QuickRankDTO> get(RegattaAndRaceIdentifier raceIdentifier) { |
|
| 133 | + List<QuickRankDTO> result = cache.get(raceIdentifier, false); |
|
| 134 | + if (result == null) { |
|
| 135 | + TrackedRace trackedRace = service.getExistingTrackedRace(raceIdentifier); |
|
| 136 | + if (trackedRace != null) { |
|
| 137 | + trackedRace.addListener(new Listener(raceIdentifier)); // register for all changes that may affect the quick ranks |
|
| 138 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 139 | + } |
|
| 140 | + } |
|
| 141 | + result = cache.get(raceIdentifier, /* wait for latest result */ true); |
|
| 142 | + return result; |
|
| 143 | + } |
|
| 144 | + |
|
| 145 | + private class Listener implements RaceChangeListener { |
|
| 146 | + private final RegattaAndRaceIdentifier raceIdentifier; |
|
| 147 | + |
|
| 148 | + public Listener(RegattaAndRaceIdentifier raceIdentifier) { |
|
| 149 | + this.raceIdentifier = raceIdentifier; |
|
| 150 | + } |
|
| 151 | + |
|
| 152 | + @Override |
|
| 153 | + public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) { |
|
| 154 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 155 | + } |
|
| 156 | + |
|
| 157 | + @Override |
|
| 158 | + public void waypointRemoved(int zeroBasedIndex, Waypoint waypointThatGotRemoved) { |
|
| 159 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 160 | + } |
|
| 161 | + |
|
| 162 | + @Override |
|
| 163 | + public void competitorPositionChanged(GPSFixMoving fix, Competitor competitor) { |
|
| 164 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 165 | + } |
|
| 166 | + |
|
| 167 | + @Override |
|
| 168 | + public void markPositionChanged(GPSFix fix, Mark mark, boolean firstInTrack) { |
|
| 169 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 170 | + } |
|
| 171 | + |
|
| 172 | + @Override |
|
| 173 | + public void markPassingReceived(Competitor competitor, Map<Waypoint, MarkPassing> oldMarkPassings, |
|
| 174 | + Iterable<MarkPassing> markPassings) { |
|
| 175 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 176 | + } |
|
| 177 | + |
|
| 178 | + @Override |
|
| 179 | + public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) { |
|
| 180 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 181 | + } |
|
| 182 | + |
|
| 183 | + @Override |
|
| 184 | + public void windDataReceived(Wind wind, WindSource windSource) { |
|
| 185 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 186 | + } |
|
| 187 | + |
|
| 188 | + @Override |
|
| 189 | + public void windDataRemoved(Wind wind, WindSource windSource) { |
|
| 190 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 191 | + } |
|
| 192 | + |
|
| 193 | + @Override |
|
| 194 | + public void windAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) { |
|
| 195 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 196 | + } |
|
| 197 | + |
|
| 198 | + @Override |
|
| 199 | + public void raceTimesChanged(TimePoint startOfTracking, TimePoint endOfTracking, TimePoint startTimeReceived) { |
|
| 200 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 201 | + } |
|
| 202 | + |
|
| 203 | + @Override |
|
| 204 | + public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) { |
|
| 205 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 206 | + } |
|
| 207 | + |
|
| 208 | + @Override |
|
| 209 | + public void delayToLiveChanged(long delayToLiveInMillis) { |
|
| 210 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 211 | + } |
|
| 212 | + |
|
| 213 | + @Override |
|
| 214 | + public void windSourcesToExcludeChanged(Iterable<? extends WindSource> windSourcesToExclude) { |
|
| 215 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 216 | + } |
|
| 217 | + |
|
| 218 | + @Override |
|
| 219 | + public void statusChanged(TrackedRaceStatus newStatus) { |
|
| 220 | + cache.triggerUpdate(raceIdentifier, CalculateOrPurge.CALCULATE); |
|
| 221 | + } |
|
| 222 | + } |
|
| 223 | + |
|
| 224 | +} |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
| ... | ... | @@ -453,10 +453,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 453 | 453 | |
| 454 | 454 | private final SwissTimingReplayService swissTimingReplayService;
|
| 455 | 455 | |
| 456 | - private final BundleContext context;
|
|
| 456 | + private final QuickRanksLiveCache quickRanksLiveCache;
|
|
| 457 | 457 | |
| 458 | 458 | public SailingServiceImpl() {
|
| 459 | - context = Activator.getDefault();
|
|
| 459 | + BundleContext context = Activator.getDefault();
|
|
| 460 | + Activator activator = Activator.getInstance();
|
|
| 461 | + if (context != null) {
|
|
| 462 | + activator.setSailingService(this); // register so this service is informed when the bundle shuts down
|
|
| 463 | + }
|
|
| 464 | + quickRanksLiveCache = new QuickRanksLiveCache(this);
|
|
| 460 | 465 | racingEventServiceTracker = createAndOpenRacingEventServiceTracker(context);
|
| 461 | 466 | replicationServiceTracker = createAndOpenReplicationServiceTracker(context);
|
| 462 | 467 | resultUrlRegistryServiceTracker = createAndOpenResultUrlRegistryServiceTracker(context);
|
| ... | ... | @@ -501,6 +506,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 501 | 506 | /* keepAliveTime */ 60, TimeUnit.SECONDS,
|
| 502 | 507 | /* workQueue */ new LinkedBlockingQueue<Runnable>());
|
| 503 | 508 | }
|
| 509 | +
|
|
| 510 | + /**
|
|
| 511 | + * Stops this service and frees its resources. In particular, caching services and threads owned by this service will be
|
|
| 512 | + * notified to stop their jobs.
|
|
| 513 | + */
|
|
| 514 | + public void stop() {
|
|
| 515 | + quickRanksLiveCache.stop();
|
|
| 516 | + }
|
|
| 504 | 517 | |
| 505 | 518 | protected SwissTimingReplayService getSwissTimingReplayService(BundleContext context) {
|
| 506 | 519 | return createAndOpenSwissTimingReplayServiceTracker(context).getService().createSwissTimingReplayService(getSwissTimingAdapter().getSwissTimingDomainFactory());
|
| ... | ... | @@ -1719,21 +1732,24 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 1719 | 1732 | |
| 1720 | 1733 | private List<SidelineDTO> getCourseSidelines(RegattaAndRaceIdentifier raceIdentifier, Date date) {
|
| 1721 | 1734 | List<SidelineDTO> result = new ArrayList<SidelineDTO>();
|
| 1722 | - if (date != null) {
|
|
| 1723 | - TimePoint dateAsTimePoint = new MillisecondsTimePoint(date);
|
|
| 1724 | - TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
|
|
| 1725 | - if (trackedRace != null) {
|
|
| 1726 | - for (Sideline sideline : trackedRace.getCourseSidelines()) {
|
|
| 1727 | - List<MarkDTO> markDTOs = new ArrayList<MarkDTO>();
|
|
| 1728 | - for (Mark mark : sideline.getMarks()) {
|
|
| 1729 | - GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
|
|
| 1730 | - Position positionAtDate = track.getEstimatedPosition(dateAsTimePoint, /* extrapolate */false);
|
|
| 1731 | - if (positionAtDate != null) {
|
|
| 1732 | - markDTOs.add(convertToMarkDTO(mark, positionAtDate));
|
|
| 1733 | - }
|
|
| 1735 | + final TimePoint dateAsTimePoint;
|
|
| 1736 | + TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
|
|
| 1737 | + if (trackedRace != null) {
|
|
| 1738 | + if (date == null) {
|
|
| 1739 | + dateAsTimePoint = MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis());
|
|
| 1740 | + } else {
|
|
| 1741 | + dateAsTimePoint = new MillisecondsTimePoint(date);
|
|
| 1742 | + }
|
|
| 1743 | + for (Sideline sideline : trackedRace.getCourseSidelines()) {
|
|
| 1744 | + List<MarkDTO> markDTOs = new ArrayList<MarkDTO>();
|
|
| 1745 | + for (Mark mark : sideline.getMarks()) {
|
|
| 1746 | + GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
|
|
| 1747 | + Position positionAtDate = track.getEstimatedPosition(dateAsTimePoint, /* extrapolate */false);
|
|
| 1748 | + if (positionAtDate != null) {
|
|
| 1749 | + markDTOs.add(convertToMarkDTO(mark, positionAtDate));
|
|
| 1734 | 1750 | }
|
| 1735 | - result.add(new SidelineDTO(sideline.getName(), markDTOs));
|
|
| 1736 | 1751 | }
|
| 1752 | + result.add(new SidelineDTO(sideline.getName(), markDTOs));
|
|
| 1737 | 1753 | }
|
| 1738 | 1754 | }
|
| 1739 | 1755 | return result;
|
| ... | ... | @@ -1742,59 +1758,71 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 1742 | 1758 | @Override
|
| 1743 | 1759 | public CoursePositionsDTO getCoursePositions(RegattaAndRaceIdentifier raceIdentifier, Date date) {
|
| 1744 | 1760 | CoursePositionsDTO result = new CoursePositionsDTO();
|
| 1745 | - if (date != null) {
|
|
| 1746 | - TimePoint dateAsTimePoint = new MillisecondsTimePoint(date);
|
|
| 1747 | - TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
|
|
| 1748 | - if (trackedRace != null) {
|
|
| 1749 | - result.marks = new HashSet<MarkDTO>();
|
|
| 1750 | - result.waypointPositions = new ArrayList<PositionDTO>();
|
|
| 1751 | - Set<Mark> marks = new HashSet<Mark>();
|
|
| 1752 | - Course course = trackedRace.getRace().getCourse();
|
|
| 1753 | - for (Waypoint waypoint : course.getWaypoints()) {
|
|
| 1754 | - Position waypointPosition = trackedRace.getApproximatePosition(waypoint, dateAsTimePoint);
|
|
| 1755 | - if (waypointPosition != null) {
|
|
| 1756 | - result.waypointPositions.add(new PositionDTO(waypointPosition.getLatDeg(), waypointPosition.getLngDeg()));
|
|
| 1757 | - }
|
|
| 1758 | - for (Mark b : waypoint.getMarks()) {
|
|
| 1759 | - marks.add(b);
|
|
| 1760 | - }
|
|
| 1761 | + TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
|
|
| 1762 | + if (trackedRace != null) {
|
|
| 1763 | + final TimePoint dateAsTimePoint;
|
|
| 1764 | + if (date == null) {
|
|
| 1765 | + dateAsTimePoint = MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis());
|
|
| 1766 | + } else {
|
|
| 1767 | + dateAsTimePoint = new MillisecondsTimePoint(date);
|
|
| 1768 | + }
|
|
| 1769 | + result.marks = new HashSet<MarkDTO>();
|
|
| 1770 | + result.waypointPositions = new ArrayList<PositionDTO>();
|
|
| 1771 | + Set<Mark> marks = new HashSet<Mark>();
|
|
| 1772 | + Course course = trackedRace.getRace().getCourse();
|
|
| 1773 | + for (Waypoint waypoint : course.getWaypoints()) {
|
|
| 1774 | + Position waypointPosition = trackedRace.getApproximatePosition(waypoint, dateAsTimePoint);
|
|
| 1775 | + if (waypointPosition != null) {
|
|
| 1776 | + result.waypointPositions.add(new PositionDTO(waypointPosition.getLatDeg(), waypointPosition
|
|
| 1777 | + .getLngDeg()));
|
|
| 1761 | 1778 | }
|
| 1762 | - for (Mark mark : marks) {
|
|
| 1763 | - GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
|
|
| 1764 | - Position positionAtDate = track.getEstimatedPosition(dateAsTimePoint, /* extrapolate */false);
|
|
| 1765 | - if (positionAtDate != null) {
|
|
| 1766 | - result.marks.add(convertToMarkDTO(mark, positionAtDate));
|
|
| 1767 | - }
|
|
| 1779 | + for (Mark b : waypoint.getMarks()) {
|
|
| 1780 | + marks.add(b);
|
|
| 1768 | 1781 | }
|
| 1782 | + }
|
|
| 1783 | + for (Mark mark : marks) {
|
|
| 1784 | + GPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
|
|
| 1785 | + Position positionAtDate = track.getEstimatedPosition(dateAsTimePoint, /* extrapolate */false);
|
|
| 1786 | + if (positionAtDate != null) {
|
|
| 1787 | + result.marks.add(convertToMarkDTO(mark, positionAtDate));
|
|
| 1788 | + }
|
|
| 1789 | + }
|
|
| 1769 | 1790 | |
| 1770 | - // set the positions of start and finish
|
|
| 1771 | - Waypoint firstWaypoint = course.getFirstWaypoint();
|
|
| 1772 | - if (firstWaypoint != null && Util.size(firstWaypoint.getMarks())==2) {
|
|
| 1773 | - final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getStartLine(dateAsTimePoint);
|
|
| 1774 | - if (markPositionDTOsAndLineAdvantage != null) {
|
|
| 1775 | - final List<PositionDTO> startMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace, firstWaypoint);
|
|
| 1776 | - result.startMarkPositions = startMarkPositionDTOs;
|
|
| 1777 | - result.startLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters();
|
|
| 1778 | - Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage.getAbsoluteAngleDifferenceToTrueWind();
|
|
| 1779 | - result.startLineAngleToCombinedWind = absoluteAngleDifferenceToTrueWind==null?null:absoluteAngleDifferenceToTrueWind.getDegrees();
|
|
| 1780 | - result.startLineAdvantageousSide = markPositionDTOsAndLineAdvantage.getAdvantageousSideWhileApproachingLine();
|
|
| 1781 | - Distance advantage = markPositionDTOsAndLineAdvantage.getAdvantage();
|
|
| 1782 | - result.startLineAdvantageInMeters = advantage==null?null:advantage.getMeters();
|
|
| 1783 | - }
|
|
| 1784 | - }
|
|
| 1785 | - Waypoint lastWaypoint = course.getLastWaypoint();
|
|
| 1786 | - if (lastWaypoint != null && Util.size(lastWaypoint.getMarks())==2) {
|
|
| 1787 | - final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getFinishLine(dateAsTimePoint);
|
|
| 1788 | - if (markPositionDTOsAndLineAdvantage != null) {
|
|
| 1789 | - final List<PositionDTO> finishMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace, lastWaypoint);
|
|
| 1790 | - result.finishMarkPositions = finishMarkPositionDTOs;
|
|
| 1791 | - result.finishLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters();
|
|
| 1792 | - Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage.getAbsoluteAngleDifferenceToTrueWind();
|
|
| 1793 | - result.finishLineAngleToCombinedWind = absoluteAngleDifferenceToTrueWind==null?null:absoluteAngleDifferenceToTrueWind.getDegrees();
|
|
| 1794 | - result.finishLineAdvantageousSide = markPositionDTOsAndLineAdvantage.getAdvantageousSideWhileApproachingLine();
|
|
| 1795 | - Distance advantage = markPositionDTOsAndLineAdvantage.getAdvantage();
|
|
| 1796 | - result.finishLineAdvantageInMeters = advantage==null?null:advantage.getMeters();
|
|
| 1797 | - }
|
|
| 1791 | + // set the positions of start and finish
|
|
| 1792 | + Waypoint firstWaypoint = course.getFirstWaypoint();
|
|
| 1793 | + if (firstWaypoint != null && Util.size(firstWaypoint.getMarks()) == 2) {
|
|
| 1794 | + final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getStartLine(dateAsTimePoint);
|
|
| 1795 | + if (markPositionDTOsAndLineAdvantage != null) {
|
|
| 1796 | + final List<PositionDTO> startMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace,
|
|
| 1797 | + firstWaypoint);
|
|
| 1798 | + result.startMarkPositions = startMarkPositionDTOs;
|
|
| 1799 | + result.startLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters();
|
|
| 1800 | + Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage
|
|
| 1801 | + .getAbsoluteAngleDifferenceToTrueWind();
|
|
| 1802 | + result.startLineAngleToCombinedWind = absoluteAngleDifferenceToTrueWind == null ? null
|
|
| 1803 | + : absoluteAngleDifferenceToTrueWind.getDegrees();
|
|
| 1804 | + result.startLineAdvantageousSide = markPositionDTOsAndLineAdvantage
|
|
| 1805 | + .getAdvantageousSideWhileApproachingLine();
|
|
| 1806 | + Distance advantage = markPositionDTOsAndLineAdvantage.getAdvantage();
|
|
| 1807 | + result.startLineAdvantageInMeters = advantage == null ? null : advantage.getMeters();
|
|
| 1808 | + }
|
|
| 1809 | + }
|
|
| 1810 | + Waypoint lastWaypoint = course.getLastWaypoint();
|
|
| 1811 | + if (lastWaypoint != null && Util.size(lastWaypoint.getMarks()) == 2) {
|
|
| 1812 | + final LineDetails markPositionDTOsAndLineAdvantage = trackedRace.getFinishLine(dateAsTimePoint);
|
|
| 1813 | + if (markPositionDTOsAndLineAdvantage != null) {
|
|
| 1814 | + final List<PositionDTO> finishMarkPositionDTOs = getMarkPositionDTOs(dateAsTimePoint, trackedRace,
|
|
| 1815 | + lastWaypoint);
|
|
| 1816 | + result.finishMarkPositions = finishMarkPositionDTOs;
|
|
| 1817 | + result.finishLineLengthInMeters = markPositionDTOsAndLineAdvantage.getLength().getMeters();
|
|
| 1818 | + Bearing absoluteAngleDifferenceToTrueWind = markPositionDTOsAndLineAdvantage
|
|
| 1819 | + .getAbsoluteAngleDifferenceToTrueWind();
|
|
| 1820 | + result.finishLineAngleToCombinedWind = absoluteAngleDifferenceToTrueWind == null ? null
|
|
| 1821 | + : absoluteAngleDifferenceToTrueWind.getDegrees();
|
|
| 1822 | + result.finishLineAdvantageousSide = markPositionDTOsAndLineAdvantage
|
|
| 1823 | + .getAdvantageousSideWhileApproachingLine();
|
|
| 1824 | + Distance advantage = markPositionDTOsAndLineAdvantage.getAdvantage();
|
|
| 1825 | + result.finishLineAdvantageInMeters = advantage == null ? null : advantage.getMeters();
|
|
| 1798 | 1826 | }
|
| 1799 | 1827 | }
|
| 1800 | 1828 | }
|
| ... | ... | @@ -1908,28 +1936,48 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 1908 | 1936 | return markPositionDTOs;
|
| 1909 | 1937 | }
|
| 1910 | 1938 | |
| 1911 | - private List<QuickRankDTO> getQuickRanks(RegattaAndRaceIdentifier raceIdentifier, Date date) throws NoWindException {
|
|
| 1939 | + /**
|
|
| 1940 | + * @param timePoint
|
|
| 1941 | + * <code>null</code> means "live" and is then replaced by "now" minus the tracked race's
|
|
| 1942 | + * {@link TrackedRace#getDelayToLiveInMillis() delay}.
|
|
| 1943 | + */
|
|
| 1944 | + public List<QuickRankDTO> computeQuickRanks(RegattaAndRaceIdentifier raceIdentifier, TimePoint timePoint)
|
|
| 1945 | + throws NoWindException {
|
|
| 1912 | 1946 | List<QuickRankDTO> result = new ArrayList<QuickRankDTO>();
|
| 1913 | - if (date != null) {
|
|
| 1914 | - TimePoint dateAsTimePoint = new MillisecondsTimePoint(date);
|
|
| 1915 | - TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
|
|
| 1916 | - if (trackedRace != null) {
|
|
| 1917 | - RaceDefinition race = trackedRace.getRace();
|
|
| 1918 | - int rank = 1;
|
|
| 1919 | - for (Competitor competitor : trackedRace.getCompetitorsFromBestToWorst(dateAsTimePoint)) {
|
|
| 1920 | - TrackedLegOfCompetitor trackedLeg = trackedRace.getTrackedLeg(competitor, dateAsTimePoint);
|
|
| 1921 | - if (trackedLeg != null) {
|
|
| 1922 | - int legNumberOneBased = race.getCourse().getLegs().indexOf(trackedLeg.getLeg()) + 1;
|
|
| 1923 | - QuickRankDTO quickRankDTO = new QuickRankDTO(baseDomainFactory.convertToCompetitorDTO(competitor), rank, legNumberOneBased);
|
|
| 1924 | - result.add(quickRankDTO);
|
|
| 1925 | - }
|
|
| 1926 | - rank++;
|
|
| 1947 | + TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
|
|
| 1948 | + if (trackedRace != null) {
|
|
| 1949 | + final TimePoint actualTimePoint;
|
|
| 1950 | + if (timePoint == null) {
|
|
| 1951 | + actualTimePoint = MillisecondsTimePoint.now().minus(trackedRace.getDelayToLiveInMillis());
|
|
| 1952 | + } else {
|
|
| 1953 | + actualTimePoint = timePoint;
|
|
| 1954 | + }
|
|
| 1955 | + RaceDefinition race = trackedRace.getRace();
|
|
| 1956 | + int rank = 1;
|
|
| 1957 | + for (Competitor competitor : trackedRace.getCompetitorsFromBestToWorst(actualTimePoint)) {
|
|
| 1958 | + TrackedLegOfCompetitor trackedLeg = trackedRace.getTrackedLeg(competitor, actualTimePoint);
|
|
| 1959 | + if (trackedLeg != null) {
|
|
| 1960 | + int legNumberOneBased = race.getCourse().getLegs().indexOf(trackedLeg.getLeg()) + 1;
|
|
| 1961 | + QuickRankDTO quickRankDTO = new QuickRankDTO(baseDomainFactory.convertToCompetitorDTO(competitor),
|
|
| 1962 | + rank, legNumberOneBased);
|
|
| 1963 | + result.add(quickRankDTO);
|
|
| 1927 | 1964 | }
|
| 1965 | + rank++;
|
|
| 1928 | 1966 | }
|
| 1929 | 1967 | }
|
| 1930 | 1968 | return result;
|
| 1931 | 1969 | }
|
| 1932 | 1970 | |
| 1971 | + private List<QuickRankDTO> getQuickRanks(RegattaAndRaceIdentifier raceIdentifier, Date date) throws NoWindException {
|
|
| 1972 | + final List<QuickRankDTO> result;
|
|
| 1973 | + if (date == null) {
|
|
| 1974 | + result = quickRanksLiveCache.get(raceIdentifier);
|
|
| 1975 | + } else {
|
|
| 1976 | + result = computeQuickRanks(raceIdentifier, date == null ? null : new MillisecondsTimePoint(date));
|
|
| 1977 | + }
|
|
| 1978 | + return result;
|
|
| 1979 | + }
|
|
| 1980 | +
|
|
| 1933 | 1981 | @Override
|
| 1934 | 1982 | public void setRaceIsKnownToStartUpwind(RegattaAndRaceIdentifier raceIdentifier, boolean raceIsKnownToStartUpwind) {
|
| 1935 | 1983 | getService().apply(new SetRaceIsKnownToStartUpwind(raceIdentifier, raceIsKnownToStartUpwind));
|
| ... | ... | @@ -3041,7 +3089,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 3041 | 3089 | public List<EventBaseDTO> getPublicEventsOfAllSailingServers() throws MalformedURLException {
|
| 3042 | 3090 | List<EventBaseDTO> result = new ArrayList<>();
|
| 3043 | 3091 | for (EventDTO localEvent : getEvents()) {
|
| 3044 | - if(localEvent.isPublic) {
|
|
| 3092 | + if (localEvent.isPublic) {
|
|
| 3045 | 3093 | result.add(localEvent);
|
| 3046 | 3094 | }
|
| 3047 | 3095 | }
|
| ... | ... | @@ -3140,14 +3188,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 3140 | 3188 | }
|
| 3141 | 3189 | |
| 3142 | 3190 | @Override
|
| 3143 | - public EventDTO createEvent(String eventName, Date startDate, Date endDate, String venue, boolean isPublic, List<String> courseAreaNames,
|
|
| 3144 | - Iterable<String> imageURLs, Iterable<String> videoURLs) throws MalformedURLException {
|
|
| 3191 | + public EventDTO createEvent(String eventName, String eventDescription, Date startDate, Date endDate, String venue,
|
|
| 3192 | + boolean isPublic, List<String> courseAreaNames, Iterable<String> imageURLs,
|
|
| 3193 | + Iterable<String> videoURLs, Iterable<String> sponsorImageURLs, String logoImageURLAsString, String officialWebsiteURLAsString)
|
|
| 3194 | + throws MalformedURLException {
|
|
| 3145 | 3195 | UUID eventUuid = UUID.randomUUID();
|
| 3146 | 3196 | TimePoint startTimePoint = startDate != null ? new MillisecondsTimePoint(startDate) : null;
|
| 3147 | 3197 | TimePoint endTimePoint = endDate != null ? new MillisecondsTimePoint(endDate) : null;
|
| 3148 | 3198 | getService().apply(
|
| 3149 | - new CreateEvent(eventName, startTimePoint, endTimePoint, venue, isPublic, eventUuid, createURLsFromStrings(imageURLs),
|
|
| 3150 | - createURLsFromStrings(videoURLs)));
|
|
| 3199 | + new CreateEvent(eventName, eventDescription, startTimePoint, endTimePoint, venue, isPublic, eventUuid,
|
|
| 3200 | + createURLsFromStrings(imageURLs), createURLsFromStrings(videoURLs),
|
|
| 3201 | + createURLsFromStrings(sponsorImageURLs),
|
|
| 3202 | + logoImageURLAsString == null ? null : new URL(logoImageURLAsString), officialWebsiteURLAsString == null ? null : new URL(officialWebsiteURLAsString)));
|
|
| 3151 | 3203 | for (String courseAreaName : courseAreaNames) {
|
| 3152 | 3204 | createCourseArea(eventUuid, courseAreaName);
|
| 3153 | 3205 | }
|
| ... | ... | @@ -3219,17 +3271,32 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S |
| 3219 | 3271 | eventDTO.id = (UUID) event.getId();
|
| 3220 | 3272 | eventDTO.setDescription(event.getDescription());
|
| 3221 | 3273 | eventDTO.setOfficialWebsiteURL(event.getOfficialWebsiteURL() != null ? event.getOfficialWebsiteURL().toString() : null);
|
| 3222 | - eventDTO.setLogoImageURL(event.getLogoImageURL() != null ? event.getLogoImageURL().toString() : null);
|
|
| 3274 | + if (event.getLogoImageURL() == null) {
|
|
| 3275 | + eventDTO.setLogoImageURL(null);
|
|
| 3276 | + } else {
|
|
| 3277 | + eventDTO.setLogoImageURL(event.getLogoImageURL().toString());
|
|
| 3278 | + setImageSize(event, eventDTO, event.getLogoImageURL());
|
|
| 3279 | + }
|
|
| 3223 | 3280 | for(URL url: event.getSponsorImageURLs()) {
|
| 3224 | 3281 | eventDTO.addSponsorImageURL(url.toString());
|
| 3282 | + setImageSize(event, eventDTO, url);
|
|
| 3225 | 3283 | }
|
| 3226 | 3284 | for(URL url: event.getImageURLs()) {
|
| 3227 | 3285 | eventDTO.addImageURL(url.toString());
|
| 3286 | + setImageSize(event, eventDTO, url);
|
|
| 3228 | 3287 | }
|
| 3229 | 3288 | for(URL url: event.getVideoURLs()) {
|
| 3230 | 3289 | eventDTO.addVideoURL(url.toString());
|
| 3231 | 3290 | }
|
| 3232 | 3291 | }
|
| 3292 | +
|
|
| 3293 | + private void setImageSize(EventBase event, EventBaseDTO eventDTO, URL imageURL) {
|
|
| 3294 | + try {
|
|
| 3295 | + eventDTO.setImageSize(imageURL.toString(), event.getImageSize(imageURL));
|
|
| 3296 | + } catch (InterruptedException | ExecutionException e) {
|
|
| 3297 | + logger.log(Level.FINE, "Was unable to obtain image size for "+imageURL+" earlier.", e);
|
|
| 3298 | + }
|
|
| 3299 | + }
|
|
| 3233 | 3300 | |
| 3234 | 3301 | private EventDTO convertToEventDTO(Event event, boolean withStatisticalData) {
|
| 3235 | 3302 | EventDTO eventDTO = new EventDTO(event.getName());
|
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java
| ... | ... | @@ -34,9 +34,12 @@ public class CompactRaceMapDataDTO implements IsSerializable { |
| 34 | 34 | for (Map.Entry<CompetitorDTO, List<GPSFixDTO>> e : boatPositions.entrySet()) { |
| 35 | 35 | this.boatPositionsByCompetitorIdAsString.put(e.getKey().getIdAsString(), e.getValue()); |
| 36 | 36 | } |
| 37 | - this.quickRanks = new ArrayList<CompactQuickRankDTO>(quickRanks.size()); |
|
| 38 | - for (QuickRankDTO quickRank : quickRanks) { |
|
| 39 | - this.quickRanks.add(new CompactQuickRankDTO(quickRank.competitor.getIdAsString(), quickRank.rank, quickRank.legNumberOneBased)); |
|
| 37 | + this.quickRanks = new ArrayList<CompactQuickRankDTO>(quickRanks == null ? 0 : quickRanks.size()); |
|
| 38 | + if (quickRanks != null) { |
|
| 39 | + for (QuickRankDTO quickRank : quickRanks) { |
|
| 40 | + this.quickRanks.add(new CompactQuickRankDTO(quickRank.competitor.getIdAsString(), quickRank.rank, |
|
| 41 | + quickRank.legNumberOneBased)); |
|
| 42 | + } |
|
| 40 | 43 | } |
| 41 | 44 | this.courseSidelines = courseSidelines; |
| 42 | 45 | this.coursePositions = coursePositions; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java
| ... | ... | @@ -1,15 +1,22 @@ |
| 1 | 1 | package com.sap.sailing.gwt.ui.shared; |
| 2 | 2 | |
| 3 | 3 | import java.util.ArrayList; |
| 4 | +import java.util.Collections; |
|
| 5 | +import java.util.Comparator; |
|
| 4 | 6 | import java.util.Date; |
| 7 | +import java.util.HashMap; |
|
| 5 | 8 | import java.util.List; |
| 9 | +import java.util.Map; |
|
| 6 | 10 | import java.util.UUID; |
| 7 | 11 | |
| 8 | 12 | import com.google.gwt.user.client.rpc.IsSerializable; |
| 13 | +import com.sap.sailing.domain.common.ImageSize; |
|
| 9 | 14 | import com.sap.sailing.domain.common.dto.NamedDTO; |
| 15 | +import com.sap.sse.common.Util; |
|
| 10 | 16 | |
| 11 | 17 | public class EventBaseDTO extends NamedDTO implements IsSerializable { |
| 12 | 18 | private static final long serialVersionUID = 818666323178097939L; |
| 19 | + private static final String STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE = "stage"; |
|
| 13 | 20 | public VenueDTO venue; |
| 14 | 21 | public Date startDate; |
| 15 | 22 | public Date endDate; |
| ... | ... | @@ -26,6 +33,11 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { |
| 26 | 33 | /** placeholder for social media URL's -> attributes will be implemented later on */ |
| 27 | 34 | private String facebookURL; |
| 28 | 35 | private String twitterURL; |
| 36 | + /** |
|
| 37 | + * For the image URL keys holds the sizes of these images if known. An image size is "known" by this object if it was |
|
| 38 | + * provided to the {@link #setImageSize} method. |
|
| 39 | + */ |
|
| 40 | + private Map<String, ImageSize> imageSizes; |
|
| 29 | 41 | |
| 30 | 42 | /** |
| 31 | 43 | * The base URL for the server instance on which the data for this event can be reached. Could be something like |
| ... | ... | @@ -45,11 +57,13 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { |
| 45 | 57 | |
| 46 | 58 | public EventBaseDTO(List<? extends LeaderboardGroupBaseDTO> leaderboardGroups) { |
| 47 | 59 | this.leaderboardGroups = leaderboardGroups; |
| 60 | + this.imageSizes = new HashMap<String, ImageSize>(); |
|
| 48 | 61 | } |
| 49 | 62 | |
| 50 | 63 | public EventBaseDTO(String name, List<? extends LeaderboardGroupBaseDTO> leaderboardGroups) { |
| 51 | 64 | super(name); |
| 52 | 65 | this.leaderboardGroups = leaderboardGroups; |
| 66 | + this.imageSizes = new HashMap<String, ImageSize>(); |
|
| 53 | 67 | } |
| 54 | 68 | |
| 55 | 69 | public boolean isRunning() { |
| ... | ... | @@ -113,21 +127,63 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { |
| 113 | 127 | return imageURLs; |
| 114 | 128 | } |
| 115 | 129 | |
| 130 | + /** |
|
| 131 | + * The stage image is determined from the {@link #imageURLs} collection by a series of heuristics and fall-back rules: |
|
| 132 | + * <ol> |
|
| 133 | + * <li>If one or more image URLs has "stage" (ignoring case) in its name, only they are considered candidates.</li> |
|
| 134 | + * <li>If no image URL has "stage" (ignoring case) in its name, all images from {@link #imageURLs} are considered candidates.</li> |
|
| 135 | + * <li>From all candidates, the one with the biggest known size (determined by the product of width and height) is chosen.</li> |
|
| 136 | + * <li>If the size isn't known for any candidate, the first candidate in {@link #imageURLs} is picked.</li> |
|
| 137 | + * </ol> |
|
| 138 | + */ |
|
| 116 | 139 | public String getStageImageURL() { |
| 117 | - String result = null; |
|
| 118 | - for(String imageUrl: imageURLs) { |
|
| 119 | - if(imageUrl.contains("stage") || imageUrl.contains("STAGE")) { |
|
| 120 | - result = imageUrl; |
|
| 121 | - break; |
|
| 122 | - } |
|
| 140 | + final String result; |
|
| 141 | + if (imageURLs.isEmpty()) { |
|
| 142 | + result = null; |
|
| 143 | + } else { |
|
| 144 | + Comparator<String> stageImageComparator = new Comparator<String>() { |
|
| 145 | + @Override |
|
| 146 | + public int compare(String o1, String o2) { |
|
| 147 | + final int result; |
|
| 148 | + if (o1.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { |
|
| 149 | + if (o2.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { |
|
| 150 | + result = compareBySize(o1, o2); |
|
| 151 | + } else { |
|
| 152 | + // o1 has stage indicator substring in its URL but o2 doesn't; o1 ranks greater |
|
| 153 | + result = 1; |
|
| 154 | + } |
|
| 155 | + } else { |
|
| 156 | + if (o2.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { |
|
| 157 | + result = -1; // o1 does not and o2 does have stage indicator substring, so o2 ranks greater |
|
| 158 | + } else { |
|
| 159 | + // both don't have stage indicator; compare by size |
|
| 160 | + result = compareBySize(o1, o2); |
|
| 161 | + } |
|
| 162 | + } |
|
| 163 | + return result; |
|
| 164 | + } |
|
| 165 | + |
|
| 166 | + private int compareBySize(String o1, String o2) { |
|
| 167 | + final int result; |
|
| 168 | + final ImageSize o1Size = getImageSize(o1); |
|
| 169 | + final ImageSize o2Size = getImageSize(o2); |
|
| 170 | + result = (o1Size == null ? 0 : (o1Size.getWidth() * o1Size.getHeight())) |
|
| 171 | + - (o2Size == null ? 0 : (o2Size.getWidth() * o2Size.getHeight())); |
|
| 172 | + return result; |
|
| 173 | + } |
|
| 174 | + }; |
|
| 175 | + List<String> sortedImageURLs = new ArrayList<>(imageURLs); |
|
| 176 | + Collections.sort(sortedImageURLs, stageImageComparator); |
|
| 177 | + result = sortedImageURLs.get(sortedImageURLs.size() - 1); |
|
| 123 | 178 | } |
| 124 | 179 | return result; |
| 125 | 180 | } |
| 126 | 181 | |
| 127 | 182 | public List<String> getPhotoGalleryImageURLs() { |
| 183 | + String stageImageURL = getStageImageURL(); // if set, exclude stage image from photo gallery |
|
| 128 | 184 | List<String> result = new ArrayList<String>(); |
| 129 | - for(String imageUrl: imageURLs) { |
|
| 130 | - if(!imageUrl.contains("stage") && !imageUrl.contains("STAGE")) { |
|
| 185 | + for (String imageUrl : imageURLs) { |
|
| 186 | + if (imageURLs.size() == 1 || !Util.equalsWithNull(imageUrl, stageImageURL)) { |
|
| 131 | 187 | result.add(imageUrl); |
| 132 | 188 | } |
| 133 | 189 | } |
| ... | ... | @@ -169,5 +225,21 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { |
| 169 | 225 | public void setTwitterURL(String twitterURL) { |
| 170 | 226 | this.twitterURL = twitterURL; |
| 171 | 227 | } |
| 228 | + |
|
| 229 | + public void setImageSize(String imageURL, ImageSize imageSize) { |
|
| 230 | + if (imageSize == null) { |
|
| 231 | + imageSizes.remove(imageURL); |
|
| 232 | + } else { |
|
| 233 | + imageSizes.put(imageURL, imageSize); |
|
| 234 | + } |
|
| 235 | + } |
|
| 172 | 236 | |
| 237 | + /** |
|
| 238 | + * @return the size of the image referenced by <code>imageURL</code> or <code>null</code> if that size is not known |
|
| 239 | + * of the image URL is none of those known to this event, in particular neither of {@link #getImageURLs()} |
|
| 240 | + * or {@link #getSponsorImageURLs()}. |
|
| 241 | + */ |
|
| 242 | + public ImageSize getImageSize(String imageURL) { |
|
| 243 | + return imageSizes.get(imageURL); |
|
| 244 | + } |
|
| 173 | 245 | } |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/MarkVectorGraphics.java
| ... | ... | @@ -36,20 +36,14 @@ public class MarkVectorGraphics { |
| 36 | 36 | this.markWidthInMeters = 1.5; |
| 37 | 37 | } |
| 38 | 38 | |
| 39 | - public void drawMarkToCanvas(Context2d ctx, boolean isSelected, |
|
| 40 | - double width, double height, double scaleFactor) { |
|
| 41 | - |
|
| 39 | + public void drawMarkToCanvas(Context2d ctx, boolean isSelected, double width, double height, double scaleFactor) { |
|
| 42 | 40 | ctx.save(); |
| 43 | 41 | ctx.clearRect(0, 0, width, height); |
| 44 | - |
|
| 45 | 42 | ctx.translate(width / 2.0, height / 2.0); |
| 46 | 43 | ctx.scale(scaleFactor, scaleFactor); |
| 47 | - |
|
| 48 | 44 | ctx.translate(-anchorPointX * 100,- anchorPointY * 100); |
| 49 | - |
|
| 50 | 45 | String markColor = color != null ? color : DEFAULT_MARK_COLOR; |
| 51 | 46 | drawMark(ctx, isSelected, markColor); |
| 52 | - |
|
| 53 | 47 | ctx.restore(); |
| 54 | 48 | } |
| 55 | 49 | |
| ... | ... | @@ -61,19 +55,19 @@ public class MarkVectorGraphics { |
| 61 | 55 | if(Shape.CYLINDER.name().equalsIgnoreCase(shape) && pattern != null && Pattern.CHECKERED.name().equalsIgnoreCase(pattern)) { |
| 62 | 56 | drawBuoyWithFinishFlag(ctx, isSelected, color); |
| 63 | 57 | } else if (Shape.CONICAL.name().equalsIgnoreCase(shape)) { |
| 64 | - drawConicalBuoy(ctx, isSelected, color); |
|
| 58 | + drawConicalBuoy(ctx, color); |
|
| 65 | 59 | } else { |
| 66 | - drawSimpleBuoy(ctx, isSelected, color); |
|
| 60 | + drawSimpleBuoy(ctx, color); |
|
| 67 | 61 | } |
| 68 | 62 | } else { |
| 69 | - drawSimpleBuoy(ctx, isSelected, color); |
|
| 63 | + drawSimpleBuoy(ctx, color); |
|
| 70 | 64 | } |
| 71 | 65 | break; |
| 72 | 66 | case FINISHBOAT: |
| 73 | 67 | drawFinishBoat(ctx, isSelected, color); |
| 74 | 68 | break; |
| 75 | 69 | case LANDMARK: |
| 76 | - drawLandmark(ctx, isSelected, color); |
|
| 70 | + drawLandmark(ctx, color); |
|
| 77 | 71 | break; |
| 78 | 72 | case STARTBOAT: |
| 79 | 73 | drawStartBoat(ctx, isSelected, color); |
| ... | ... | @@ -87,7 +81,7 @@ public class MarkVectorGraphics { |
| 87 | 81 | } |
| 88 | 82 | } |
| 89 | 83 | |
| 90 | - protected void drawSimpleBuoy(Context2d ctx, boolean isSelected, String color) { |
|
| 84 | + void drawSimpleBuoy(Context2d ctx, String color) { |
|
| 91 | 85 | ctx.setStrokeStyle("rgba(0,0,0,0)"); |
| 92 | 86 | |
| 93 | 87 | ctx.save(); |
| ... | ... | @@ -336,7 +330,7 @@ public class MarkVectorGraphics { |
| 336 | 330 | ctx.restore(); |
| 337 | 331 | } |
| 338 | 332 | |
| 339 | - protected void drawLandmark(Context2d ctx, boolean isSelected, String color) { |
|
| 333 | + protected void drawLandmark(Context2d ctx, String color) { |
|
| 340 | 334 | ctx.setStrokeStyle("rgba(0,0,0,0)"); |
| 341 | 335 | |
| 342 | 336 | ctx.save(); |
| ... | ... | @@ -401,7 +395,7 @@ public class MarkVectorGraphics { |
| 401 | 395 | ctx.restore(); |
| 402 | 396 | } |
| 403 | 397 | |
| 404 | - protected void drawConicalBuoy(Context2d ctx, boolean isSelected, String color) { |
|
| 398 | + protected void drawConicalBuoy(Context2d ctx, String color) { |
|
| 405 | 399 | ctx.setStrokeStyle("rgba(0,0,0,0)"); |
| 406 | 400 | |
| 407 | 401 | ctx.save(); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathCanvasOverlay.java
| ... | ... | @@ -12,6 +12,7 @@ import com.google.gwt.maps.client.MapWidget; |
| 12 | 12 | import com.google.gwt.maps.client.base.LatLng; |
| 13 | 13 | import com.google.gwt.maps.client.base.Point; |
| 14 | 14 | import com.google.gwt.maps.client.geometrylib.SphericalUtils; |
| 15 | +import com.sap.sailing.domain.common.AbstractBearing; |
|
| 15 | 16 | import com.sap.sailing.domain.common.Mile; |
| 16 | 17 | import com.sap.sailing.domain.common.dto.PositionDTO; |
| 17 | 18 | import com.sap.sailing.domain.common.impl.DegreeBearingImpl; |
| ... | ... | @@ -164,7 +165,7 @@ public class PathCanvasOverlay extends WindFieldCanvasOverlay implements Named { |
| 164 | 165 | //if ((displayWindAlongPath) && ((index % arrowInterleave) == 0)) { |
| 165 | 166 | if (displayWindAlongPath) { |
| 166 | 167 | if (checkPointsAreFarEnough(windDTO,prevWindDTO)) { |
| 167 | - DegreeBearingImpl dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); |
|
| 168 | + AbstractBearing dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); |
|
| 168 | 169 | // System.out.print("index: "+index+"\n"); |
| 169 | 170 | |
| 170 | 171 | drawScaledArrow(windDTO, dbi.getRadians(), index, true); |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java
| ... | ... | @@ -9,6 +9,7 @@ import com.google.gwt.canvas.dom.client.TextMetrics; |
| 9 | 9 | import com.google.gwt.i18n.client.DateTimeFormat; |
| 10 | 10 | import com.google.gwt.i18n.client.TimeZone; |
| 11 | 11 | import com.google.gwt.maps.client.MapWidget; |
| 12 | +import com.sap.sailing.domain.common.AbstractBearing; |
|
| 12 | 13 | import com.sap.sailing.domain.common.impl.DegreeBearingImpl; |
| 13 | 14 | import com.sap.sailing.gwt.ui.simulator.racemap.FullCanvasOverlay; |
| 14 | 15 | import com.sap.sailing.simulator.util.SailingSimulatorConstants; |
| ... | ... | @@ -102,7 +103,7 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay { |
| 102 | 103 | // |
| 103 | 104 | // TODO: draw current arrow |
| 104 | 105 | // |
| 105 | - DegreeBearingImpl curBear = new DegreeBearingImpl(this.curBearing); |
|
| 106 | + AbstractBearing curBear = new DegreeBearingImpl(this.curBearing); |
|
| 106 | 107 | |
| 107 | 108 | //Context2d context2d = canvas.getContext2d(); |
| 108 | 109 |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindFieldCanvasOverlay.java
| ... | ... | @@ -14,6 +14,7 @@ import java.util.logging.Logger; |
| 14 | 14 | import com.google.gwt.canvas.dom.client.Context2d; |
| 15 | 15 | import com.google.gwt.event.shared.HandlerRegistration; |
| 16 | 16 | import com.google.gwt.maps.client.MapWidget; |
| 17 | +import com.sap.sailing.domain.common.AbstractBearing; |
|
| 17 | 18 | import com.sap.sailing.domain.common.impl.DegreeBearingImpl; |
| 18 | 19 | import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; |
| 19 | 20 | import com.sap.sailing.gwt.ui.shared.WindFieldDTO; |
| ... | ... | @@ -149,7 +150,7 @@ public class WindFieldCanvasOverlay extends FullCanvasOverlay implements TimeLis |
| 149 | 150 | while (windDTOIter.hasNext()) { |
| 150 | 151 | final SimulatorWindDTO windDTO = windDTOIter.next(); |
| 151 | 152 | //System.out.println("wind angle: "+windDTO.trueWindBearingDeg); |
| 152 | - final DegreeBearingImpl dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); |
|
| 153 | + final AbstractBearing dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); |
|
| 153 | 154 | drawScaledArrow(windDTO, dbi.getRadians(), ++index, drawHead); |
| 154 | 155 | } |
| 155 | 156 | final String title = "Wind Field at " + windDTOList.size() + " points."; |
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/WindLineGuidesCanvasOverlay.java
| ... | ... | @@ -15,6 +15,7 @@ import com.google.gwt.canvas.dom.client.Context2d; |
| 15 | 15 | import com.google.gwt.maps.client.MapWidget; |
| 16 | 16 | import com.google.gwt.maps.client.base.LatLng; |
| 17 | 17 | import com.google.gwt.maps.client.base.Point; |
| 18 | +import com.sap.sailing.domain.common.AbstractBearing; |
|
| 18 | 19 | import com.sap.sailing.domain.common.dto.PositionDTO; |
| 19 | 20 | import com.sap.sailing.domain.common.impl.DegreeBearingImpl; |
| 20 | 21 | import com.sap.sailing.gwt.ui.shared.SimulatorWindDTO; |
| ... | ... | @@ -158,7 +159,7 @@ public class WindLineGuidesCanvasOverlay extends FullCanvasOverlay implements Ti |
| 158 | 159 | SimulatorWindDTO windDTO = windDTOIter.next(); |
| 159 | 160 | //System.out.println("wind angle: "+index+", "+windDTO.trueWindBearingDeg); |
| 160 | 161 | if (((index % xRes) > 0)&&((index % xRes) < (xRes-1))) { |
| 161 | - DegreeBearingImpl dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); |
|
| 162 | + AbstractBearing dbi = new DegreeBearingImpl(windDTO.trueWindBearingDeg); |
|
| 162 | 163 | drawScaledArrow(windDTO, dbi.getRadians(), index, pxLength, drawHead); |
| 163 | 164 | } |
| 164 | 165 | index++; |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/RaceBoard.gwt.xml
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/D35.png
| ... | ... | Binary files /dev/null and b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/D35.png differ |
java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/client/images/boatclass/EXTREME40.png
java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/MediaDBTest.java
| ... | ... | @@ -1,7 +1,6 @@ |
| 1 | 1 | package com.sap.sailing.media.persistence.test; |
| 2 | 2 | |
| 3 | 3 | import java.net.UnknownHostException; |
| 4 | -import java.util.Date; |
|
| 5 | 4 | import java.util.List; |
| 6 | 5 | |
| 7 | 6 | import org.bson.types.ObjectId; |
| ... | ... | @@ -9,6 +8,10 @@ import org.hamcrest.core.Is; |
| 9 | 8 | import org.junit.Test; |
| 10 | 9 | |
| 11 | 10 | import com.mongodb.MongoException; |
| 11 | +import com.sap.sailing.domain.common.Duration; |
|
| 12 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 13 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 14 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 12 | 15 | import com.sap.sailing.domain.common.media.MediaTrack.MimeType; |
| 13 | 16 | import com.sap.sailing.domain.persistence.media.DBMediaTrack; |
| 14 | 17 | import com.sap.sailing.domain.persistence.media.MediaDB; |
| ... | ... | @@ -29,10 +32,10 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 29 | 32 | final String videoTitle = "Test Video"; |
| 30 | 33 | final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; |
| 31 | 34 | MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 32 | - Date date = new Date(); |
|
| 33 | - int durationInMillis = 23; |
|
| 35 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 36 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 34 | 37 | String mimeType = MimeType.ogv.name(); |
| 35 | - String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mimeType); |
|
| 38 | + String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, duration, mimeType); |
|
| 36 | 39 | assertNotNull(dbId); |
| 37 | 40 | DBMediaTrack videoTrack = mongoDB.loadAllMediaTracks().iterator().next(); |
| 38 | 41 | assertNotNull(videoTrack); |
| ... | ... | @@ -46,10 +49,10 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 46 | 49 | final String videoTitle = "Test Video"; |
| 47 | 50 | final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; |
| 48 | 51 | MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 49 | - Date date = new Date(); |
|
| 50 | - int durationInMillis = 23; |
|
| 52 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 53 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 51 | 54 | String mimeType = MimeType.ogv.name(); |
| 52 | - mongoDB.insertMediaTrackWithId(dbId, videoTitle, url, date, durationInMillis, mimeType); |
|
| 55 | + mongoDB.insertMediaTrackWithId(dbId, videoTitle, url, startTime, duration, mimeType); |
|
| 53 | 56 | DBMediaTrack videoTrack = mongoDB.loadAllMediaTracks().iterator().next(); |
| 54 | 57 | assertNotNull(videoTrack); |
| 55 | 58 | assertThat(videoTrack.dbId, Is.is(dbId)); |
| ... | ... | @@ -62,10 +65,10 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 62 | 65 | final String videoTitle = "Test Video"; |
| 63 | 66 | final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; |
| 64 | 67 | MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 65 | - Date date = new Date(); |
|
| 66 | - int durationInMillis = 23; |
|
| 68 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 69 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 67 | 70 | String mimeType = MimeType.ogv.name(); |
| 68 | - mongoDB.insertMediaTrackWithId(dbId, videoTitle, url, date, durationInMillis, mimeType); |
|
| 71 | + mongoDB.insertMediaTrackWithId(dbId, videoTitle, url, startTime, duration, mimeType); |
|
| 69 | 72 | } |
| 70 | 73 | |
| 71 | 74 | @Test(expected=IllegalArgumentException.class) |
| ... | ... | @@ -73,11 +76,11 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 73 | 76 | final String videoTitle = "Test Video"; |
| 74 | 77 | final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; |
| 75 | 78 | MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 76 | - Date date = new Date(); |
|
| 77 | - int durationInMillis = 23; |
|
| 79 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 80 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 78 | 81 | String mimeType = MimeType.ogv.name(); |
| 79 | - String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mimeType); |
|
| 80 | - mongoDB.insertMediaTrackWithId(dbId, videoTitle, url, date, durationInMillis, mimeType); |
|
| 82 | + String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, duration, mimeType); |
|
| 83 | + mongoDB.insertMediaTrackWithId(dbId, videoTitle, url, startTime, duration, mimeType); |
|
| 81 | 84 | } |
| 82 | 85 | |
| 83 | 86 | // @Test |
| ... | ... | @@ -87,12 +90,12 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 87 | 90 | // final String title = "Test Video"; |
| 88 | 91 | // final String url = "test"; |
| 89 | 92 | // Date date = new Date(); |
| 90 | -// int durationInMillis = 23; |
|
| 93 | +// int duration = 23; |
|
| 91 | 94 | // String mimeType = MimeType.ogv.name(); |
| 92 | 95 | // |
| 93 | 96 | // MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 94 | 97 | // |
| 95 | -// mongoDB.importMediaTrack(dbId, title, url, date, durationInMillis, mimeType); |
|
| 98 | +// mongoDB.importMediaTrack(dbId, title, url, date, duration, mimeType); |
|
| 96 | 99 | // |
| 97 | 100 | // List<DBMediaTrack> allMediaTracks = mongoDB.loadAllMediaTracks(); |
| 98 | 101 | // assertThat(allMediaTracks.size(), is(1)); |
| ... | ... | @@ -101,7 +104,7 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 101 | 104 | // assertThat(dbMediaTrack.title, is(title)); |
| 102 | 105 | // assertThat(dbMediaTrack.url, is(url)); |
| 103 | 106 | // assertThat(dbMediaTrack.startTime, is(date)); |
| 104 | -// assertThat(dbMediaTrack.durationInMillis, is(durationInMillis)); |
|
| 107 | +// assertThat(dbMediaTrack.duration, is(duration)); |
|
| 105 | 108 | // assertThat(dbMediaTrack.mimeType, is(mimeType)); |
| 106 | 109 | // } |
| 107 | 110 | // |
| ... | ... | @@ -112,12 +115,12 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 112 | 115 | // final String title = "Test Video"; |
| 113 | 116 | // final String url = "test"; |
| 114 | 117 | // Date date = new Date(); |
| 115 | -// int durationInMillis = 23; |
|
| 118 | +// int duration = 23; |
|
| 116 | 119 | // String mimeType = MimeType.ogv.name(); |
| 117 | 120 | // |
| 118 | 121 | // MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 119 | 122 | // |
| 120 | -// mongoDB.importMediaTrack(dbId, title, url, date, durationInMillis, mimeType); |
|
| 123 | +// mongoDB.importMediaTrack(dbId, title, url, date, duration, mimeType); |
|
| 121 | 124 | // } |
| 122 | 125 | // |
| 123 | 126 | // @Test |
| ... | ... | @@ -126,14 +129,14 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 126 | 129 | // final String title = "Test Video"; |
| 127 | 130 | // final String url = "test"; |
| 128 | 131 | // Date date = new Date(); |
| 129 | -// int durationInMillis = 23; |
|
| 132 | +// int duration = 23; |
|
| 130 | 133 | // String mimeType = MimeType.ogv.name(); |
| 131 | 134 | // |
| 132 | 135 | // MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 133 | -// String dbId = mongoDB.insertMediaTrack(title, url, date, durationInMillis, mimeType); |
|
| 136 | +// String dbId = mongoDB.insertMediaTrack(title, url, date, duration, mimeType); |
|
| 134 | 137 | // |
| 135 | 138 | // String newTitle = title + "x"; |
| 136 | -// boolean trackCreated = mongoDB.importMediaTrack(dbId, newTitle, url, date, durationInMillis, mimeType); |
|
| 139 | +// boolean trackCreated = mongoDB.importMediaTrack(dbId, newTitle, url, date, duration, mimeType); |
|
| 137 | 140 | // |
| 138 | 141 | // assertThat(trackCreated, equalTo(true)); |
| 139 | 142 | // List<DBMediaTrack> allMediaTracks = mongoDB.loadAllMediaTracks(); |
| ... | ... | @@ -143,7 +146,7 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 143 | 146 | // assertThat(dbMediaTrack.title, is(newTitle)); |
| 144 | 147 | // assertThat(dbMediaTrack.url, is(url)); |
| 145 | 148 | // assertThat(dbMediaTrack.startTime, is(date)); |
| 146 | -// assertThat(dbMediaTrack.durationInMillis, is(durationInMillis)); |
|
| 149 | +// assertThat(dbMediaTrack.duration, is(duration)); |
|
| 147 | 150 | // assertThat(dbMediaTrack.mimeType, is(mimeType)); |
| 148 | 151 | // } |
| 149 | 152 | // |
| ... | ... | @@ -153,14 +156,14 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 153 | 156 | // final String title = "Test Video"; |
| 154 | 157 | // final String url = "test"; |
| 155 | 158 | // Date date = new Date(); |
| 156 | -// int durationInMillis = 23; |
|
| 159 | +// int duration = 23; |
|
| 157 | 160 | // String mimeType = MimeType.ogv.name(); |
| 158 | 161 | // |
| 159 | 162 | // MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 160 | -// String dbId = mongoDB.insertMediaTrack(title, url, date, durationInMillis, mimeType); |
|
| 163 | +// String dbId = mongoDB.insertMediaTrack(title, url, date, duration, mimeType); |
|
| 161 | 164 | // |
| 162 | 165 | // String newUrl = url + "x"; |
| 163 | -// boolean trackCreated = mongoDB.importMediaTrack(dbId, title, newUrl, date, durationInMillis, mimeType); |
|
| 166 | +// boolean trackCreated = mongoDB.importMediaTrack(dbId, title, newUrl, date, duration, mimeType); |
|
| 164 | 167 | // |
| 165 | 168 | // assertThat(trackCreated, equalTo(true)); |
| 166 | 169 | // List<DBMediaTrack> allMediaTracks = mongoDB.loadAllMediaTracks(); |
| ... | ... | @@ -170,7 +173,7 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 170 | 173 | // assertThat(dbMediaTrack.title, is(title)); |
| 171 | 174 | // assertThat(dbMediaTrack.url, is(newUrl)); |
| 172 | 175 | // assertThat(dbMediaTrack.startTime, is(date)); |
| 173 | -// assertThat(dbMediaTrack.durationInMillis, is(durationInMillis)); |
|
| 176 | +// assertThat(dbMediaTrack.duration, is(duration)); |
|
| 174 | 177 | // assertThat(dbMediaTrack.mimeType, is(mimeType)); |
| 175 | 178 | // } |
| 176 | 179 | // |
| ... | ... | @@ -180,14 +183,14 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 180 | 183 | // final String title = "Test Video"; |
| 181 | 184 | // final String url = "test"; |
| 182 | 185 | // Date startTime = new Date(); |
| 183 | -// int durationInMillis = 23; |
|
| 186 | +// int duration = 23; |
|
| 184 | 187 | // String mimeType = MimeType.ogv.name(); |
| 185 | 188 | // |
| 186 | 189 | // MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 187 | -// String dbId = mongoDB.insertMediaTrack(title, url, startTime, durationInMillis, mimeType); |
|
| 190 | +// String dbId = mongoDB.insertMediaTrack(title, url, startTime, duration, mimeType); |
|
| 188 | 191 | // |
| 189 | 192 | // Date newStartTime = new Date(startTime.getTime() + 1); |
| 190 | -// boolean trackCreated = mongoDB.importMediaTrack(dbId, title, url, newStartTime, durationInMillis, mimeType); |
|
| 193 | +// boolean trackCreated = mongoDB.importMediaTrack(dbId, title, url, newStartTime, duration, mimeType); |
|
| 191 | 194 | // |
| 192 | 195 | // assertThat(trackCreated, equalTo(true)); |
| 193 | 196 | // List<DBMediaTrack> allMediaTracks = mongoDB.loadAllMediaTracks(); |
| ... | ... | @@ -197,7 +200,7 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 197 | 200 | // assertThat(dbMediaTrack.title, is(title)); |
| 198 | 201 | // assertThat(dbMediaTrack.url, is(url)); |
| 199 | 202 | // assertThat(dbMediaTrack.startTime, is(newStartTime)); |
| 200 | -// assertThat(dbMediaTrack.durationInMillis, is(durationInMillis)); |
|
| 203 | +// assertThat(dbMediaTrack.duration, is(duration)); |
|
| 201 | 204 | // assertThat(dbMediaTrack.mimeType, is(mimeType)); |
| 202 | 205 | // } |
| 203 | 206 | // |
| ... | ... | @@ -207,13 +210,13 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 207 | 210 | // final String title = "Test Video"; |
| 208 | 211 | // final String url = "test"; |
| 209 | 212 | // Date startTime = new Date(); |
| 210 | -// int durationInMillis = 23; |
|
| 213 | +// int duration = 23; |
|
| 211 | 214 | // String mimeType = MimeType.ogv.name(); |
| 212 | 215 | // |
| 213 | 216 | // MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 214 | -// String dbId = mongoDB.insertMediaTrack(title, url, startTime, durationInMillis, mimeType); |
|
| 217 | +// String dbId = mongoDB.insertMediaTrack(title, url, startTime, duration, mimeType); |
|
| 215 | 218 | // |
| 216 | -// int newDurationInMillis = durationInMillis + 1; |
|
| 219 | +// int newDurationInMillis = duration + 1; |
|
| 217 | 220 | // boolean trackCreated = mongoDB.importMediaTrack(dbId, title, url, startTime, newDurationInMillis, mimeType); |
| 218 | 221 | // |
| 219 | 222 | // assertThat(trackCreated, equalTo(true)); |
| ... | ... | @@ -224,7 +227,7 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 224 | 227 | // assertThat(dbMediaTrack.title, is(title)); |
| 225 | 228 | // assertThat(dbMediaTrack.url, is(url)); |
| 226 | 229 | // assertThat(dbMediaTrack.startTime, is(startTime)); |
| 227 | -// assertThat(dbMediaTrack.durationInMillis, is(newDurationInMillis)); |
|
| 230 | +// assertThat(dbMediaTrack.duration, is(newDurationInMillis)); |
|
| 228 | 231 | // assertThat(dbMediaTrack.mimeType, is(mimeType)); |
| 229 | 232 | // } |
| 230 | 233 | // |
| ... | ... | @@ -240,12 +243,12 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 240 | 243 | private void storeNumberOfTestMediaTracks(MediaDB mongoDB, int count) { |
| 241 | 244 | final String videoTitleTemplate = "Test Video "; |
| 242 | 245 | final String url = "http://localhost:8888/media/HTML5/1809147112001_1842870496001_SAP-Regatta-Day02-Final_libtheora.ogv"; |
| 243 | - Date date = new Date(); |
|
| 244 | - int durationInMillis = 23; |
|
| 246 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 247 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 245 | 248 | String mimeType = MimeType.ogv.name(); |
| 246 | 249 | |
| 247 | 250 | for (int i = 0; i < count; i++) { |
| 248 | - mongoDB.insertMediaTrack(videoTitleTemplate + i, url, date, durationInMillis, mimeType); |
|
| 251 | + mongoDB.insertMediaTrack(videoTitleTemplate + i, url, startTime, duration, mimeType); |
|
| 249 | 252 | } |
| 250 | 253 | } |
| 251 | 254 | |
| ... | ... | @@ -255,10 +258,10 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 255 | 258 | final String videoTitle = "Test Video"; |
| 256 | 259 | final String url = "test"; |
| 257 | 260 | MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 258 | - Date date = new Date(); |
|
| 259 | - int durationInMillis = 23; |
|
| 261 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 262 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 260 | 263 | String mimeType = MimeType.ogv.name(); |
| 261 | - String dbId = mongoDB.insertMediaTrack(videoTitle, url, date, durationInMillis, mimeType); |
|
| 264 | + String dbId = mongoDB.insertMediaTrack(videoTitle, url, startTime, duration, mimeType); |
|
| 262 | 265 | |
| 263 | 266 | //delete |
| 264 | 267 | mongoDB.deleteMediaTrack(dbId); |
| ... | ... | @@ -273,19 +276,19 @@ public class MediaDBTest extends AbstractMongoDBTest { |
| 273 | 276 | final String videoTitle = "Test Video"; |
| 274 | 277 | final String url = "test"; |
| 275 | 278 | MediaDB mongoDB = MediaDBFactory.INSTANCE.getMediaDB(getMongoService()); |
| 276 | - Date originalDate = new Date(); |
|
| 277 | - int durationInMillis = 23; |
|
| 279 | + TimePoint originalDate = MillisecondsTimePoint.now(); |
|
| 280 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 278 | 281 | String mimeType = MimeType.ogv.name(); |
| 279 | - String dbId = mongoDB.insertMediaTrack(videoTitle, url, originalDate, durationInMillis, mimeType); |
|
| 282 | + String dbId = mongoDB.insertMediaTrack(videoTitle, url, originalDate, duration, mimeType); |
|
| 280 | 283 | |
| 281 | 284 | //update with new date |
| 282 | - Date newDate = new Date(originalDate.getTime() + 1000); |
|
| 285 | + TimePoint newDate = originalDate.plus(1000); |
|
| 283 | 286 | mongoDB.updateStartTime(dbId, newDate); |
| 284 | 287 | |
| 285 | 288 | //assert start time is updated and everything else preserved |
| 286 | 289 | DBMediaTrack videoTrack = mongoDB.loadAllMediaTracks().iterator().next(); |
| 287 | 290 | assertThat(videoTrack.startTime, is(newDate)); |
| 288 | - assertThat(videoTrack.durationInMillis, is(durationInMillis)); |
|
| 291 | + assertThat(videoTrack.duration, is(duration)); |
|
| 289 | 292 | assertThat(videoTrack.title, is(videoTitle)); |
| 290 | 293 | assertThat(videoTrack.url, is(url)); |
| 291 | 294 | assertThat(videoTrack.mimeType, is(mimeType)); |
java/com.sap.sailing.operationaltransformation/.classpath
| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | -<?xml version="1.0" encoding="UTF-8"?>
|
|
| 2 | -<classpath>
|
|
| 3 | - <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
|
| 4 | - <classpathentry kind="src" path="src"/>
|
|
| 5 | - <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
|
|
| 6 | - <classpathentry kind="output" path="bin"/>
|
|
| 7 | -</classpath>
|
|
| 1 | +<?xml version="1.0" encoding="UTF-8"?> |
|
| 2 | +<classpath> |
|
| 3 | + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> |
|
| 4 | + <classpathentry kind="src" path="src"/> |
|
| 5 | + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/> |
|
| 6 | + <classpathentry kind="output" path="bin"/> |
|
| 7 | +</classpath> |
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/EventBaseJsonDeserializer.java
| ... | ... | @@ -12,8 +12,8 @@ import org.json.simple.JSONObject; |
| 12 | 12 | import com.sap.sailing.domain.base.EventBase; |
| 13 | 13 | import com.sap.sailing.domain.base.LeaderboardGroupBase; |
| 14 | 14 | import com.sap.sailing.domain.base.Venue; |
| 15 | -import com.sap.sailing.domain.base.impl.EventBaseImpl; |
|
| 16 | 15 | import com.sap.sailing.domain.base.impl.StrippedEventImpl; |
| 16 | +import com.sap.sailing.domain.common.impl.ImageSizeImpl; |
|
| 17 | 17 | import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
| 18 | 18 | import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; |
| 19 | 19 | import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; |
| ... | ... | @@ -50,7 +50,7 @@ public class EventBaseJsonDeserializer implements JsonDeserializer<EventBase> { |
| 50 | 50 | leaderboardGroups.add(leaderboardGroupDeserializer.deserialize((JSONObject) lgJson)); |
| 51 | 51 | } |
| 52 | 52 | } |
| 53 | - EventBaseImpl result = new StrippedEventImpl(name, startDate == null ? null : new MillisecondsTimePoint(startDate.longValue()), |
|
| 53 | + StrippedEventImpl result = new StrippedEventImpl(name, startDate == null ? null : new MillisecondsTimePoint(startDate.longValue()), |
|
| 54 | 54 | endDate == null ? null : new MillisecondsTimePoint(endDate.longValue()), venue, /* is public */ true, id, leaderboardGroups); |
| 55 | 55 | result.setDescription(description); |
| 56 | 56 | if (officialWebsiteURLAsString != null) { |
| ... | ... | @@ -88,6 +88,21 @@ public class EventBaseJsonDeserializer implements JsonDeserializer<EventBase> { |
| 88 | 88 | throw new JsonDeserializationException("Error deserializing sponsor image URLs for event "+name, e); |
| 89 | 89 | } |
| 90 | 90 | } |
| 91 | + JSONArray imageSizes = (JSONArray) object.get(EventBaseJsonSerializer.FIELD_IMAGE_SIZES); |
|
| 92 | + if (imageSizes != null) { |
|
| 93 | + for (Object imageURLAndSizeObject : imageSizes) { |
|
| 94 | + JSONObject imageURLAndSizeJson = (JSONObject) imageURLAndSizeObject; |
|
| 95 | + try { |
|
| 96 | + result.setImageSize( |
|
| 97 | + new URL((String) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_URL)), |
|
| 98 | + new ImageSizeImpl( |
|
| 99 | + ((Number) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_WIDTH)).intValue(), |
|
| 100 | + ((Number) imageURLAndSizeJson.get(EventBaseJsonSerializer.FIELD_IMAGE_HEIGHT)).intValue())); |
|
| 101 | + } catch (MalformedURLException e) { |
|
| 102 | + throw new JsonDeserializationException(e); |
|
| 103 | + } |
|
| 104 | + } |
|
| 105 | + } |
|
| 91 | 106 | return result; |
| 92 | 107 | } |
| 93 | 108 |
java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/EventBaseJsonSerializer.java
| ... | ... | @@ -1,6 +1,9 @@ |
| 1 | 1 | package com.sap.sailing.server.gateway.serialization.impl;
|
| 2 | 2 | |
| 3 | 3 | import java.net.URL;
|
| 4 | +import java.util.concurrent.ExecutionException;
|
|
| 5 | +import java.util.logging.Level;
|
|
| 6 | +import java.util.logging.Logger;
|
|
| 4 | 7 | |
| 5 | 8 | import org.json.simple.JSONArray;
|
| 6 | 9 | import org.json.simple.JSONObject;
|
| ... | ... | @@ -8,9 +11,12 @@ import org.json.simple.JSONObject; |
| 8 | 11 | import com.sap.sailing.domain.base.EventBase;
|
| 9 | 12 | import com.sap.sailing.domain.base.LeaderboardGroupBase;
|
| 10 | 13 | import com.sap.sailing.domain.base.Venue;
|
| 14 | +import com.sap.sailing.domain.common.ImageSize;
|
|
| 11 | 15 | import com.sap.sailing.server.gateway.serialization.JsonSerializer;
|
| 12 | 16 | |
| 13 | 17 | public class EventBaseJsonSerializer implements JsonSerializer<EventBase> {
|
| 18 | + private static final Logger logger = Logger.getLogger(EventBaseJsonSerializer.class.getName());
|
|
| 19 | +
|
|
| 14 | 20 | public static final String FIELD_ID = "id";
|
| 15 | 21 | public static final String FIELD_NAME = "name";
|
| 16 | 22 | public static final String FIELD_DESCRIPTION = "description";
|
| ... | ... | @@ -21,6 +27,10 @@ public class EventBaseJsonSerializer implements JsonSerializer<EventBase> { |
| 21 | 27 | public static final String FIELD_VIDEO_URLS = "videoURLs";
|
| 22 | 28 | public static final String FIELD_SPONSOR_IMAGE_URLS = "sponsorImageURLs";
|
| 23 | 29 | public static final String FIELD_LOGO_IMAGE_URL = "logoImageURL";
|
| 30 | + public static final String FIELD_IMAGE_SIZES = "imageSizes";
|
|
| 31 | + public static final String FIELD_IMAGE_URL = "imageURL";
|
|
| 32 | + public static final String FIELD_IMAGE_WIDTH = "imageWidth";
|
|
| 33 | + public static final String FIELD_IMAGE_HEIGHT = "imageHeight";
|
|
| 24 | 34 | public static final String FIELD_OFFICIAL_WEBSITE_URL = "officialWebsiteURL";
|
| 25 | 35 | public static final String FIELDS_LEADERBOARD_GROUPS = "leaderboardGroups";
|
| 26 | 36 | |
| ... | ... | @@ -50,9 +60,42 @@ public class EventBaseJsonSerializer implements JsonSerializer<EventBase> { |
| 50 | 60 | for (LeaderboardGroupBase lg : event.getLeaderboardGroups()) {
|
| 51 | 61 | leaderboardGroups.add(leaderboardGroupBaseSerializer.serialize(lg));
|
| 52 | 62 | }
|
| 63 | + JSONArray imageSizes = new JSONArray();
|
|
| 64 | + result.put(FIELD_IMAGE_SIZES, imageSizes);
|
|
| 65 | + for (URL imageURL : event.getImageURLs()) {
|
|
| 66 | + addImageSize(imageURL, imageSizes, event);
|
|
| 67 | + }
|
|
| 68 | + if (event.getLogoImageURL() != null) {
|
|
| 69 | + addImageSize(event.getLogoImageURL(), imageSizes, event);
|
|
| 70 | + }
|
|
| 71 | + for (URL sponsorImageURL : event.getSponsorImageURLs()) {
|
|
| 72 | + addImageSize(sponsorImageURL, imageSizes, event);
|
|
| 73 | + }
|
|
| 53 | 74 | return result;
|
| 54 | 75 | }
|
| 55 | 76 | |
| 77 | + /**
|
|
| 78 | + * If <code>eventBase</code> knows the size of the image with URL <code>imageURL</code>, that size is serialized as
|
|
| 79 | + * JSON object to <code>imageSizes</code>
|
|
| 80 | + */
|
|
| 81 | + private void addImageSize(URL imageURL, JSONArray imageSizes, EventBase eventBase) {
|
|
| 82 | + ImageSize imageSize;
|
|
| 83 | + try {
|
|
| 84 | + imageSize = eventBase.getImageSize(imageURL);
|
|
| 85 | + if (imageSize != null) {
|
|
| 86 | + JSONObject imageSizeJson = new JSONObject();
|
|
| 87 | + imageSizes.add(imageSizeJson);
|
|
| 88 | + imageSizeJson.put(FIELD_IMAGE_URL, imageURL.toString());
|
|
| 89 | + imageSizeJson.put(FIELD_IMAGE_WIDTH, imageSize.getWidth());
|
|
| 90 | + imageSizeJson.put(FIELD_IMAGE_HEIGHT, imageSize.getHeight());
|
|
| 91 | + }
|
|
| 92 | + } catch (InterruptedException e) {
|
|
| 93 | + logger.log(Level.FINE, "Couldn't retrieve image size for URL "+imageURL, e);
|
|
| 94 | + } catch (ExecutionException e) {
|
|
| 95 | + logger.log(Level.FINE, "Couldn't retrieve image size for URL "+imageURL, e);
|
|
| 96 | + }
|
|
| 97 | + }
|
|
| 98 | +
|
|
| 56 | 99 | private JSONArray getURLsAsStringArray(Iterable<URL> urls) {
|
| 57 | 100 | JSONArray jsonImageURLs = new JSONArray();
|
| 58 | 101 | for (URL url : urls) {
|
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/AbstractSailingServerResource.java
| ... | ... | @@ -1,5 +1,8 @@ |
| 1 | 1 | package com.sap.sailing.server.gateway.jaxrs; |
| 2 | 2 | |
| 3 | +import java.math.BigDecimal; |
|
| 4 | +import java.math.RoundingMode; |
|
| 5 | + |
|
| 3 | 6 | import javax.servlet.ServletContext; |
| 4 | 7 | import javax.ws.rs.core.Context; |
| 5 | 8 | |
| ... | ... | @@ -63,4 +66,10 @@ public abstract class AbstractSailingServerResource { |
| 63 | 66 | } |
| 64 | 67 | return trackedRace; |
| 65 | 68 | } |
| 69 | + |
|
| 70 | + protected static Double roundDouble(Double value, int places) { |
|
| 71 | + BigDecimal bigDecimal = new BigDecimal(value); |
|
| 72 | + bigDecimal = bigDecimal.setScale(places, RoundingMode.HALF_UP); |
|
| 73 | + return bigDecimal.doubleValue(); |
|
| 74 | + } |
|
| 66 | 75 | } |
java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java
| ... | ... | @@ -667,5 +667,85 @@ public class RegattasResource extends AbstractSailingServerResource { |
| 667 | 667 | return response; |
| 668 | 668 | } |
| 669 | 669 | |
| 670 | + @GET |
|
| 671 | + @Produces("application/json;charset=UTF-8") |
|
| 672 | + @Path("{regattaname}/races/{racename}/competitors/live") |
|
| 673 | + public Response getCompetitorLiveRanks(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName, |
|
| 674 | + @DefaultValue("-1") @QueryParam("topN") Integer topN) { |
|
| 675 | + Response response; |
|
| 676 | + Regatta regatta = findRegattaByName(regattaName); |
|
| 677 | + if (regatta == null) { |
|
| 678 | + response = Response.status(Status.NOT_FOUND).entity("Could not find a regatta with name '" + regattaName + "'.").type(MediaType.TEXT_PLAIN).build(); |
|
| 679 | + } else { |
|
| 680 | + RaceDefinition race = findRaceByName(regatta, raceName); |
|
| 681 | + if (race == null) { |
|
| 682 | + response = Response.status(Status.NOT_FOUND).entity("Could not find a race with name '" + raceName + "'.").type(MediaType.TEXT_PLAIN).build(); |
|
| 683 | + } else { |
|
| 684 | + TrackedRace trackedRace = findTrackedRace(regattaName, raceName); |
|
| 685 | + Course course = trackedRace.getRace().getCourse(); |
|
| 686 | + TimePoint timePoint = trackedRace.getTimePointOfNewestEvent() == null ? MillisecondsTimePoint.now() |
|
| 687 | + : trackedRace.getTimePointOfNewestEvent(); |
|
| 688 | + // if(trackedRace.isLive(timePoint)) { |
|
| 689 | + |
|
| 690 | + JSONObject jsonLiveData = new JSONObject(); |
|
| 691 | + jsonLiveData.put("name", trackedRace.getRace().getName()); |
|
| 692 | + jsonLiveData.put("regatta", regatta.getName()); |
|
| 693 | + |
|
| 694 | + JSONArray jsonCompetitors = new JSONArray(); |
|
| 695 | + try { |
|
| 696 | + List<Competitor> competitorsFromBestToWorst = trackedRace.getCompetitorsFromBestToWorst(timePoint); |
|
| 697 | + Integer rank = 1; |
|
| 698 | + for (Competitor competitor : competitorsFromBestToWorst) { |
|
| 699 | + JSONObject jsonCompetitorInLeg = new JSONObject(); |
|
| 700 | + |
|
| 701 | + if(topN != null && topN > 0 && rank > topN) { |
|
| 702 | + break; |
|
| 703 | + } |
|
| 704 | + TrackedLegOfCompetitor currentLegOfCompetitor = trackedRace.getCurrentLeg(competitor, timePoint); |
|
| 705 | + if (currentLegOfCompetitor != null) { |
|
| 706 | + jsonCompetitorInLeg.put("id", competitor.getId() != null ? competitor.getId().toString() : null); |
|
| 707 | + jsonCompetitorInLeg.put("name", competitor.getName()); |
|
| 708 | + jsonCompetitorInLeg.put("sailNumber", competitor.getBoat().getSailID()); |
|
| 709 | + jsonCompetitorInLeg.put("color", competitor.getColor() != null ? competitor.getColor().getAsHtml() : null); |
|
| 710 | + |
|
| 711 | + jsonCompetitorInLeg.put("rank", rank++); |
|
| 712 | + |
|
| 713 | + int indexOfWaypoint = course.getIndexOfWaypoint(currentLegOfCompetitor.getLeg().getFrom()); |
|
| 714 | + jsonCompetitorInLeg.put("leg", indexOfWaypoint); |
|
| 715 | + |
|
| 716 | + Speed speedOverGround = currentLegOfCompetitor.getSpeedOverGround(timePoint); |
|
| 717 | + if(speedOverGround != null) { |
|
| 718 | + jsonCompetitorInLeg.put("speedOverGround-kts", roundDouble(speedOverGround.getKnots(), 2)); |
|
| 719 | + } |
|
| 720 | + |
|
| 721 | + Distance distanceTraveled = currentLegOfCompetitor.getDistanceTraveled(timePoint); |
|
| 722 | + if (distanceTraveled != null) { |
|
| 723 | + jsonCompetitorInLeg.put("distanceTraveled-m", roundDouble(distanceTraveled.getMeters(), 2)); |
|
| 724 | + } |
|
| 725 | + |
|
| 726 | + Double gapToLeaderInSeconds = currentLegOfCompetitor.getGapToLeaderInSeconds(timePoint, WindPositionMode.LEG_MIDDLE); |
|
| 727 | + if(gapToLeaderInSeconds != null) { |
|
| 728 | + jsonCompetitorInLeg.put("gapToLeader-s", roundDouble(gapToLeaderInSeconds, 2)); |
|
| 729 | + } |
|
| 730 | + |
|
| 731 | + Distance windwardDistanceToOverallLeader = currentLegOfCompetitor.getWindwardDistanceToOverallLeader(timePoint, WindPositionMode.LEG_MIDDLE); |
|
| 732 | + if(windwardDistanceToOverallLeader != null) { |
|
| 733 | + jsonCompetitorInLeg.put("gapToLeader-m", roundDouble(windwardDistanceToOverallLeader.getMeters(), 2)); |
|
| 734 | + } |
|
| 735 | + |
|
| 736 | + jsonCompetitors.add(jsonCompetitorInLeg); |
|
| 737 | + } |
|
| 738 | + } |
|
| 739 | + } catch (NoWindException e1) { |
|
| 740 | + // well, we don't know the wind direction... then no gap to leader will be shown... |
|
| 741 | + } |
|
| 742 | + jsonLiveData.put("competitors", jsonCompetitors); |
|
| 743 | + |
|
| 744 | + String json = jsonLiveData.toJSONString(); |
|
| 745 | + return Response.ok(json, MediaType.APPLICATION_JSON).build(); |
|
| 746 | + } |
|
| 747 | + } |
|
| 748 | + return response; |
|
| 749 | + } |
|
| 670 | 750 | } |
| 671 | 751 | |
| ... | ... | \ No newline at end of file |
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ConnectionResetAndReconnectTest.java
| ... | ... | @@ -122,7 +122,7 @@ public class ConnectionResetAndReconnectTest extends AbstractServerReplicationTe |
| 122 | 122 | List<String> regattas = new ArrayList<String>();
|
| 123 | 123 | regattas.add("Day1");
|
| 124 | 124 | regattas.add("Day2");
|
| 125 | - return master.addEvent(eventName, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 125 | + return master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 126 | 126 | }
|
| 127 | 127 | |
| 128 | 128 | private void stopMessagingExchange() {
|
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/EventReplicationTest.java
| ... | ... | @@ -35,7 +35,7 @@ public class EventReplicationTest extends AbstractServerReplicationTest { |
| 35 | 35 | List<String> regattas = new ArrayList<String>();
|
| 36 | 36 | regattas.add("Day1");
|
| 37 | 37 | regattas.add("Day2");
|
| 38 | - Event masterEvent = master.addEvent(eventName, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 38 | + Event masterEvent = master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 39 | 39 | |
| 40 | 40 | Thread.sleep(1000);
|
| 41 | 41 | Event replicatedEvent = replica.getEvent(masterEvent.getId());
|
| ... | ... | @@ -54,7 +54,7 @@ public class EventReplicationTest extends AbstractServerReplicationTest { |
| 54 | 54 | List<String> regattas = new ArrayList<String>();
|
| 55 | 55 | regattas.add("Day1");
|
| 56 | 56 | regattas.add("Day2");
|
| 57 | - final Event masterEvent = master.addEvent(eventName, null, null, venueName, isPublic, UUID.randomUUID());
|
|
| 57 | + final Event masterEvent = master.addEvent(eventName, /* eventDescription */ null, null, null, venueName, isPublic, UUID.randomUUID());
|
|
| 58 | 58 | final String leaderboardGroupName = "LGName";
|
| 59 | 59 | LeaderboardGroup lg = master.apply(new CreateLeaderboardGroup(leaderboardGroupName, "LGDescription", /* displayGroupsInReverseOrder */
|
| 60 | 60 | "displayName", /* leaderboardNames */
|
| ... | ... | @@ -78,7 +78,7 @@ public class EventReplicationTest extends AbstractServerReplicationTest { |
| 78 | 78 | List<String> regattas = new ArrayList<String>();
|
| 79 | 79 | regattas.add("Day1");
|
| 80 | 80 | regattas.add("Day2");
|
| 81 | - Event masterEvent = master.addEvent(eventName, null, null, venueName, isPublic, UUID.randomUUID());
|
|
| 81 | + Event masterEvent = master.addEvent(eventName, /* eventDescription */ null, null, null, venueName, isPublic, UUID.randomUUID());
|
|
| 82 | 82 | |
| 83 | 83 | Thread.sleep(1000);
|
| 84 | 84 | Event replicatedEvent = replica.getEvent(masterEvent.getId());
|
| ... | ... | @@ -97,7 +97,7 @@ public class EventReplicationTest extends AbstractServerReplicationTest { |
| 97 | 97 | final String courseArea = "Alpha";
|
| 98 | 98 | final TimePoint eventStartDate = new MillisecondsTimePoint(new Date());
|
| 99 | 99 | final TimePoint eventEndDate = new MillisecondsTimePoint(new Date());
|
| 100 | - Event masterEvent = master.addEvent(eventName, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 100 | + Event masterEvent = master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 101 | 101 | CourseArea masterCourseArea = master.addCourseArea(masterEvent.getId(), courseArea, UUID.randomUUID());
|
| 102 | 102 | |
| 103 | 103 | Thread.sleep(1000);
|
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java
| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | package com.sap.sailing.server.replication.test;
|
| 2 | 2 | |
| 3 | 3 | import static org.hamcrest.core.Is.is;
|
| 4 | +
|
|
| 4 | 5 | import static org.junit.Assert.assertNotNull;
|
| 5 | 6 | import static org.junit.Assert.assertNull;
|
| 6 | 7 | import static org.junit.Assert.assertSame;
|
| ... | ... | @@ -30,6 +31,7 @@ import com.sap.sailing.domain.base.impl.RegattaImpl; |
| 30 | 31 | import com.sap.sailing.domain.common.RegattaName;
|
| 31 | 32 | import com.sap.sailing.domain.common.ScoringSchemeType;
|
| 32 | 33 | import com.sap.sailing.domain.common.TimePoint;
|
| 34 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl;
|
|
| 33 | 35 | import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
| 34 | 36 | import com.sap.sailing.domain.common.media.MediaTrack;
|
| 35 | 37 | import com.sap.sailing.domain.common.media.MediaTrack.MimeType;
|
| ... | ... | @@ -86,7 +88,7 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl |
| 86 | 88 | final TimePoint eventStartDate = new MillisecondsTimePoint(new Date());
|
| 87 | 89 | final TimePoint eventEndDate = new MillisecondsTimePoint(new Date());
|
| 88 | 90 | |
| 89 | - Event event = master.addEvent(eventName, eventStartDate, eventEndDate, venue, false, eventId);
|
|
| 91 | + Event event = master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venue, false, eventId);
|
|
| 90 | 92 | assertNotNull(master.getEvent(eventId));
|
| 91 | 93 | assertNull(replica.getEvent(eventId));
|
| 92 | 94 | |
| ... | ... | @@ -123,11 +125,11 @@ public class InitialLoadReplicationObjectIdentityTest extends AbstractServerRepl |
| 123 | 125 | event.addLeaderboardGroup(leaderboardGroup);
|
| 124 | 126 | |
| 125 | 127 | /* Media Library */
|
| 126 | - MediaTrack mediaTrack1 = new MediaTrack("title-1", "url", new Date(), 1, MimeType.mp4);
|
|
| 128 | + MediaTrack mediaTrack1 = new MediaTrack("title-1", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, MimeType.mp4);
|
|
| 127 | 129 | master.mediaTrackAdded(mediaTrack1);
|
| 128 | - MediaTrack mediaTrack2 = new MediaTrack("title-2", "url", new Date(), 1, MimeType.ogv);
|
|
| 130 | + MediaTrack mediaTrack2 = new MediaTrack("title-2", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, MimeType.ogv);
|
|
| 129 | 131 | master.mediaTrackAdded(mediaTrack2);
|
| 130 | - MediaTrack mediaTrack3 = new MediaTrack("title-3", "url", new Date(), 1, MimeType.mp4);
|
|
| 132 | + MediaTrack mediaTrack3 = new MediaTrack("title-3", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, MimeType.mp4);
|
|
| 131 | 133 | master.mediaTrackAdded(mediaTrack3);
|
| 132 | 134 | |
| 133 | 135 | /* fire up replication */
|
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java
| ... | ... | @@ -1,17 +1,18 @@ |
| 1 | 1 | package com.sap.sailing.server.replication.test; |
| 2 | 2 | |
| 3 | -import static org.hamcrest.core.Is.is; |
|
| 4 | -import static org.junit.Assert.assertEquals; |
|
| 5 | -import static org.junit.Assert.assertNotSame; |
|
| 6 | -import static org.junit.Assert.assertThat; |
|
| 7 | - |
|
| 8 | -import java.util.Date; |
|
| 9 | - |
|
| 10 | 3 | import org.junit.Test; |
| 11 | 4 | |
| 5 | +import com.sap.sailing.domain.common.Duration; |
|
| 6 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 7 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 8 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 12 | 9 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 13 | 10 | import com.sap.sailing.domain.common.media.MediaTrack.MimeType; |
| 14 | 11 | |
| 12 | +import static org.junit.Assert.*; |
|
| 13 | + |
|
| 14 | +import static org.hamcrest.core.Is.*; |
|
| 15 | + |
|
| 15 | 16 | public class MediaReplicationTest extends AbstractServerReplicationTest { |
| 16 | 17 | |
| 17 | 18 | private void waitSomeTime() throws InterruptedException { |
| ... | ... | @@ -22,10 +23,10 @@ public class MediaReplicationTest extends AbstractServerReplicationTest { |
| 22 | 23 | private MediaTrack createMediaTrack() { |
| 23 | 24 | String title = "title"; |
| 24 | 25 | String url = "url"; |
| 25 | - Date startTime = new Date(); |
|
| 26 | - int durationInMillis = 1; |
|
| 26 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 27 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 27 | 28 | MimeType mimeType = MimeType.mp4; |
| 28 | - MediaTrack mediaTrack = new MediaTrack(title, url, startTime, durationInMillis, mimeType); |
|
| 29 | + MediaTrack mediaTrack = new MediaTrack(title, url, startTime, duration, mimeType); |
|
| 29 | 30 | return mediaTrack; |
| 30 | 31 | } |
| 31 | 32 | |
| ... | ... | @@ -81,7 +82,7 @@ public class MediaReplicationTest extends AbstractServerReplicationTest { |
| 81 | 82 | public void testUpdateMediaTrackStartTimeReplication() throws InterruptedException { |
| 82 | 83 | MediaTrack mediaTrack = createMediaTrack(); |
| 83 | 84 | master.mediaTrackAdded(mediaTrack); |
| 84 | - mediaTrack.startTime = new Date(mediaTrack.startTime.getTime() + 1000); |
|
| 85 | + mediaTrack.startTime = mediaTrack.startTime.plus(1000); |
|
| 85 | 86 | master.mediaTrackStartTimeChanged(mediaTrack); |
| 86 | 87 | waitSomeTime(); |
| 87 | 88 | assertThat(replica.getAllMediaTracks().size(), is(1)); |
| ... | ... | @@ -92,11 +93,11 @@ public class MediaReplicationTest extends AbstractServerReplicationTest { |
| 92 | 93 | public void testUpdateMediaTrackDurationReplication() throws InterruptedException { |
| 93 | 94 | MediaTrack mediaTrack = createMediaTrack(); |
| 94 | 95 | master.mediaTrackAdded(mediaTrack); |
| 95 | - mediaTrack.durationInMillis = mediaTrack.durationInMillis + 1000; |
|
| 96 | + mediaTrack.duration = mediaTrack.duration.plus(1000); |
|
| 96 | 97 | master.mediaTrackDurationChanged(mediaTrack); |
| 97 | 98 | waitSomeTime(); |
| 98 | 99 | assertThat(replica.getAllMediaTracks().size(), is(1)); |
| 99 | - assertThat(replica.getAllMediaTracks().iterator().next().durationInMillis, is(mediaTrack.durationInMillis)); |
|
| 100 | + assertThat(replica.getAllMediaTracks().iterator().next().duration, is(mediaTrack.duration)); |
|
| 100 | 101 | } |
| 101 | 102 | |
| 102 | 103 | } |
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/RegattaReplicationTest.java
| ... | ... | @@ -66,7 +66,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { |
| 66 | 66 | final TimePoint eventStartDate = new MillisecondsTimePoint(new Date());
|
| 67 | 67 | final TimePoint eventEndDate = new MillisecondsTimePoint(new Date());
|
| 68 | 68 | |
| 69 | - Event event = master.addEvent("Event", eventStartDate, eventEndDate, "Venue", true, UUID.randomUUID());
|
|
| 69 | + Event event = master.addEvent("Event", /* eventDescription */ null, eventStartDate, eventEndDate, "Venue", true, UUID.randomUUID());
|
|
| 70 | 70 | master.addCourseArea(event.getId(), "Alpha", alphaCourseAreaId);
|
| 71 | 71 | master.addCourseArea(event.getId(), "TV", tvCourseAreaId);
|
| 72 | 72 | |
| ... | ... | @@ -112,7 +112,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { |
| 112 | 112 | final UUID golfCourseAreaId = UUID.randomUUID();
|
| 113 | 113 | final TimePoint eventStartDate = new MillisecondsTimePoint(new Date());
|
| 114 | 114 | final TimePoint eventEndDate = new MillisecondsTimePoint(new Date());
|
| 115 | - Event event = master.addEvent("Event", eventStartDate, eventEndDate, "Venue", /*isPublic*/true, UUID.randomUUID());
|
|
| 115 | + Event event = master.addEvent("Event", /* eventDescription */ null, eventStartDate, eventEndDate, "Venue", /*isPublic*/true, UUID.randomUUID());
|
|
| 116 | 116 | master.addCourseArea(event.getId(), "TV", tvCourseAreaId);
|
| 117 | 117 | master.addCourseArea(event.getId(), "Golf", golfCourseAreaId);
|
| 118 | 118 | final String regattaName = RegattaImpl.getDefaultName("Kiel Week 2012", "49er");
|
| ... | ... | @@ -137,7 +137,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { |
| 137 | 137 | final TimePoint eventStartDate = new MillisecondsTimePoint(new Date());
|
| 138 | 138 | final TimePoint eventEndDate = new MillisecondsTimePoint(new Date());
|
| 139 | 139 | |
| 140 | - Event event = master.addEvent("Event", eventStartDate, eventEndDate, "Venue", true, UUID.randomUUID());
|
|
| 140 | + Event event = master.addEvent("Event", /* eventDescription */ null, eventStartDate, eventEndDate, "Venue", true, UUID.randomUUID());
|
|
| 141 | 141 | master.addCourseArea(event.getId(), "Alpha", alphaCourseAreaId);
|
| 142 | 142 | |
| 143 | 143 | UUID currentCourseAreaId = null;
|
| ... | ... | @@ -295,7 +295,7 @@ public class RegattaReplicationTest extends AbstractServerReplicationTest { |
| 295 | 295 | final String courseArea = "Alpha";
|
| 296 | 296 | final TimePoint eventStartDate = new MillisecondsTimePoint(new Date());
|
| 297 | 297 | final TimePoint eventEndDate = new MillisecondsTimePoint(new Date());
|
| 298 | - Event masterEvent = master.addEvent(eventName, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 298 | + Event masterEvent = master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 299 | 299 | CourseArea masterCourseArea = master.addCourseArea(masterEvent.getId(), courseArea, UUID.randomUUID());
|
| 300 | 300 | |
| 301 | 301 | Regatta masterRegatta = master.createRegatta(RegattaImpl.getDefaultName(eventName, boatClassName), boatClassName, UUID.randomUUID(), series,
|
java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/ReplicationStopTest.java
| ... | ... | @@ -38,7 +38,7 @@ public class ReplicationStopTest extends AbstractServerReplicationTest { |
| 38 | 38 | List<String> regattas = new ArrayList<String>();
|
| 39 | 39 | regattas.add("Day1");
|
| 40 | 40 | regattas.add("Day2");
|
| 41 | - return master.addEvent(eventName, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 41 | + return master.addEvent(eventName, /* eventDescription */ null, eventStartDate, eventEndDate, venueName, isPublic, UUID.randomUUID());
|
|
| 42 | 42 | }
|
| 43 | 43 | |
| 44 | 44 |
java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java
| ... | ... | @@ -3,7 +3,9 @@ package com.sap.sailing.server.impl; |
| 3 | 3 | import static org.hamcrest.CoreMatchers.is; |
| 4 | 4 | import static org.hamcrest.CoreMatchers.not; |
| 5 | 5 | import static org.hamcrest.CoreMatchers.sameInstance; |
| 6 | + |
|
| 6 | 7 | import static org.junit.Assert.assertThat; |
| 8 | + |
|
| 7 | 9 | import static org.mockito.Matchers.any; |
| 8 | 10 | import static org.mockito.Matchers.eq; |
| 9 | 11 | import static org.mockito.Matchers.same; |
| ... | ... | @@ -16,13 +18,14 @@ import static org.mockito.Mockito.when; |
| 16 | 18 | import java.util.ArrayList; |
| 17 | 19 | import java.util.Arrays; |
| 18 | 20 | import java.util.Collection; |
| 19 | -import java.util.Date; |
|
| 20 | 21 | |
| 21 | 22 | import org.bson.types.ObjectId; |
| 22 | 23 | import org.junit.Before; |
| 23 | 24 | import org.junit.Test; |
| 24 | 25 | import org.mockito.Mockito; |
| 25 | 26 | |
| 27 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 28 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 26 | 29 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 27 | 30 | import com.sap.sailing.domain.common.media.MediaTrack.MimeType; |
| 28 | 31 | import com.sap.sailing.domain.persistence.DomainObjectFactory; |
| ... | ... | @@ -89,7 +92,7 @@ public class MasterDataImporterMediaTest { |
| 89 | 92 | createRacingEventService(); |
| 90 | 93 | |
| 91 | 94 | String dbId = new ObjectId().toStringMongod(); |
| 92 | - MediaTrack mediaTrackToImport = new MediaTrack(dbId, "title", "url", new Date(), 0, MimeType.mp3); |
|
| 95 | + MediaTrack mediaTrackToImport = new MediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, MimeType.mp3); |
|
| 93 | 96 | mediaTracksToImport.add(mediaTrackToImport); |
| 94 | 97 | racingEventService.mediaTracksImported(mediaTracksToImport, OVERRIDE); |
| 95 | 98 | |
| ... | ... | @@ -111,11 +114,11 @@ public class MasterDataImporterMediaTest { |
| 111 | 114 | public void testImportOneTrackToSameTarget_WithOverride() throws Exception { |
| 112 | 115 | String dbId = new ObjectId().toStringMongod(); |
| 113 | 116 | MimeType mimeType = MimeType.mp3; |
| 114 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 117 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 115 | 118 | createRacingEventService(existingMediaTrack); |
| 116 | 119 | Collection<MediaTrack> allMediaTracksBeforeImport = racingEventService.getAllMediaTracks(); |
| 117 | 120 | |
| 118 | - MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.durationInMillis, mimeType); |
|
| 121 | + MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.duration, mimeType); |
|
| 119 | 122 | mediaTracksToImport.add(mediaTrackToImport); |
| 120 | 123 | racingEventService.mediaTracksImported(mediaTracksToImport, OVERRIDE); |
| 121 | 124 | |
| ... | ... | @@ -134,11 +137,11 @@ public class MasterDataImporterMediaTest { |
| 134 | 137 | public void testImportOneTrackToExistingOtherTrack_WithOverride() throws Exception { |
| 135 | 138 | String dbId = new ObjectId().toStringMongod(); |
| 136 | 139 | MimeType mimeType = MimeType.mp3; |
| 137 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 140 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 138 | 141 | createRacingEventService(existingMediaTrack); |
| 139 | 142 | |
| 140 | 143 | String dbId2 = new ObjectId().toStringMongod(); |
| 141 | - MediaTrack mediaTrackToImport = new MediaTrack(dbId2, existingMediaTrack.title, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.durationInMillis, mimeType); |
|
| 144 | + MediaTrack mediaTrackToImport = new MediaTrack(dbId2, existingMediaTrack.title, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.duration, mimeType); |
|
| 142 | 145 | mediaTracksToImport.add(mediaTrackToImport); |
| 143 | 146 | racingEventService.mediaTracksImported(mediaTracksToImport, OVERRIDE); |
| 144 | 147 | |
| ... | ... | @@ -158,10 +161,10 @@ public class MasterDataImporterMediaTest { |
| 158 | 161 | public void testImportOneTrackToSameTargetWithChangedTitle_WithOverride() throws Exception { |
| 159 | 162 | String dbId = new ObjectId().toStringMongod(); |
| 160 | 163 | MimeType mimeType = MimeType.mp3; |
| 161 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 164 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 162 | 165 | createRacingEventService(existingMediaTrack); |
| 163 | 166 | |
| 164 | - MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title + "x", existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.durationInMillis, mimeType); |
|
| 167 | + MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title + "x", existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.duration, mimeType); |
|
| 165 | 168 | assertThat(existingMediaTrack.title, is(not(mediaTrackToImport.title))); |
| 166 | 169 | |
| 167 | 170 | mediaTracksToImport.add(mediaTrackToImport); |
| ... | ... | @@ -185,10 +188,10 @@ public class MasterDataImporterMediaTest { |
| 185 | 188 | public void testImportOneTrackWithNullTitleToSameTargetWithTitle_WithOverride() throws Exception { |
| 186 | 189 | String dbId = new ObjectId().toStringMongod(); |
| 187 | 190 | MimeType mimeType = MimeType.mp3; |
| 188 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 191 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 189 | 192 | createRacingEventService(existingMediaTrack); |
| 190 | 193 | |
| 191 | - MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, null, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.durationInMillis, mimeType); |
|
| 194 | + MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, null, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.duration, mimeType); |
|
| 192 | 195 | assertThat(existingMediaTrack.title, is(not(mediaTrackToImport.title))); |
| 193 | 196 | |
| 194 | 197 | mediaTracksToImport.add(mediaTrackToImport); |
| ... | ... | @@ -211,10 +214,10 @@ public class MasterDataImporterMediaTest { |
| 211 | 214 | public void testImportOneTrackToSameTargetWithChangedTitle_NoOverride() throws Exception { |
| 212 | 215 | String dbId = new ObjectId().toStringMongod(); |
| 213 | 216 | MimeType mimeType = MimeType.mp3; |
| 214 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 217 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 215 | 218 | createRacingEventService(existingMediaTrack); |
| 216 | 219 | |
| 217 | - MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title + "x", existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.durationInMillis, mimeType); |
|
| 220 | + MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title + "x", existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.duration, mimeType); |
|
| 218 | 221 | assertThat(existingMediaTrack.title, is(not(mediaTrackToImport.title))); |
| 219 | 222 | |
| 220 | 223 | mediaTracksToImport.add(mediaTrackToImport); |
| ... | ... | @@ -237,10 +240,10 @@ public class MasterDataImporterMediaTest { |
| 237 | 240 | public void testImportOneTrackToSameTargetWithChangedUrl_WithOverride() throws Exception { |
| 238 | 241 | String dbId = new ObjectId().toStringMongod(); |
| 239 | 242 | MimeType mimeType = MimeType.mp3; |
| 240 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 243 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 241 | 244 | createRacingEventService(existingMediaTrack); |
| 242 | 245 | |
| 243 | - MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url + "x", existingMediaTrack.startTime, existingMediaTrack.durationInMillis, mimeType); |
|
| 246 | + MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url + "x", existingMediaTrack.startTime, existingMediaTrack.duration, mimeType); |
|
| 244 | 247 | assertThat(existingMediaTrack.url, is(not(mediaTrackToImport.url))); |
| 245 | 248 | |
| 246 | 249 | mediaTracksToImport.add(mediaTrackToImport); |
| ... | ... | @@ -263,10 +266,10 @@ public class MasterDataImporterMediaTest { |
| 263 | 266 | public void testImportOneTrackToSameTargetWithChangedStarttime_WithOverride() throws Exception { |
| 264 | 267 | String dbId = new ObjectId().toStringMongod(); |
| 265 | 268 | MimeType mimeType = MimeType.mp3; |
| 266 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 269 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 267 | 270 | createRacingEventService(existingMediaTrack); |
| 268 | 271 | |
| 269 | - MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url, new Date(existingMediaTrack.startTime.getTime() + 1), existingMediaTrack.durationInMillis, mimeType); |
|
| 272 | + MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url, existingMediaTrack.startTime.plus(1), existingMediaTrack.duration, mimeType); |
|
| 270 | 273 | assertThat(existingMediaTrack.startTime, is(not(mediaTrackToImport.startTime))); |
| 271 | 274 | |
| 272 | 275 | mediaTracksToImport.add(mediaTrackToImport); |
| ... | ... | @@ -289,11 +292,11 @@ public class MasterDataImporterMediaTest { |
| 289 | 292 | public void testImportOneTrackToSameTargetWithChangedDuration_WithOverride() throws Exception { |
| 290 | 293 | String dbId = new ObjectId().toStringMongod(); |
| 291 | 294 | MimeType mimeType = MimeType.mp3; |
| 292 | - DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", new Date(), 0, mimeType.name()); |
|
| 295 | + DBMediaTrack existingMediaTrack = new DBMediaTrack(dbId, "title", "url", MillisecondsTimePoint.now(), MillisecondsDurationImpl.ONE_HOUR, mimeType.name()); |
|
| 293 | 296 | createRacingEventService(existingMediaTrack); |
| 294 | 297 | |
| 295 | - MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.durationInMillis + 1, mimeType); |
|
| 296 | - assertThat(existingMediaTrack.durationInMillis, is(not(mediaTrackToImport.durationInMillis))); |
|
| 298 | + MediaTrack mediaTrackToImport = new MediaTrack(existingMediaTrack.dbId, existingMediaTrack.title, existingMediaTrack.url, existingMediaTrack.startTime, existingMediaTrack.duration.plus(1), mimeType); |
|
| 299 | + assertThat(existingMediaTrack.duration, is(not(mediaTrackToImport.duration))); |
|
| 297 | 300 | |
| 298 | 301 | mediaTracksToImport.add(mediaTrackToImport); |
| 299 | 302 | racingEventService.mediaTracksImported(mediaTracksToImport, OVERRIDE); |
| ... | ... | @@ -301,7 +304,7 @@ public class MasterDataImporterMediaTest { |
| 301 | 304 | Collection<MediaTrack> allMediaTracksAfterImport = racingEventService.getAllMediaTracks(); |
| 302 | 305 | assertThat(allMediaTracksAfterImport.size(), is(1)); |
| 303 | 306 | MediaTrack mediaTrack = allMediaTracksAfterImport.iterator().next(); |
| 304 | - assertThat(mediaTrack.durationInMillis, is(mediaTrackToImport.durationInMillis)); |
|
| 307 | + assertThat(mediaTrack.duration, is(mediaTrackToImport.duration)); |
|
| 305 | 308 | |
| 306 | 309 | verify(racingEventService, never()).mediaTrackAdded(any(MediaTrack.class)); |
| 307 | 310 | verify(racingEventService, never()).mediaTrackDeleted(any(MediaTrack.class)); |
java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaLibaryTest.java
| ... | ... | @@ -1,29 +1,31 @@ |
| 1 | 1 | package com.sap.sailing.server.impl; |
| 2 | 2 | |
| 3 | -import static org.hamcrest.core.Is.is; |
|
| 4 | -import static org.junit.Assert.assertEquals; |
|
| 5 | -import static org.junit.Assert.assertFalse; |
|
| 6 | -import static org.junit.Assert.assertThat; |
|
| 7 | -import static org.junit.Assert.assertTrue; |
|
| 8 | - |
|
| 9 | 3 | import java.util.Collection; |
| 10 | -import java.util.Date; |
|
| 11 | 4 | import java.util.Iterator; |
| 12 | 5 | import java.util.Set; |
| 13 | 6 | |
| 14 | 7 | import org.junit.Before; |
| 15 | 8 | import org.junit.Test; |
| 16 | 9 | |
| 10 | +import com.sap.sailing.domain.common.Duration; |
|
| 11 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 12 | +import com.sap.sailing.domain.common.TimeRange; |
|
| 13 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 14 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
|
| 15 | +import com.sap.sailing.domain.common.impl.TimeRangeImpl; |
|
| 17 | 16 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 18 | 17 | import com.sap.sailing.domain.common.media.MediaTrack.MimeType; |
| 19 | -import com.sap.sailing.server.impl.MediaLibrary.Interval; |
|
| 18 | + |
|
| 19 | +import static org.junit.Assert.*; |
|
| 20 | + |
|
| 21 | +import static org.hamcrest.Matchers.*; |
|
| 20 | 22 | |
| 21 | 23 | |
| 22 | 24 | public class MediaLibaryTest { |
| 23 | 25 | |
| 24 | - private static final int FIFTEEN_MINUTES_IN_MILLIS = 15 * 60 * 1000; |
|
| 25 | - private static final int THIRTY_MINUTES_IN_MILLIS = 30 * 60 * 1000; |
|
| 26 | - private static final int ONE_HOUR_IN_MILLIS = 60 * 60 * 1000; |
|
| 26 | + private static final Duration FIFTEEN_MINUTES_IN_MILLIS = MillisecondsDurationImpl.ONE_MINUTE.times(15); |
|
| 27 | + private static final Duration THIRTY_MINUTES_IN_MILLIS = MillisecondsDurationImpl.ONE_MINUTE.times(30); |
|
| 28 | + private static final Duration ONE_HOUR_IN_MILLIS = MillisecondsDurationImpl.ONE_HOUR; |
|
| 27 | 29 | |
| 28 | 30 | private MediaLibrary mediaLibary; |
| 29 | 31 | |
| ... | ... | @@ -36,10 +38,10 @@ public class MediaLibaryTest { |
| 36 | 38 | private MediaTrack createMediaTrack(String dbId) { |
| 37 | 39 | String title = "title"; |
| 38 | 40 | String url = "url"; |
| 39 | - Date startTime = new Date(); |
|
| 40 | - int durationInMillis = 1; |
|
| 41 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 42 | + Duration duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 41 | 43 | MimeType mimeType = MimeType.mp4; |
| 42 | - MediaTrack mediaTrack = new MediaTrack(dbId, title, url, startTime, durationInMillis, mimeType); |
|
| 44 | + MediaTrack mediaTrack = new MediaTrack(dbId, title, url, startTime, duration, mimeType); |
|
| 43 | 45 | return mediaTrack; |
| 44 | 46 | } |
| 45 | 47 | |
| ... | ... | @@ -61,7 +63,7 @@ public class MediaLibaryTest { |
| 61 | 63 | assertThat(item.title, is(mediaTrack.title)); |
| 62 | 64 | assertThat(item.url, is(mediaTrack.url)); |
| 63 | 65 | assertThat(item.startTime, is(mediaTrack.startTime)); |
| 64 | - assertThat(item.durationInMillis, is(mediaTrack.durationInMillis)); |
|
| 66 | + assertThat(item.duration, is(mediaTrack.duration)); |
|
| 65 | 67 | assertThat(item.mimeType, is(mediaTrack.mimeType)); |
| 66 | 68 | |
| 67 | 69 | } |
| ... | ... | @@ -78,80 +80,80 @@ public class MediaLibaryTest { |
| 78 | 80 | |
| 79 | 81 | @Test |
| 80 | 82 | public void testQueryMediaTracksBetween_MatchingStartTimes_TrackLongerThanEndTime() { |
| 81 | - Date startTime = new Date(); |
|
| 82 | - int duration = ONE_HOUR_IN_MILLIS; |
|
| 83 | - Date rangeStart = startTime; |
|
| 84 | - Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); |
|
| 83 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 84 | + Duration duration = ONE_HOUR_IN_MILLIS; |
|
| 85 | + TimePoint rangeStart = startTime; |
|
| 86 | + TimePoint rangeEnd = rangeStart.plus(THIRTY_MINUTES_IN_MILLIS); |
|
| 85 | 87 | |
| 86 | 88 | assertOverlap(startTime, duration, rangeStart, rangeEnd); |
| 87 | 89 | } |
| 88 | 90 | |
| 89 | 91 | @Test |
| 90 | 92 | public void testQueryMediaTracksBetween_MatchingStartTimes_TrackShorterThanEndTime() { |
| 91 | - Date startTime = new Date(); |
|
| 92 | - int duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 93 | - Date rangeStart = startTime; |
|
| 94 | - Date rangeEnd = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); |
|
| 93 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 94 | + Duration duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 95 | + TimePoint rangeStart = startTime; |
|
| 96 | + TimePoint rangeEnd = rangeStart.plus(ONE_HOUR_IN_MILLIS); |
|
| 95 | 97 | |
| 96 | 98 | assertOverlap(startTime, duration, rangeStart, rangeEnd); |
| 97 | 99 | } |
| 98 | 100 | |
| 99 | 101 | @Test |
| 100 | 102 | public void testQueryMediaTracksBetween_StartsBeforeEndsAfterRange() { |
| 101 | - Date startTime = new Date(); |
|
| 102 | - int duration = ONE_HOUR_IN_MILLIS; |
|
| 103 | - Date rangeStart = new Date(startTime.getTime() + FIFTEEN_MINUTES_IN_MILLIS); |
|
| 104 | - Date rangeEnd = new Date(rangeStart.getTime() + FIFTEEN_MINUTES_IN_MILLIS); |
|
| 103 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 104 | + Duration duration = ONE_HOUR_IN_MILLIS; |
|
| 105 | + TimePoint rangeStart = startTime.plus(FIFTEEN_MINUTES_IN_MILLIS); |
|
| 106 | + TimePoint rangeEnd = rangeStart.plus(FIFTEEN_MINUTES_IN_MILLIS); |
|
| 105 | 107 | |
| 106 | 108 | assertOverlap(startTime, duration, rangeStart, rangeEnd); |
| 107 | 109 | } |
| 108 | 110 | |
| 109 | 111 | @Test |
| 110 | 112 | public void testQueryMediaTracksBetween_StartsAfterEndsBeforeRange() { |
| 111 | - Date rangeStart = new Date(); |
|
| 112 | - Date rangeEnd = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); |
|
| 113 | - Date startTime = new Date(rangeStart.getTime() + FIFTEEN_MINUTES_IN_MILLIS); |
|
| 114 | - int duration = FIFTEEN_MINUTES_IN_MILLIS; |
|
| 113 | + TimePoint rangeStart = MillisecondsTimePoint.now(); |
|
| 114 | + TimePoint rangeEnd = rangeStart.plus(ONE_HOUR_IN_MILLIS); |
|
| 115 | + TimePoint startTime = rangeStart.plus(FIFTEEN_MINUTES_IN_MILLIS); |
|
| 116 | + Duration duration = FIFTEEN_MINUTES_IN_MILLIS; |
|
| 115 | 117 | |
| 116 | 118 | assertOverlap(startTime, duration, rangeStart, rangeEnd); |
| 117 | 119 | } |
| 118 | 120 | |
| 119 | 121 | @Test |
| 120 | 122 | public void testQueryMediaTracksBetween_StartsAfterEndsAfterRange() { |
| 121 | - Date rangeStart = new Date(); |
|
| 122 | - Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); |
|
| 123 | - Date startTime = new Date(rangeStart.getTime() + FIFTEEN_MINUTES_IN_MILLIS); |
|
| 124 | - int duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 123 | + TimePoint rangeStart = MillisecondsTimePoint.now(); |
|
| 124 | + TimePoint rangeEnd = rangeStart.plus(THIRTY_MINUTES_IN_MILLIS); |
|
| 125 | + TimePoint startTime = rangeStart.plus(FIFTEEN_MINUTES_IN_MILLIS); |
|
| 126 | + Duration duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 125 | 127 | |
| 126 | 128 | assertOverlap(startTime, duration, rangeStart, rangeEnd); |
| 127 | 129 | } |
| 128 | 130 | |
| 129 | 131 | @Test |
| 130 | 132 | public void testQueryMediaTracksBetween_StartsBeforeEndsBeforeRange() { |
| 131 | - Date startTime = new Date(); |
|
| 132 | - int duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 133 | - Date rangeStart = new Date(startTime.getTime() + FIFTEEN_MINUTES_IN_MILLIS); |
|
| 134 | - Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); |
|
| 133 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 134 | + Duration duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 135 | + TimePoint rangeStart = startTime.plus(FIFTEEN_MINUTES_IN_MILLIS); |
|
| 136 | + TimePoint rangeEnd = rangeStart.plus(THIRTY_MINUTES_IN_MILLIS); |
|
| 135 | 137 | |
| 136 | 138 | assertOverlap(startTime, duration, rangeStart, rangeEnd); |
| 137 | 139 | } |
| 138 | 140 | |
| 139 | 141 | @Test |
| 140 | 142 | public void testQueryMediaTracksBetween_TrackEndsBeforeRange() { |
| 141 | - Date startTime = new Date(); |
|
| 142 | - int duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 143 | - Date rangeStart = new Date(startTime.getTime() + duration + 1); |
|
| 144 | - Date rangeEnd = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); |
|
| 143 | + TimePoint startTime = MillisecondsTimePoint.now(); |
|
| 144 | + Duration duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 145 | + TimePoint rangeStart = startTime.plus(duration).plus(1); |
|
| 146 | + TimePoint rangeEnd = rangeStart.plus(ONE_HOUR_IN_MILLIS); |
|
| 145 | 147 | |
| 146 | 148 | assertNoOverlap(startTime, duration, rangeStart, rangeEnd); |
| 147 | 149 | } |
| 148 | 150 | |
| 149 | 151 | @Test |
| 150 | 152 | public void testQueryMediaTracksBetween_TrackStartsAfterRange() { |
| 151 | - Date rangeStart = new Date(); |
|
| 152 | - Date rangeEnd = new Date(rangeStart.getTime() + THIRTY_MINUTES_IN_MILLIS); |
|
| 153 | - Date startTime = new Date(rangeStart.getTime() + ONE_HOUR_IN_MILLIS); |
|
| 154 | - int duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 153 | + TimePoint rangeStart = MillisecondsTimePoint.now(); |
|
| 154 | + TimePoint rangeEnd = rangeStart.plus(THIRTY_MINUTES_IN_MILLIS); |
|
| 155 | + TimePoint startTime = rangeStart.plus(ONE_HOUR_IN_MILLIS); |
|
| 156 | + Duration duration = THIRTY_MINUTES_IN_MILLIS; |
|
| 155 | 157 | |
| 156 | 158 | assertNoOverlap(startTime, duration, rangeStart, rangeEnd); |
| 157 | 159 | } |
| ... | ... | @@ -160,11 +162,11 @@ public class MediaLibaryTest { |
| 160 | 162 | public void testCacheChangeStartTime() { |
| 161 | 163 | MediaTrack originalMediaTrack = new MediaTrack(); |
| 162 | 164 | originalMediaTrack.dbId = "a"; |
| 163 | - originalMediaTrack.startTime = new Date(); |
|
| 164 | - originalMediaTrack.durationInMillis = 100; |
|
| 165 | + originalMediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 166 | + originalMediaTrack.duration = new MillisecondsDurationImpl(1); |
|
| 165 | 167 | |
| 166 | - Date queryStartTime = new Date(originalMediaTrack.startTime.getTime() + 1); |
|
| 167 | - Date queryEndTime = new Date(originalMediaTrack.deriveEndTime().getTime() - 1); |
|
| 168 | + TimePoint queryStartTime = originalMediaTrack.startTime.minus(1); |
|
| 169 | + TimePoint queryEndTime = originalMediaTrack.deriveEndTime().plus(1); |
|
| 168 | 170 | |
| 169 | 171 | mediaLibary.addMediaTrack(originalMediaTrack); |
| 170 | 172 | |
| ... | ... | @@ -173,8 +175,8 @@ public class MediaLibaryTest { |
| 173 | 175 | |
| 174 | 176 | MediaTrack changedMediaTrack = new MediaTrack(); |
| 175 | 177 | changedMediaTrack.dbId = "a"; |
| 176 | - changedMediaTrack.startTime = new Date(originalMediaTrack.startTime.getTime() + 101); |
|
| 177 | - changedMediaTrack.durationInMillis = 100; |
|
| 178 | + changedMediaTrack.startTime = originalMediaTrack.startTime.plus(101); |
|
| 179 | + changedMediaTrack.duration = new MillisecondsDurationImpl(100); |
|
| 178 | 180 | mediaLibary.startTimeChanged(changedMediaTrack); |
| 179 | 181 | |
| 180 | 182 | Set<MediaTrack> secondQueryResult = mediaLibary.findMediaTracksInTimeRange(queryStartTime, queryEndTime); |
| ... | ... | @@ -190,11 +192,11 @@ public class MediaLibaryTest { |
| 190 | 192 | public void testCacheRemoveMediaTrack() { |
| 191 | 193 | MediaTrack mediaTrack = new MediaTrack(); |
| 192 | 194 | mediaTrack.dbId = "a"; |
| 193 | - mediaTrack.startTime = new Date(); |
|
| 194 | - mediaTrack.durationInMillis = 100; |
|
| 195 | + mediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 196 | + mediaTrack.duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 195 | 197 | |
| 196 | - Date originalStartTime = mediaTrack.startTime; |
|
| 197 | - Date originalEndTime = mediaTrack.deriveEndTime(); |
|
| 198 | + TimePoint originalStartTime = mediaTrack.startTime; |
|
| 199 | + TimePoint originalEndTime = mediaTrack.deriveEndTime(); |
|
| 198 | 200 | |
| 199 | 201 | mediaLibary.addMediaTrack(mediaTrack); |
| 200 | 202 | |
| ... | ... | @@ -212,12 +214,12 @@ public class MediaLibaryTest { |
| 212 | 214 | public void testCacheAddSecondMediaTrackWithSameInterval() { |
| 213 | 215 | MediaTrack firstMediaTrack = new MediaTrack(); |
| 214 | 216 | firstMediaTrack.dbId = "a"; |
| 215 | - firstMediaTrack.startTime = new Date(); |
|
| 216 | - firstMediaTrack.durationInMillis = 100; |
|
| 217 | + firstMediaTrack.startTime = MillisecondsTimePoint.now(); |
|
| 218 | + firstMediaTrack.duration = MillisecondsDurationImpl.ONE_HOUR; |
|
| 217 | 219 | mediaLibary.addMediaTrack(firstMediaTrack); |
| 218 | 220 | |
| 219 | - Date queryStartTime = new Date(firstMediaTrack.startTime.getTime() + 1); |
|
| 220 | - Date queryEndTime = new Date(firstMediaTrack.deriveEndTime().getTime() - 1); |
|
| 221 | + TimePoint queryStartTime = firstMediaTrack.startTime.plus(1); |
|
| 222 | + TimePoint queryEndTime = firstMediaTrack.deriveEndTime().minus(1); |
|
| 221 | 223 | |
| 222 | 224 | Set<MediaTrack> firstQueryResult = mediaLibary.findMediaTracksInTimeRange(queryStartTime, queryEndTime); |
| 223 | 225 | assertThat(firstQueryResult.size(), is(1)); |
| ... | ... | @@ -225,26 +227,26 @@ public class MediaLibaryTest { |
| 225 | 227 | MediaTrack secondMediaTrack = new MediaTrack(); |
| 226 | 228 | secondMediaTrack.dbId = "b"; |
| 227 | 229 | secondMediaTrack.startTime = firstMediaTrack.startTime; |
| 228 | - secondMediaTrack.durationInMillis = firstMediaTrack.durationInMillis; |
|
| 230 | + secondMediaTrack.duration = firstMediaTrack.duration; |
|
| 229 | 231 | mediaLibary.addMediaTrack(secondMediaTrack); |
| 230 | 232 | |
| 231 | 233 | Set<MediaTrack> secondQueryResult = mediaLibary.findMediaTracksInTimeRange(queryStartTime, queryEndTime); |
| 232 | 234 | assertThat(secondQueryResult.size(), is(2)); |
| 233 | 235 | |
| 234 | - Date uncachedStartTime = new Date(firstMediaTrack.startTime.getTime() + 2); |
|
| 235 | - Date uncachedEndTime = new Date(firstMediaTrack.deriveEndTime().getTime() - 2); |
|
| 236 | + TimePoint uncachedStartTime = firstMediaTrack.startTime.plus(2); |
|
| 237 | + TimePoint uncachedEndTime = firstMediaTrack.deriveEndTime().minus(2); |
|
| 236 | 238 | |
| 237 | 239 | Set<MediaTrack> thirdQueryResult = mediaLibary.findMediaTracksInTimeRange(uncachedStartTime, uncachedEndTime); |
| 238 | 240 | assertThat(thirdQueryResult.size(), is(2)); |
| 239 | 241 | } |
| 240 | 242 | |
| 241 | - private void assertOverlap(Date startTime, int durationInMillis, Date rangeStart, Date rangeEnd) { |
|
| 243 | + private void assertOverlap(TimePoint startTime, Duration duration, TimePoint rangeStart, TimePoint rangeEnd) { |
|
| 242 | 244 | //insert test object |
| 243 | 245 | String dbId = "1234"; |
| 244 | 246 | String title = "title"; |
| 245 | 247 | String url = "url"; |
| 246 | 248 | MimeType mimeType = MimeType.mp4; |
| 247 | - MediaTrack mediaTrack = new MediaTrack(dbId, title, url, startTime, durationInMillis, mimeType); |
|
| 249 | + MediaTrack mediaTrack = new MediaTrack(dbId, title, url, startTime, duration, mimeType); |
|
| 248 | 250 | mediaLibary.addMediaTrack(mediaTrack); |
| 249 | 251 | |
| 250 | 252 | Collection<MediaTrack> mediaTracks = mediaLibary.findMediaTracksInTimeRange(rangeStart, rangeEnd); |
| ... | ... | @@ -252,13 +254,13 @@ public class MediaLibaryTest { |
| 252 | 254 | assertThat(mediaTracks.iterator().next().dbId, is(dbId)); |
| 253 | 255 | } |
| 254 | 256 | |
| 255 | - private void assertNoOverlap(Date startTime, int durationInMillis, Date rangeStart, Date rangeEnd) { |
|
| 257 | + private void assertNoOverlap(TimePoint startTime, Duration duration, TimePoint rangeStart, TimePoint rangeEnd) { |
|
| 256 | 258 | //insert test object |
| 257 | 259 | String dbId = "1234"; |
| 258 | 260 | String title = "title"; |
| 259 | 261 | String url = "url"; |
| 260 | 262 | MimeType mimeType = MimeType.mp4; |
| 261 | - MediaTrack mediaTrack = new MediaTrack(dbId, title, url, startTime, durationInMillis, mimeType); |
|
| 263 | + MediaTrack mediaTrack = new MediaTrack(dbId, title, url, startTime, duration, mimeType); |
|
| 262 | 264 | mediaLibary.addMediaTrack(mediaTrack); |
| 263 | 265 | |
| 264 | 266 | Collection<MediaTrack> mediaTracks = mediaLibary.findMediaTracksInTimeRange(rangeStart, rangeEnd); |
| ... | ... | @@ -267,40 +269,40 @@ public class MediaLibaryTest { |
| 267 | 269 | |
| 268 | 270 | @Test |
| 269 | 271 | public void testIntervalEqualsIdentical() throws Exception { |
| 270 | - Date date1 = new Date(); |
|
| 271 | - Date date2 = new Date(date1.getTime() + 1); |
|
| 272 | - Interval interval = new Interval(date1, date2); |
|
| 272 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 273 | + TimePoint date2 = date1.plus(1); |
|
| 274 | + TimeRange interval = new TimeRangeImpl(date1, date2); |
|
| 273 | 275 | assertTrue(interval.equals(interval)); |
| 274 | 276 | } |
| 275 | 277 | |
| 276 | 278 | @Test |
| 277 | 279 | public void testIntervalEqualsSame() throws Exception { |
| 278 | - Date date1_1 = new Date(); |
|
| 279 | - Date date1_2 = new Date(date1_1.getTime() + 1); |
|
| 280 | - Date date2_1 = new Date(date1_1.getTime()); |
|
| 281 | - Date date2_2 = new Date(date1_2.getTime()); |
|
| 282 | - Interval interval1 = new Interval(date1_1, date1_2); |
|
| 283 | - Interval interval2 = new Interval(date2_1, date2_2); |
|
| 280 | + TimePoint date1_1 = MillisecondsTimePoint.now(); |
|
| 281 | + TimePoint date1_2 = date1_1.plus(1); |
|
| 282 | + TimePoint date2_1 = date1_1; |
|
| 283 | + TimePoint date2_2 = date1_2; |
|
| 284 | + TimeRange interval1 = new TimeRangeImpl(date1_1, date1_2); |
|
| 285 | + TimeRange interval2 = new TimeRangeImpl(date2_1, date2_2); |
|
| 284 | 286 | assertTrue(interval1.equals(interval2)); |
| 285 | 287 | assertEquals(interval1.hashCode(), interval2.hashCode()); |
| 286 | 288 | } |
| 287 | 289 | |
| 288 | 290 | @Test |
| 289 | 291 | public void testIntervalEqualsNull() throws Exception { |
| 290 | - Date date1 = new Date(); |
|
| 291 | - Date date2 = new Date(date1.getTime() + 1); |
|
| 292 | - Interval interval = new Interval(date1, date2); |
|
| 292 | + TimePoint date1 = MillisecondsTimePoint.now(); |
|
| 293 | + TimePoint date2 = date1.plus(1); |
|
| 294 | + TimeRange interval = new TimeRangeImpl(date1, date2); |
|
| 293 | 295 | assertFalse(interval.equals(null)); |
| 294 | 296 | } |
| 295 | 297 | |
| 296 | 298 | @Test |
| 297 | 299 | public void testIntervalNotEquals() throws Exception { |
| 298 | - Date date1_1 = new Date(); |
|
| 299 | - Date date1_2 = new Date(date1_1.getTime() + 1); |
|
| 300 | - Date date2_1 = new Date(date1_1.getTime() + 2); |
|
| 301 | - Date date2_2 = new Date(date1_1.getTime() + 3); |
|
| 302 | - Interval interval1 = new Interval(date1_1, date1_2); |
|
| 303 | - Interval interval2 = new Interval(date2_1, date2_2); |
|
| 300 | + TimePoint date1_1 = MillisecondsTimePoint.now(); |
|
| 301 | + TimePoint date1_2 = date1_1.plus(1); |
|
| 302 | + TimePoint date2_1 = date1_1.plus(2); |
|
| 303 | + TimePoint date2_2 = date1_1.plus(3); |
|
| 304 | + TimeRange interval1 = new TimeRangeImpl(date1_1, date1_2); |
|
| 305 | + TimeRange interval2 = new TimeRangeImpl(date2_1, date2_2); |
|
| 304 | 306 | assertFalse(interval1.equals(interval2)); |
| 305 | 307 | } |
| 306 | 308 |
java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java
| ... | ... | @@ -3,6 +3,7 @@ package com.sap.sailing.server.test; |
| 3 | 3 | import static org.junit.Assert.assertEquals; |
| 4 | 4 | import static org.junit.Assert.assertNotNull; |
| 5 | 5 | import static org.junit.Assert.assertSame; |
| 6 | + |
|
| 6 | 7 | import static org.mockito.Mockito.doReturn; |
| 7 | 8 | import static org.mockito.Mockito.spy; |
| 8 | 9 | |
| ... | ... | @@ -71,6 +72,7 @@ import com.sap.sailing.domain.common.impl.DataImportProgressImpl; |
| 71 | 72 | import com.sap.sailing.domain.common.impl.DegreeBearingImpl; |
| 72 | 73 | import com.sap.sailing.domain.common.impl.DegreePosition; |
| 73 | 74 | import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; |
| 75 | +import com.sap.sailing.domain.common.impl.MillisecondsDurationImpl; |
|
| 74 | 76 | import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; |
| 75 | 77 | import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; |
| 76 | 78 | import com.sap.sailing.domain.common.media.MediaTrack; |
| ... | ... | @@ -162,7 +164,7 @@ public class MasterDataImportTest { |
| 162 | 164 | ClassNotFoundException { |
| 163 | 165 | // Setup source service |
| 164 | 166 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 165 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 167 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 166 | 168 | UUID courseAreaUUID = UUID.randomUUID(); |
| 167 | 169 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 168 | 170 | event.getVenue().addCourseArea(courseArea); |
| ... | ... | @@ -378,7 +380,7 @@ public class MasterDataImportTest { |
| 378 | 380 | InterruptedException, ClassNotFoundException { |
| 379 | 381 | // Setup source service |
| 380 | 382 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 381 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 383 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 382 | 384 | UUID courseAreaUUID = UUID.randomUUID(); |
| 383 | 385 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 384 | 386 | event.getVenue().addCourseArea(courseArea); |
| ... | ... | @@ -534,7 +536,7 @@ public class MasterDataImportTest { |
| 534 | 536 | ClassNotFoundException { |
| 535 | 537 | // Setup source service |
| 536 | 538 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 537 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 539 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 538 | 540 | UUID courseAreaUUID = UUID.randomUUID(); |
| 539 | 541 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 540 | 542 | event.getVenue().addCourseArea(courseArea); |
| ... | ... | @@ -676,7 +678,7 @@ public class MasterDataImportTest { |
| 676 | 678 | PersistenceFactory.INSTANCE.getDomainObjectFactory(MongoDBService.INSTANCE, sourceDomainFactory), |
| 677 | 679 | PersistenceFactory.INSTANCE.getMongoObjectFactory(MongoDBService.INSTANCE), |
| 678 | 680 | MediaDBFactory.INSTANCE.getDefaultMediaDB(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE); |
| 679 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 681 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 680 | 682 | UUID courseAreaUUID = UUID.randomUUID(); |
| 681 | 683 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 682 | 684 | event.getVenue().addCourseArea(courseArea); |
| ... | ... | @@ -831,7 +833,7 @@ public class MasterDataImportTest { |
| 831 | 833 | InterruptedException, ClassNotFoundException { |
| 832 | 834 | // Setup source service |
| 833 | 835 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 834 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 836 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 835 | 837 | UUID courseAreaUUID = UUID.randomUUID(); |
| 836 | 838 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 837 | 839 | event.getVenue().addCourseArea(courseArea); |
| ... | ... | @@ -945,8 +947,8 @@ public class MasterDataImportTest { |
| 945 | 947 | domainFactory = destService.getBaseDomainFactory(); |
| 946 | 948 | // Create existing data on target |
| 947 | 949 | venueNameNotToOverride = "doNotOverride"; |
| 948 | - Event eventNotToOverride = destService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, venueNameNotToOverride, false, |
|
| 949 | - eventUUID); |
|
| 950 | + Event eventNotToOverride = destService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, venueNameNotToOverride, |
|
| 951 | + false, eventUUID); |
|
| 950 | 952 | courseAreaNotToOverride = new CourseAreaImpl("testAreaNotToOverride", courseAreaUUID); |
| 951 | 953 | eventNotToOverride.getVenue().addCourseArea(courseAreaNotToOverride); |
| 952 | 954 | |
| ... | ... | @@ -1029,7 +1031,7 @@ public class MasterDataImportTest { |
| 1029 | 1031 | InterruptedException, ClassNotFoundException { |
| 1030 | 1032 | // Setup source service |
| 1031 | 1033 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 1032 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 1034 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 1033 | 1035 | UUID courseAreaUUID = UUID.randomUUID(); |
| 1034 | 1036 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 1035 | 1037 | event.getVenue().addCourseArea(courseArea); |
| ... | ... | @@ -1139,7 +1141,7 @@ public class MasterDataImportTest { |
| 1139 | 1141 | domainFactory = destService.getBaseDomainFactory(); |
| 1140 | 1142 | // Create existing data on target |
| 1141 | 1143 | String venueNameToOverride = "Override"; |
| 1142 | - Event eventToOverride = destService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, venueNameToOverride, false, eventUUID); |
|
| 1144 | + Event eventToOverride = destService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, venueNameToOverride, false, eventUUID); |
|
| 1143 | 1145 | CourseArea courseAreaToOverride = new CourseAreaImpl("testAreaToOverride", courseAreaUUID); |
| 1144 | 1146 | eventToOverride.getVenue().addCourseArea(courseAreaToOverride); |
| 1145 | 1147 | |
| ... | ... | @@ -1256,12 +1258,15 @@ public class MasterDataImportTest { |
| 1256 | 1258 | |
| 1257 | 1259 | Iterable<URL> imageURLs = new HashSet<>(); |
| 1258 | 1260 | Iterable<URL> videoURLs = new HashSet<>(); |
| 1259 | - Event event = sourceService.createEventWithoutReplication("Test Event", new MillisecondsTimePoint(0), |
|
| 1260 | - new MillisecondsTimePoint(10), "testvenue", false, UUID.randomUUID(), imageURLs, videoURLs); |
|
| 1261 | + Iterable<URL> sponsorImageURLs = new HashSet<>(); |
|
| 1262 | + Event event = sourceService.createEventWithoutReplication("Test Event", /* eventDescription */ null, |
|
| 1263 | + new MillisecondsTimePoint(0), new MillisecondsTimePoint(10), "testvenue", false, UUID.randomUUID(), imageURLs, |
|
| 1264 | + videoURLs, sponsorImageURLs, /* logoImageURL */ null, /* officialWebsiteURL */ null); |
|
| 1261 | 1265 | CourseArea defaultCourseArea = sourceService.addCourseArea(event.getId(), "ECHO", UUID.randomUUID()); |
| 1262 | 1266 | |
| 1263 | - Regatta regatta = sourceService.createRegatta(RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, UUID.randomUUID(), |
|
| 1264 | - new ArrayList<Series>(), true, new LowPoint(), defaultCourseArea.getId(), /* useStartTimeInference */ true); |
|
| 1267 | + Regatta regatta = sourceService.createRegatta( |
|
| 1268 | + RegattaImpl.getDefaultName(TEST_REGATTA_NAME, TEST_BOAT_CLASS_NAME), TEST_BOAT_CLASS_NAME, |
|
| 1269 | + UUID.randomUUID(), new ArrayList<Series>(), true, new LowPoint(), defaultCourseArea.getId(), /* useStartTimeInference */ true); |
|
| 1265 | 1270 | // Let's use the setters directly because we are not testing replication |
| 1266 | 1271 | RegattaConfigurationImpl configuration = new RegattaConfigurationImpl(); |
| 1267 | 1272 | configuration.setDefaultCourseDesignerMode(CourseDesignerMode.BY_MAP); |
| ... | ... | @@ -1320,8 +1325,10 @@ public class MasterDataImportTest { |
| 1320 | 1325 | |
| 1321 | 1326 | Iterable<URL> imageURLs = new HashSet<>(); |
| 1322 | 1327 | Iterable<URL> videoURLs = new HashSet<>(); |
| 1323 | - Event event = sourceService.createEventWithoutReplication("Test Event", new MillisecondsTimePoint(0), |
|
| 1324 | - new MillisecondsTimePoint(10), "testvenue", false, UUID.randomUUID(), imageURLs, videoURLs); |
|
| 1328 | + Iterable<URL> sponsorImageURLs = new HashSet<>(); |
|
| 1329 | + Event event = sourceService.createEventWithoutReplication("Test Event", /* eventDescription */ null, |
|
| 1330 | + new MillisecondsTimePoint(0), new MillisecondsTimePoint(10), "testvenue", false, UUID.randomUUID(), imageURLs, |
|
| 1331 | + videoURLs, sponsorImageURLs, /* logoImageURL */ null, /* officialWebsiteURL */ null); |
|
| 1325 | 1332 | CourseArea defaultCourseArea = sourceService.addCourseArea(event.getId(), "ECHO", UUID.randomUUID()); |
| 1326 | 1333 | |
| 1327 | 1334 | List<String> raceColumnNames = new ArrayList<String>(); |
| ... | ... | @@ -1450,7 +1457,7 @@ public class MasterDataImportTest { |
| 1450 | 1457 | IOException, InterruptedException, ClassNotFoundException { |
| 1451 | 1458 | // Setup source service |
| 1452 | 1459 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 1453 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 1460 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 1454 | 1461 | UUID courseAreaUUID = UUID.randomUUID(); |
| 1455 | 1462 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 1456 | 1463 | event.getVenue().addCourseArea(courseArea); |
| ... | ... | @@ -1599,7 +1606,7 @@ public class MasterDataImportTest { |
| 1599 | 1606 | ClassNotFoundException { |
| 1600 | 1607 | // Setup source service |
| 1601 | 1608 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 1602 | - MediaTrack trackOnSource = new MediaTrack("testTitle", "http://test/test.mp4", new Date(0), 2000, |
|
| 1609 | + MediaTrack trackOnSource = new MediaTrack("testTitle", "http://test/test.mp4", new MillisecondsTimePoint(0), MillisecondsDurationImpl.ONE_HOUR, |
|
| 1603 | 1610 | MediaTrack.MimeType.mp4); |
| 1604 | 1611 | sourceService.mediaTrackAdded(trackOnSource); |
| 1605 | 1612 | |
| ... | ... | @@ -1656,7 +1663,7 @@ public class MasterDataImportTest { |
| 1656 | 1663 | InterruptedException, ClassNotFoundException { |
| 1657 | 1664 | // Setup source service |
| 1658 | 1665 | RacingEventService sourceService = new RacingEventServiceImpl(); |
| 1659 | - Event event = sourceService.addEvent(TEST_EVENT_NAME, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 1666 | + Event event = sourceService.addEvent(TEST_EVENT_NAME, /* eventDescription */ null, eventStartDate, eventEndDate, "testVenue", false, eventUUID); |
|
| 1660 | 1667 | UUID courseAreaUUID = UUID.randomUUID(); |
| 1661 | 1668 | CourseArea courseArea = new CourseAreaImpl("testArea", courseAreaUUID); |
| 1662 | 1669 | event.getVenue().addCourseArea(courseArea); |
java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/SearchServiceTest.java
| ... | ... | @@ -117,8 +117,10 @@ public class SearchServiceTest { |
| 117 | 117 | final TimePoint pfingstbuschStartDate = new MillisecondsTimePoint(cal.getTime()); |
| 118 | 118 | cal.set(2014, 5, 8, 16, 00); |
| 119 | 119 | final TimePoint pfingstbuschEndDate = new MillisecondsTimePoint(cal.getTime()); |
| 120 | - pfingstbusch = server.apply(new CreateEvent("Pfingsbusch", pfingstbuschStartDate, pfingstbuschEndDate, "Kiel", /* isPublic */ |
|
| 121 | - true, UUID.randomUUID(), Collections.<URL>emptySet(), Collections.<URL>emptySet())); |
|
| 120 | + pfingstbusch = server.apply(new CreateEvent("Pfingsbusch", /* eventDescription */ null, pfingstbuschStartDate, pfingstbuschEndDate, /* isPublic */ |
|
| 121 | + "Kiel", true, UUID.randomUUID(), Collections.<URL>emptySet(), |
|
| 122 | + Collections.<URL>emptySet(), |
|
| 123 | + /* sponsorImageURLs */ Collections.<URL>emptySet(), /* logoImageURLAsString */ null, /* officialWebsiteURLAsString */ null)); |
|
| 122 | 124 | kiel = pfingstbusch.getVenue(); |
| 123 | 125 | final CourseAreaImpl kielAlpha = new CourseAreaImpl("Alpha", UUID.randomUUID()); |
| 124 | 126 | kiel.addCourseArea(kielAlpha); |
| ... | ... | @@ -148,8 +150,10 @@ public class SearchServiceTest { |
| 148 | 150 | final TimePoint aalStartDate = new MillisecondsTimePoint(cal.getTime()); |
| 149 | 151 | cal.set(2014, 5, 8, 18, 00); |
| 150 | 152 | final TimePoint aalEndDate = new MillisecondsTimePoint(cal.getTime()); |
| 151 | - aalEvent = server.apply(new CreateEvent("Aalregatta", aalStartDate, aalEndDate, "Flensburg", /* isPublic */ |
|
| 152 | - true, UUID.randomUUID(), Collections.<URL>emptySet(), Collections.<URL>emptySet())); |
|
| 153 | + aalEvent = server.apply(new CreateEvent("Aalregatta", /* eventDescription */ null, aalStartDate, aalEndDate, /* isPublic */ |
|
| 154 | + "Flensburg", true, UUID.randomUUID(), Collections.<URL>emptySet(), |
|
| 155 | + Collections.<URL>emptySet(), /* sponsorImageURLs */ Collections.<URL>emptySet(), |
|
| 156 | + /* logoimageURL */ null, /* officialWebsiteURLAsString */ null)); |
|
| 153 | 157 | flensburg = aalEvent.getVenue(); |
| 154 | 158 | final CourseAreaImpl flensburgStandard = new CourseAreaImpl("Standard", UUID.randomUUID()); |
| 155 | 159 | flensburg.addCourseArea(flensburgStandard); |
java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java
| ... | ... | @@ -340,6 +340,7 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch |
| 340 | 340 | *
|
| 341 | 341 | * @param eventName
|
| 342 | 342 | * The name of the new event
|
| 343 | + * @param eventDescription TODO
|
|
| 343 | 344 | * @param startDate
|
| 344 | 345 | * The start date of the event
|
| 345 | 346 | * @param endDate
|
| ... | ... | @@ -352,7 +353,7 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch |
| 352 | 353 | * The name of the venue of the new event
|
| 353 | 354 | * @return The new event
|
| 354 | 355 | */
|
| 355 | - Event addEvent(String eventName, TimePoint startDate, TimePoint endDate, String venueName, boolean isPublic, UUID id);
|
|
| 356 | + Event addEvent(String eventName, String eventDescription, TimePoint startDate, TimePoint endDate, String venueName, boolean isPublic, UUID id);
|
|
| 356 | 357 | |
| 357 | 358 | /**
|
| 358 | 359 | * Updates a sailing event with the name <code>eventName</code>, the venue<code>venue</code> and the regattas with
|
| ... | ... | @@ -459,8 +460,8 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch |
| 459 | 460 | */
|
| 460 | 461 | ConcurrentHashMap<String, Regatta> getPersistentRegattasForRaceIDs();
|
| 461 | 462 | |
| 462 | - Event createEventWithoutReplication(String eventName, TimePoint startDate, TimePoint endDate, String venue, boolean isPublic,
|
|
| 463 | - UUID id, Iterable<URL> imageURLs, Iterable<URL> videoURLs);
|
|
| 463 | + Event createEventWithoutReplication(String eventName, String eventDescription, TimePoint startDate, TimePoint endDate, String venue,
|
|
| 464 | + boolean isPublic, UUID id, Iterable<URL> imageURLs, Iterable<URL> videoURLs, Iterable<URL> sponsorImageURLs, URL logoImageURL, URL officialWebsiteURL);
|
|
| 464 | 465 | |
| 465 | 466 | void setRegattaForRace(Regatta regatta, String raceIdAsString);
|
| 466 | 467 |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/MediaLibrary.java
| ... | ... | @@ -4,10 +4,8 @@ import java.io.IOException; |
| 4 | 4 | import java.io.ObjectInputStream; |
| 5 | 5 | import java.io.ObjectOutputStream; |
| 6 | 6 | import java.util.ArrayList; |
| 7 | -import java.util.Arrays; |
|
| 8 | 7 | import java.util.Collection; |
| 9 | 8 | import java.util.Collections; |
| 10 | -import java.util.Date; |
|
| 11 | 9 | import java.util.HashMap; |
| 12 | 10 | import java.util.HashSet; |
| 13 | 11 | import java.util.Map; |
| ... | ... | @@ -16,43 +14,15 @@ import java.util.Set; |
| 16 | 14 | import java.util.concurrent.ConcurrentHashMap; |
| 17 | 15 | import java.util.concurrent.ConcurrentMap; |
| 18 | 16 | |
| 17 | +import com.sap.sailing.domain.common.TimePoint; |
|
| 18 | +import com.sap.sailing.domain.common.TimeRange; |
|
| 19 | +import com.sap.sailing.domain.common.impl.TimeRangeImpl; |
|
| 19 | 20 | import com.sap.sailing.domain.common.media.MediaTrack; |
| 20 | -import com.sap.sailing.domain.common.media.MediaUtil; |
|
| 21 | 21 | import com.sap.sailing.util.impl.LockUtil; |
| 22 | 22 | import com.sap.sailing.util.impl.NamedReentrantReadWriteLock; |
| 23 | 23 | |
| 24 | 24 | class MediaLibrary { |
| 25 | 25 | |
| 26 | - static class Interval { |
|
| 27 | - |
|
| 28 | - public final Date begin; |
|
| 29 | - public final Date end; |
|
| 30 | - |
|
| 31 | - public Interval(Date begin, Date end) { |
|
| 32 | - this.begin = begin; |
|
| 33 | - this.end = end; |
|
| 34 | - } |
|
| 35 | - |
|
| 36 | - @Override |
|
| 37 | - public boolean equals(Object obj) { |
|
| 38 | - if (this == obj) { |
|
| 39 | - return true; |
|
| 40 | - } else if (obj instanceof Interval) { |
|
| 41 | - Interval interval = (Interval) obj; |
|
| 42 | - return MediaUtil.equalsDatesAllowingNull(this.begin, interval.begin) |
|
| 43 | - && MediaUtil.equalsDatesAllowingNull(this.end, interval.end); |
|
| 44 | - } else { |
|
| 45 | - return false; |
|
| 46 | - } |
|
| 47 | - } |
|
| 48 | - |
|
| 49 | - @Override |
|
| 50 | - public int hashCode() { |
|
| 51 | - return Arrays.hashCode(new Date[] { begin, end }); |
|
| 52 | - } |
|
| 53 | - |
|
| 54 | - } |
|
| 55 | - |
|
| 56 | 26 | /** |
| 57 | 27 | * The set of MediaTracks kept by this library. Due to complex write operations which require explicit write-locking |
| 58 | 28 | * anyway there's no need to realize this collection with built-in concurrency support. |
| ... | ... | @@ -65,7 +35,7 @@ class MediaLibrary { |
| 65 | 35 | * result in case a MediaTrack is being removed from the library or changes values such that it needs to be removed |
| 66 | 36 | * from the cache. |
| 67 | 37 | */ |
| 68 | - private final ConcurrentMap<Interval, Set<MediaTrack>> cacheByInterval = new ConcurrentHashMap<Interval, Set<MediaTrack>>(); |
|
| 38 | + private final ConcurrentMap<TimeRange, Set<MediaTrack>> cacheByInterval = new ConcurrentHashMap<TimeRange, Set<MediaTrack>>(); |
|
| 69 | 39 | |
| 70 | 40 | private final NamedReentrantReadWriteLock lock = new NamedReentrantReadWriteLock(MediaLibrary.class.getName(), /* fair */ false); |
| 71 | 41 | |
| ... | ... | @@ -104,11 +74,11 @@ class MediaLibrary { |
| 104 | 74 | * @param endTime |
| 105 | 75 | * @return |
| 106 | 76 | */ |
| 107 | - Set<MediaTrack> findMediaTracksInTimeRange(Date startTime, Date endTime) { |
|
| 77 | + Set<MediaTrack> findMediaTracksInTimeRange(TimePoint startTime, TimePoint endTime) { |
|
| 108 | 78 | |
| 109 | 79 | if (startTime != null) { |
| 110 | 80 | |
| 111 | - Interval interval = new Interval(startTime, endTime); |
|
| 81 | + TimeRange interval = new TimeRangeImpl(startTime, endTime); |
|
| 112 | 82 | LockUtil.lockForRead(lock); |
| 113 | 83 | try { |
| 114 | 84 | Set<MediaTrack> cachedMediaTracks = cacheByInterval.get(interval); |
| ... | ... | @@ -138,6 +108,16 @@ class MediaLibrary { |
| 138 | 108 | return Collections.emptySet(); |
| 139 | 109 | } |
| 140 | 110 | |
| 111 | + public Collection<MediaTrack> findLiveMediaTracksForRace(String regattaName, String raceName) { |
|
| 112 | + Set<MediaTrack> result = new HashSet<MediaTrack>(); |
|
| 113 | + for (MediaTrack mediaTrack : mediaTracksByDbId.values()) { |
|
| 114 | + if (mediaTrack.duration == null) { |
|
| 115 | + result.add(mediaTrack); |
|
| 116 | + } |
|
| 117 | + } |
|
| 118 | + return result; |
|
| 119 | + } |
|
| 120 | + |
|
| 141 | 121 | /** |
| 142 | 122 | * Adding media tracks is expected to happen so rarely that we don't optimize for it and simply re-use the add-many |
| 143 | 123 | * method. |
| ... | ... | @@ -224,7 +204,7 @@ class MediaLibrary { |
| 224 | 204 | try { |
| 225 | 205 | MediaTrack mediaTrack = mediaTracksByDbId.get(changedMediaTrack); |
| 226 | 206 | if (mediaTrack != null) { |
| 227 | - mediaTrack.durationInMillis = changedMediaTrack.durationInMillis; |
|
| 207 | + mediaTrack.duration = changedMediaTrack.duration; |
|
| 228 | 208 | updateCache_Change(mediaTrack); |
| 229 | 209 | } |
| 230 | 210 | } finally { |
| ... | ... | @@ -236,9 +216,9 @@ class MediaLibrary { |
| 236 | 216 | * To be called only under write lock! |
| 237 | 217 | */ |
| 238 | 218 | private void updateCache_Add(MediaTrack mediaTrack) { |
| 239 | - for (Entry<Interval, Set<MediaTrack>> cacheEntry : cacheByInterval.entrySet()) { |
|
| 240 | - Interval interval = cacheEntry.getKey(); |
|
| 241 | - if (mediaTrack.overlapsWith(interval.begin, interval.end)) { |
|
| 219 | + for (Entry<TimeRange, Set<MediaTrack>> cacheEntry : cacheByInterval.entrySet()) { |
|
| 220 | + TimeRange interval = cacheEntry.getKey(); |
|
| 221 | + if (mediaTrack.overlapsWith(interval.from(), interval.to())) { |
|
| 242 | 222 | cacheEntry.getValue().add(mediaTrack); |
| 243 | 223 | } |
| 244 | 224 | } |
| ... | ... | @@ -248,10 +228,10 @@ class MediaLibrary { |
| 248 | 228 | * To be called only under write lock! |
| 249 | 229 | */ |
| 250 | 230 | private void updateCache_Change(MediaTrack mediaTrack) { |
| 251 | - for (Entry<Interval, Set<MediaTrack>> cacheEntry : cacheByInterval.entrySet()) { |
|
| 231 | + for (Entry<TimeRange, Set<MediaTrack>> cacheEntry : cacheByInterval.entrySet()) { |
|
| 252 | 232 | cacheEntry.getValue().remove(mediaTrack); |
| 253 | - Interval interval = cacheEntry.getKey(); |
|
| 254 | - if (mediaTrack.overlapsWith(interval.begin, interval.end)) { |
|
| 233 | + TimeRange interval = cacheEntry.getKey(); |
|
| 234 | + if (mediaTrack.overlapsWith(interval.from(), interval.to())) { |
|
| 255 | 235 | cacheEntry.getValue().add(mediaTrack); |
| 256 | 236 | } |
| 257 | 237 | } |
| ... | ... | @@ -261,7 +241,7 @@ class MediaLibrary { |
| 261 | 241 | * To be called only under write lock! |
| 262 | 242 | */ |
| 263 | 243 | private void updateCache_Remove(MediaTrack mediaTrack) { |
| 264 | - for (Entry<Interval, Set<MediaTrack>> cacheEntry : cacheByInterval.entrySet()) { |
|
| 244 | + for (Entry<TimeRange, Set<MediaTrack>> cacheEntry : cacheByInterval.entrySet()) { |
|
| 265 | 245 | cacheEntry.getValue().remove(mediaTrack); |
| 266 | 246 | } |
| 267 | 247 | } |
java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java
| ... | ... | @@ -14,7 +14,6 @@ import java.net.URLEncoder; |
| 14 | 14 | import java.util.ArrayList;
|
| 15 | 15 | import java.util.Collection;
|
| 16 | 16 | import java.util.Collections;
|
| 17 | -import java.util.Date;
|
|
| 18 | 17 | import java.util.HashMap;
|
| 19 | 18 | import java.util.HashSet;
|
| 20 | 19 | import java.util.Iterator;
|
| ... | ... | @@ -82,6 +81,7 @@ import com.sap.sailing.domain.common.dto.FleetDTO; |
| 82 | 81 | import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
|
| 83 | 82 | import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO;
|
| 84 | 83 | import com.sap.sailing.domain.common.impl.DataImportProgressImpl;
|
| 84 | +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
|
| 85 | 85 | import com.sap.sailing.domain.common.media.MediaTrack;
|
| 86 | 86 | import com.sap.sailing.domain.common.media.MediaTrack.MimeType;
|
| 87 | 87 | import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
| ... | ... | @@ -560,7 +560,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 560 | 560 | for (DBMediaTrack dbMediaTrack : allDBMediaTracks) {
|
| 561 | 561 | MimeType mimeType = dbMediaTrack.mimeType != null ? MimeType.byName(dbMediaTrack.mimeType) : null;
|
| 562 | 562 | MediaTrack mediaTrack = new MediaTrack(dbMediaTrack.dbId, dbMediaTrack.title, dbMediaTrack.url,
|
| 563 | - dbMediaTrack.startTime, dbMediaTrack.durationInMillis, mimeType);
|
|
| 563 | + dbMediaTrack.startTime, dbMediaTrack.duration, mimeType);
|
|
| 564 | 564 | mediaTrackAdded(mediaTrack);
|
| 565 | 565 | }
|
| 566 | 566 | }
|
| ... | ... | @@ -2294,11 +2294,14 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 2294 | 2294 | |
| 2295 | 2295 | // Used for TESTING only
|
| 2296 | 2296 | @Override
|
| 2297 | - public Event addEvent(String eventName, TimePoint startDate, TimePoint endDate, String venue, boolean isPublic, UUID id) {
|
|
| 2298 | - Event result = createEventWithoutReplication(eventName, startDate, endDate, venue, isPublic, id,
|
|
| 2299 | - /* imageURLs */ Collections.<URL>emptyList(), /* videoURLs */ Collections.<URL>emptyList());
|
|
| 2300 | - replicate(new CreateEvent(eventName, startDate, endDate, venue, isPublic, id, /* imageURLs */
|
|
| 2301 | - Collections.<URL> emptyList(), /* videoURLs */Collections.<URL> emptyList()));
|
|
| 2297 | + public Event addEvent(String eventName, String eventDescription, TimePoint startDate, TimePoint endDate, String venue, boolean isPublic, UUID id) {
|
|
| 2298 | + Event result = createEventWithoutReplication(eventName, eventDescription, startDate, endDate, venue, isPublic,
|
|
| 2299 | + id, /* imageURLs */ Collections.<URL>emptyList(),
|
|
| 2300 | + /* videoURLs */ Collections.<URL>emptyList(), /* sponsorImageURLs */ Collections.<URL>emptyList(), /* logoImageURL */ null, /* officialWebsiteURL */ null);
|
|
| 2301 | + replicate(new CreateEvent(eventName, eventDescription, startDate, endDate, venue, isPublic, /* imageURLs */
|
|
| 2302 | + id, Collections.<URL> emptyList(),
|
|
| 2303 | + /* videoURLs */Collections.<URL> emptyList(), /* sponsorImageURLs */ Collections.<URL> emptyList(),
|
|
| 2304 | + /* logoimageURL */ null, /* officialWebsiteURLAsString */ null));
|
|
| 2302 | 2305 | return result;
|
| 2303 | 2306 | }
|
| 2304 | 2307 | |
| ... | ... | @@ -2308,12 +2311,17 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 2308 | 2311 | }
|
| 2309 | 2312 | |
| 2310 | 2313 | @Override
|
| 2311 | - public Event createEventWithoutReplication(String eventName, TimePoint startDate, TimePoint endDate, String venue,
|
|
| 2312 | - boolean isPublic, UUID id, Iterable<URL> imageURLs, Iterable<URL> videoURLs) {
|
|
| 2314 | + public Event createEventWithoutReplication(String eventName, String eventDescription, TimePoint startDate, TimePoint endDate,
|
|
| 2315 | + String venue, boolean isPublic, UUID id, Iterable<URL> imageURLs,
|
|
| 2316 | + Iterable<URL> videoURLs, Iterable<URL> sponsorImageURLs, URL logoImageURL, URL officialWebsiteURL) {
|
|
| 2313 | 2317 | Event result = new EventImpl(eventName, startDate, endDate, venue, isPublic, id);
|
| 2314 | 2318 | addEvent(result);
|
| 2319 | + result.setDescription(eventDescription);
|
|
| 2315 | 2320 | result.setImageURLs(imageURLs);
|
| 2316 | 2321 | result.setVideoURLs(videoURLs);
|
| 2322 | + result.setSponsorImageURLs(sponsorImageURLs);
|
|
| 2323 | + result.setLogoImageURL(logoImageURL);
|
|
| 2324 | + result.setOfficialWebsiteURL(officialWebsiteURL);
|
|
| 2317 | 2325 | return result;
|
| 2318 | 2326 | }
|
| 2319 | 2327 | |
| ... | ... | @@ -2427,7 +2435,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 2427 | 2435 | String mimeType = mediaTrack.mimeType != null ? mediaTrack.mimeType.name() : null;
|
| 2428 | 2436 | if (mediaTrack.dbId == null) {
|
| 2429 | 2437 | mediaTrack.dbId = mediaDB.insertMediaTrack(mediaTrack.title, mediaTrack.url, mediaTrack.startTime,
|
| 2430 | - mediaTrack.durationInMillis, mimeType);
|
|
| 2438 | + mediaTrack.duration, mimeType);
|
|
| 2431 | 2439 | }
|
| 2432 | 2440 | mediaLibrary.addMediaTrack(mediaTrack);
|
| 2433 | 2441 | replicate(new AddMediaTrackOperation(mediaTrack));
|
| ... | ... | @@ -2461,7 +2469,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 2461 | 2469 | |
| 2462 | 2470 | @Override
|
| 2463 | 2471 | public void mediaTrackDurationChanged(MediaTrack mediaTrack) {
|
| 2464 | - mediaDB.updateDuration(mediaTrack.dbId, mediaTrack.durationInMillis);
|
|
| 2472 | + mediaDB.updateDuration(mediaTrack.dbId, mediaTrack.duration);
|
|
| 2465 | 2473 | mediaLibrary.durationChanged(mediaTrack);
|
| 2466 | 2474 | replicate(new UpdateMediaTrackDurationOperation(mediaTrack));
|
| 2467 | 2475 | }
|
| ... | ... | @@ -2478,7 +2486,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 2478 | 2486 | for (MediaTrack trackToImport : mediaTracksToImport) {
|
| 2479 | 2487 | MediaTrack existingTrack = mediaLibrary.lookupMediaTrack(trackToImport);
|
| 2480 | 2488 | if (existingTrack == null) {
|
| 2481 | - mediaDB.insertMediaTrackWithId(trackToImport.dbId, trackToImport.title, trackToImport.url, trackToImport.startTime, trackToImport.durationInMillis, trackToImport.mimeType.name());
|
|
| 2489 | + mediaDB.insertMediaTrackWithId(trackToImport.dbId, trackToImport.title, trackToImport.url, trackToImport.startTime, trackToImport.duration, trackToImport.mimeType.name());
|
|
| 2482 | 2490 | mediaTrackAdded(trackToImport);
|
| 2483 | 2491 | } else if (override) {
|
| 2484 | 2492 | |
| ... | ... | @@ -2498,8 +2506,8 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 2498 | 2506 | existingTrack.startTime = trackToImport.startTime;
|
| 2499 | 2507 | mediaTrackStartTimeChanged(existingTrack);
|
| 2500 | 2508 | }
|
| 2501 | - if (existingTrack.durationInMillis != trackToImport.durationInMillis) {
|
|
| 2502 | - existingTrack.durationInMillis = trackToImport.durationInMillis;
|
|
| 2509 | + if (existingTrack.duration != trackToImport.duration) {
|
|
| 2510 | + existingTrack.duration = trackToImport.duration;
|
|
| 2503 | 2511 | mediaTrackDurationChanged(existingTrack);
|
| 2504 | 2512 | }
|
| 2505 | 2513 | }
|
| ... | ... | @@ -2510,9 +2518,13 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport |
| 2510 | 2518 | public Collection<MediaTrack> getMediaTracksForRace(RegattaAndRaceIdentifier regattaAndRaceIdentifier) {
|
| 2511 | 2519 | TrackedRace trackedRace = getExistingTrackedRace(regattaAndRaceIdentifier);
|
| 2512 | 2520 | if (trackedRace != null) {
|
| 2513 | - Date raceStart = trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asDate();
|
|
| 2514 | - Date raceEnd = trackedRace.getEndOfRace() == null ? null : trackedRace.getEndOfRace().asDate();
|
|
| 2515 | - return mediaLibrary.findMediaTracksInTimeRange(raceStart, raceEnd);
|
|
| 2521 | + if (trackedRace.isLive(MillisecondsTimePoint.now())) {
|
|
| 2522 | + return mediaLibrary.findLiveMediaTracksForRace(trackedRace.getRaceIdentifier().getRegattaName(), trackedRace.getRaceIdentifier().getRaceName());
|
|
| 2523 | + } else {
|
|
| 2524 | + TimePoint raceStart = trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace();
|
|
| 2525 | + TimePoint raceEnd = trackedRace.getEndOfRace() == null ? null : trackedRace.getEndOfRace();
|
|
| 2526 | + return mediaLibrary.findMediaTracksInTimeRange(raceStart, raceEnd);
|
|
| 2527 | + }
|
|
| 2516 | 2528 | } else {
|
| 2517 | 2529 | return Collections.emptyList();
|
| 2518 | 2530 | }
|
java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/CreateEvent.java
| ... | ... | @@ -22,19 +22,27 @@ public class CreateEvent extends AbstractEventOperation<Event> { |
| 22 | 22 | private final TimePoint endDate;
|
| 23 | 23 | private final boolean isPublic;
|
| 24 | 24 | private final String eventName;
|
| 25 | + private final String eventDescription;
|
|
| 25 | 26 | private final Iterable<URL> videoURLs;
|
| 26 | 27 | private final Iterable<URL> imageURLs;
|
| 28 | + private final Iterable<URL> sponsorImageURLs;
|
|
| 29 | + private final URL logoImageURL;
|
|
| 30 | + private final URL officialWebsiteURL;
|
|
| 27 | 31 | |
| 28 | - public CreateEvent(String eventName, TimePoint startDate, TimePoint endDate, String venue, boolean isPublic,
|
|
| 29 | - UUID id, Iterable<URL> imageURLs, Iterable<URL> videoURLs) {
|
|
| 32 | + public CreateEvent(String eventName, String eventDescription, TimePoint startDate, TimePoint endDate, String venue,
|
|
| 33 | + boolean isPublic, UUID id, Iterable<URL> imageURLs, Iterable<URL> videoURLs, Iterable<URL> sponsorImageURLs, URL logoImageURL, URL officialWebsiteURL) {
|
|
| 30 | 34 | super(id);
|
| 31 | 35 | this.eventName = eventName;
|
| 36 | + this.eventDescription = eventDescription;
|
|
| 32 | 37 | this.startDate = startDate;
|
| 33 | 38 | this.endDate = endDate;
|
| 34 | 39 | this.venue = venue;
|
| 35 | 40 | this.isPublic = isPublic;
|
| 36 | 41 | this.imageURLs = imageURLs;
|
| 37 | 42 | this.videoURLs = videoURLs;
|
| 43 | + this.sponsorImageURLs = sponsorImageURLs;
|
|
| 44 | + this.logoImageURL = logoImageURL;
|
|
| 45 | + this.officialWebsiteURL = officialWebsiteURL;
|
|
| 38 | 46 | }
|
| 39 | 47 | |
| 40 | 48 | @Override
|
| ... | ... | @@ -55,7 +63,8 @@ public class CreateEvent extends AbstractEventOperation<Event> { |
| 55 | 63 | |
| 56 | 64 | @Override
|
| 57 | 65 | public Event internalApplyTo(RacingEventService toState) {
|
| 58 | - return toState.createEventWithoutReplication(getEventName(), startDate, endDate, venue, isPublic, getId(), imageURLs, videoURLs);
|
|
| 66 | + return toState.createEventWithoutReplication(getEventName(), eventDescription, startDate, endDate, venue, isPublic,
|
|
| 67 | + getId(), imageURLs, videoURLs, sponsorImageURLs, logoImageURL, officialWebsiteURL);
|
|
| 59 | 68 | }
|
| 60 | 69 | |
| 61 | 70 | }
|
java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/PolarDiagramCSVTest.java
| ... | ... | @@ -9,6 +9,7 @@ import junit.framework.Assert; |
| 9 | 9 | |
| 10 | 10 | import org.junit.Test; |
| 11 | 11 | |
| 12 | +import com.sap.sailing.domain.common.AbstractBearing; |
|
| 12 | 13 | import com.sap.sailing.domain.common.Bearing; |
| 13 | 14 | import com.sap.sailing.domain.common.Speed; |
| 14 | 15 | import com.sap.sailing.domain.common.SpeedWithBearing; |
| ... | ... | @@ -168,7 +169,7 @@ public class PolarDiagramCSVTest { |
| 168 | 169 | |
| 169 | 170 | private static void testSpeedsAtBearingsOfPolarDiagrams(PolarDiagram expectedPD, PolarDiagram actualPD, |
| 170 | 171 | String typeName) { |
| 171 | - DegreeBearingImpl testBearing = null; |
|
| 172 | + AbstractBearing testBearing = null; |
|
| 172 | 173 | Speed actualSpeed = null; |
| 173 | 174 | Speed expectedSpeed = null; |
| 174 | 175 | SpeedWithBearing wind = null; |
java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java
| ... | ... | @@ -31,7 +31,7 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase { |
| 31 | 31 | private static Logger logger = Logger.getLogger("com.sap.sailing"); |
| 32 | 32 | private boolean debugMsgOn = false; |
| 33 | 33 | |
| 34 | - double oobFact = 0.75; // out-of-bounds factor |
|
| 34 | + double oobFact = 2.0; // out-of-bounds factor |
|
| 35 | 35 | int maxTurns = 0; |
| 36 | 36 | boolean upwindLeg = false; |
| 37 | 37 | String initPathStr = "0"; |
| ... | ... | @@ -560,7 +560,6 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase { |
| 560 | 560 | System.out.println("Horizontal Bin Size: "+hrzBinSize); |
| 561 | 561 | } |
| 562 | 562 | |
| 563 | - //double oobFact = 0.75; // out-of-bounds factor |
|
| 564 | 563 | boolean reachedEnd = false; |
| 565 | 564 | int addSteps = 0; |
| 566 | 565 | int finalSteps = 0; // maximum number of additional steps after first target-path found |
java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/dialog/DialogUtils.java
| ... | ... | @@ -32,6 +32,14 @@ public abstract class DialogUtils { |
| 32 | 32 | } |
| 33 | 33 | } |
| 34 | 34 | }); |
| 35 | + widget.addKeyUpHandler(new KeyUpHandler() { |
|
| 36 | + @Override |
|
| 37 | + public void onKeyUp(KeyUpEvent event) { |
|
| 38 | + if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) { |
|
| 39 | + button.click(); |
|
| 40 | + } |
|
| 41 | + } |
|
| 42 | + }); |
|
| 35 | 43 | } |
| 36 | 44 | } |
| 37 | 45 |
mobile/com.sap.sailing.racecommittee.app/res/layout/confirm_dialog.xml
| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | +<?xml version="1.0" encoding="utf-8"?> |
|
| 2 | + |
|
| 3 | +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
|
| 4 | + android:orientation="vertical" |
|
| 5 | + android:layout_width="wrap_content" |
|
| 6 | + android:layout_height="wrap_content"> |
|
| 7 | + |
|
| 8 | + <TextView |
|
| 9 | + android:layout_width="match_parent" |
|
| 10 | + android:layout_height="wrap_content" |
|
| 11 | + android:layout_marginTop="8dp" |
|
| 12 | + android:layout_marginLeft="4dp" |
|
| 13 | + android:layout_marginRight="4dp" |
|
| 14 | + android:layout_marginBottom="8dp" |
|
| 15 | + android:textSize="12pt" |
|
| 16 | + android:textStyle="bold" |
|
| 17 | + android:gravity="center" |
|
| 18 | + android:text="@string/confirm_general_recall" |
|
| 19 | + android:lines="1"/> |
|
| 20 | + |
|
| 21 | +</LinearLayout> |
|
| ... | ... | \ No newline at end of file |
mobile/com.sap.sailing.racecommittee.app/res/values-de/strings.xml
| ... | ... | @@ -28,6 +28,9 @@ |
| 28 | 28 | <string name="abort_running_race">Abbruch</string> |
| 29 | 29 | <string name="choose_xray_flag_up">Einzelrückruf</string> |
| 30 | 30 | <string name="choose_first_substitute_flag">Gesamtrückruf</string> |
| 31 | + <string name="confirm_general_recall">General Recall ausführen?</string> |
|
| 32 | + <string name="confirm">Bestätigen</string> |
|
| 33 | + <string name="abort">Abbruch</string> |
|
| 31 | 34 | <string name="choose_blue_flag">Zieleinlauf beginnen</string> |
| 32 | 35 | <string name="flag_ap">AP Flagge</string> |
| 33 | 36 | <string name="flag_november">November</string> |
mobile/com.sap.sailing.racecommittee.app/res/values/strings.xml
| ... | ... | @@ -30,6 +30,9 @@ |
| 30 | 30 | <string name="choose_xray_flag_up">Display Individual Recall</string>
|
| 31 | 31 | <string name="choose_xray_flag_down">Remove Individual Recall</string>
|
| 32 | 32 | <string name="choose_first_substitute_flag">General Recall</string>
|
| 33 | + <string name="confirm_general_recall">Execute General Recall?</string>
|
|
| 34 | + <string name="confirm">Confirm</string>
|
|
| 35 | + <string name="abort">Cancel</string>
|
|
| 33 | 36 | <string name="choose_blue_flag">Begin finish phase</string>
|
| 34 | 37 | <string name="flag_ap">AP Flag</string>
|
| 35 | 38 | <string name="flag_november">November</string>
|
mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/running/BaseRunningRaceFragment.java
| ... | ... | @@ -19,6 +19,7 @@ import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.AbortTypeSelection |
| 19 | 19 | import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.RaceDialogFragment; |
| 20 | 20 | import com.sap.sailing.racecommittee.app.ui.fragments.dialogs.RaceFinishingTimeDialog; |
| 21 | 21 | import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.BaseRaceInfoRaceFragment; |
| 22 | +import com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.running.ConfirmDialog.ConfirmRecallListener; |
|
| 22 | 23 | import com.sap.sailing.racecommittee.app.ui.utils.FlagPoleStateRenderer; |
| 23 | 24 | import com.sap.sailing.racecommittee.app.utils.TimeUtils; |
| 24 | 25 | |
| ... | ... | @@ -83,10 +84,20 @@ public abstract class BaseRunningRaceFragment<ProcedureType extends RacingProced |
| 83 | 84 | generalRecallButton.setOnClickListener(new OnClickListener() { |
| 84 | 85 | @Override |
| 85 | 86 | public void onClick(View v) { |
| 86 | - TimePoint now = MillisecondsTimePoint.now(); |
|
| 87 | - getRaceState().setGeneralRecall(now); |
|
| 88 | - // TODO see bug 1649: Explicit passing of pass identifier in RaceState interface |
|
| 89 | - getRaceState().setAdvancePass(now); |
|
| 87 | + ConfirmDialog confirmDialog = new ConfirmDialog(); |
|
| 88 | + confirmDialog.setTargetFragment(BaseRunningRaceFragment.this, 1); |
|
| 89 | + confirmDialog.setCallback(new ConfirmRecallListener() { |
|
| 90 | + @Override |
|
| 91 | + public void returnAddedElementToPicker(boolean recall) { |
|
| 92 | + TimePoint now = MillisecondsTimePoint.now(); |
|
| 93 | + getRaceState().setGeneralRecall(now); |
|
| 94 | + // TODO see bug 1649: Explicit passing of pass identifier in RaceState interface |
|
| 95 | + getRaceState().setAdvancePass(now); |
|
| 96 | + } |
|
| 97 | + }); |
|
| 98 | + final String tag = "confirm_general_recall_fragment"; |
|
| 99 | + confirmDialog.show(getFragmentManager(), tag); |
|
| 100 | + |
|
| 90 | 101 | } |
| 91 | 102 | }); |
| 92 | 103 |
mobile/com.sap.sailing.racecommittee.app/src/com/sap/sailing/racecommittee/app/ui/fragments/raceinfo/running/ConfirmDialog.java
| ... | ... | @@ -0,0 +1,48 @@ |
| 1 | +package com.sap.sailing.racecommittee.app.ui.fragments.raceinfo.running; |
|
| 2 | + |
|
| 3 | +import android.app.AlertDialog; |
|
| 4 | +import android.app.Dialog; |
|
| 5 | +import android.app.DialogFragment; |
|
| 6 | +import android.content.DialogInterface; |
|
| 7 | +import android.os.Bundle; |
|
| 8 | +import android.view.LayoutInflater; |
|
| 9 | +import android.view.View; |
|
| 10 | + |
|
| 11 | +import com.sap.sailing.racecommittee.app.R; |
|
| 12 | + |
|
| 13 | +public class ConfirmDialog extends DialogFragment { |
|
| 14 | + private ConfirmRecallListener confirmRecallListener; |
|
| 15 | + |
|
| 16 | + public interface ConfirmRecallListener { |
|
| 17 | + public void returnAddedElementToPicker(boolean recall); |
|
| 18 | + } |
|
| 19 | + |
|
| 20 | + public void setCallback(ConfirmRecallListener callback) { |
|
| 21 | + this.confirmRecallListener = callback; |
|
| 22 | + } |
|
| 23 | + |
|
| 24 | + @Override |
|
| 25 | + public Dialog onCreateDialog(Bundle savedInstanceState) { |
|
| 26 | + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); |
|
| 27 | + LayoutInflater inflater = getActivity().getLayoutInflater(); |
|
| 28 | + final View view = inflater.inflate(R.layout.confirm_dialog, null); |
|
| 29 | + builder.setView(view).setPositiveButton(R.string.confirm, new DialogInterface.OnClickListener() { |
|
| 30 | + @Override |
|
| 31 | + public void onClick(DialogInterface dialog, int id) { |
|
| 32 | + returnData(); |
|
| 33 | + } |
|
| 34 | + }).setNegativeButton(R.string.abort, new DialogInterface.OnClickListener() { |
|
| 35 | + public void onClick(DialogInterface dialog, int id) { |
|
| 36 | + ConfirmDialog.this.getDialog().cancel(); |
|
| 37 | + } |
|
| 38 | + }); |
|
| 39 | + |
|
| 40 | + return builder.create(); |
|
| 41 | + } |
|
| 42 | + |
|
| 43 | + private void returnData() { |
|
| 44 | + if (confirmRecallListener != null) { |
|
| 45 | + confirmRecallListener.returnAddedElementToPicker(true); |
|
| 46 | + } |
|
| 47 | + } |
|
| 48 | +} |
|
| ... | ... | \ No newline at end of file |
wiki/amazon-ec2.md
| ... | ... | @@ -266,14 +266,15 @@ A closer look reveals that an ELB instance consists itself of many other invisib |
| 266 | 266 | Here are the steps to create a load balanced setup: |
| 267 | 267 | |
| 268 | 268 | - Create a master instance holding all data (see http://wiki.sapsailing.com/wiki/amazon-ec2#Setting-up-Master-and-Replica) |
| 269 | +- When using the Race Committee App (RCApp), make sure the app is configured to send its data to the master instance and not the ELB (otherwise, write requests may end up at replicas which they wouldn't know how to handle). You may want to configure a separate URL for the master server for this purpose, so you don't have to re-configure the RCApp devices when switching to a different master server. |
|
| 269 | 270 | - Create `n` instances that are configured to connect to the master server |
| 270 | 271 | - Create a load balancer that redirects everything from HTTP port 8888 to HTTP port 8888 and leave the other switches and checkboxes on their default value |
| 271 | 272 | - As "Ping Port" enter HTTP port 8888 and use /index.html as the "Ping Path." Leave the other values on their defaults again. |
| 272 | 273 | - Put the ELB into the "Sailing Analytics App" security group as it will appear in the landscape as a regular sailing analytics application server. |
| 273 | 274 | - Associate all your instances |
| 274 | -- Connect your domain with the IP of the load balancer. It could be a good idea to use an Elastic IP that always stays the same for the domain and associate it with your load balancer. That way you can also easily switch between a load balancer and a single instance setup. |
|
| 275 | +- Connect your domain with the IP of the load balancer. It could be a good idea to use an Elastic IP that always stays the same for the domain and associate it with your load balancer. That way you can also easily switch between a load balancer and a single instance setup. Again, remember not to let the RCApp devices point to the ELB domain as their updates could hit a replica which wouldn't know how to handle! |
|
| 275 | 276 | |
| 276 | -Amazon ELB is designed to handle unlimited concurrent requests per second with “gradually increasing” load pattern (although it's initial capacity is described to reach 20k requests/secs). It is not designed to handle heavy sudden spike of load or flash traffic because of it's internal structure where it needs to fire up more instances when load increases. ELB's can be pre-warmed though by writing to the AWS Support Team. |
|
| 277 | +Amazon ELB is designed to handle unlimited concurrent requests per second with “gradually increasing” load pattern (although it's initial capacity is described to reach 20k requests/secs). It is not designed to handle heavy sudden spike of load or flash traffic because of its internal structure where it needs to fire up more instances when load increases. ELB's can be pre-warmed though by writing to the AWS Support Team. |
|
| 277 | 278 | |
| 278 | 279 | ### Access MongoDB database |
| 279 | 280 |
wiki/images/winscp/winscp-create-folder.png
| ... | ... | Binary files /dev/null and b/wiki/images/winscp/winscp-create-folder.png differ |
wiki/images/winscp/winscp-install-1.png
| ... | ... | Binary files /dev/null and b/wiki/images/winscp/winscp-install-1.png differ |
wiki/images/winscp/winscp-keys.png
| ... | ... | Binary files /dev/null and b/wiki/images/winscp/winscp-keys.png differ |
wiki/images/winscp/winscp-login.png
| ... | ... | Binary files /dev/null and b/wiki/images/winscp/winscp-login.png differ |
wiki/images/winscp/winscp-var-www-static.png
| ... | ... | Binary files /dev/null and b/wiki/images/winscp/winscp-var-www-static.png differ |
wiki/racelog-tracking/app-spec/.gitignore
| ... | ... | @@ -1 +0,0 @@ |
| 1 | -app-spec.pdf |
wiki/racelog-tracking/app-spec/app-spec.md
| ... | ... | @@ -50,12 +50,14 @@ First the use cases for the tracking app are presented, which should give a good |
| 50 | 50 | The proposed smartphone app should enable casual users to apply the Sailing Analytics to their own tracking data. The following paragraphs describe the most important use cases in this context from the viewpoint of the users, leaving the technical details for a later section. The use cases are ordered by their importance - the ones that should be supported first are presented first. |
| 51 | 51 | |
| 52 | 52 | ### Individual Training |
| 53 | -This can be considered as being the simplest use-case. After downloading the app, the sailor creates a user account. Alternatively, he may log in to an existing account. As is common for apps this login should persist across app and phone restarts, until the users explicitly logs out. The user data could also serve as the basis for the competitor data (for which additional information about the country, sail-number and boatclass is required), but this discussed later on in the technical addendum. |
|
| 53 | +<div class="image"><img src="login.png" width="400px"/></div> |
|
| 54 | + |
|
| 55 | +This can be considered as being the simplest use-case. After downloading the app, the sailor creates a user account. Alternatively, he may log in to an existing account. As is common for apps this login should persist across app and phone restarts, until the users explicitly logs out. The user data could also serve as the basis for the competitor data (for which additional information about the country, sail-number and boatclass is required). |
|
| 54 | 56 | |
| 55 | 57 | !!! server |
| 56 | 58 | Clarify how user and competitor management play together. Are they entirely separate, and a user can create different competitors for his account, and switch between them, or is a user the same thing as a competitor? What happens, e.g., if a user switches to a different boat - changing the boat class and sail-number in his user preferences would cause wrong information for old races. Instead of having to login with a different user, instead the user might select from the list of competitors he has created from that account, or choose to add a new one. |
| 57 | 59 | |
| 58 | -<div class="image"><img src="training.svg"/></div> |
|
| 60 | +<div class="image"><img src="training.png" width="700px"/></div> |
|
| 59 | 61 | |
| 60 | 62 | On the following start screen there is a dominant button for starting the tracking - this functionality has to be readily available at all times, as missing out on tracking data is the potentially worst thing that can happen - everything else can be done later on. During tracking, the following information should be shown: |
| 61 | 63 | |
| ... | ... | @@ -186,6 +188,25 @@ The Leaderboard is a pretty complex table. It includes several levels of hierarc |
| 186 | 188 | An important part of the app surely also lies in managing the existing tracks and races generated from these tracks. On the one hand, the user may simply wish to see what tracks are on his phone (and what their synchronization status to the server is), and browser through his races. The list of races then serves as an entrypoint for analysis or sharing. |
| 187 | 189 | |
| 188 | 190 | |
| 191 | +### Start Line Analysis |
|
| 192 | +This app is targeted at sailors as users - enabling them to track and analyze their own training sessions and races. To improve, a sailor needs to know which part of his performance can be optimized. Apart from the RaceBoard, a detailed start line analysis can be helpful. Interesting figures include: |
|
| 193 | + |
|
| 194 | +* distance to start line at time of start |
|
| 195 | +* start on left or right side of line |
|
| 196 | +* on which tack was the boat at time of start |
|
| 197 | +* speed on crossing the start line |
|
| 198 | +* speed at time of start |
|
| 199 | +* rank for the first few 10s-intervals (rank at 10s/20s/... into the race) |
|
| 200 | + |
|
| 201 | +In addition to the figures for the own boat, these figures could be compared to those of other competitors when analyzing a race. By doing so, a sailor can learn what others are doing better or worse than him. |
|
| 202 | + |
|
| 203 | + |
|
| 204 | +### Social Media Integration |
|
| 205 | +Enabling social interaction within the app can increase the visibility of both the app and the Sailing Analytics. Part of this is allowing users to invite others to participate in a race. Social interaction within the app is one part of this: users can invite other users to participate in their race, and communicate directly e.g. by setting the start time. |
|
| 206 | + |
|
| 207 | +Connecting to existing social networks and communication channels is another simple yet effective measure to this end. A race or regatta (represented by a URL) can be easily shared through these. |
|
| 208 | +The URL should be handled by the app if available. Otherwise a mobile representation of the requested site should be rendered, ideally with an additional recommendation on installing the app. |
|
| 209 | + |
|
| 189 | 210 | |
| 190 | 211 | ## Technical Addendum |
| 191 | 212 | This addendum addresses more technical issues that seem obvious from the point in time of writing this documentation. More technical issues will surely arise during implementation, but the following sections try to cover the larger areas that can already be foreseen. |
| ... | ... | @@ -217,6 +238,37 @@ The question arises, which other steps should also be made accessible through a |
| 217 | 238 | Basically, this means duplicating functionality (maybe even for the third time: complex regatta management in the AdminConsole, simple wizard on the smartphone, intermediate one in the Smartphone-Tracking web interface) to allow user to perform difficult or more complex tasks at their computer. This does not have to be implemented immediately, at least we can wait to see for which steps users would like to see this possibility most. |
| 218 | 239 | |
| 219 | 240 | |
| 241 | +### Necessary APIs |
|
| 242 | +The various components of the app that have been described have to interact with the existing Sailing Analytics backend. Some of these interactions are entirely new, others already exist - but only for the web interface (through the _GWT-RPC_ channel). Other interactions again are already available in the form of _RESTful JSON APIs_ which are used by the RaceCommittee App. The following list gives an overview of the data and interactions which span both mobile device and backend. |
|
| 243 | + |
|
| 244 | +* Account Management |
|
| 245 | + * Creating an account |
|
| 246 | + * Logging in |
|
| 247 | + * Searching user list |
|
| 248 | +* Competitor Management |
|
| 249 | + * Creating a new competitor |
|
| 250 | + * Editing an existing competitor |
|
| 251 | +* Submitting GPS Fixes |
|
| 252 | +* Submitting further sensor data and multimedia |
|
| 253 | +* Track Management |
|
| 254 | + * Retrieving list |
|
| 255 | + * Defining visibility |
|
| 256 | +* Race Management |
|
| 257 | + * Retrieving list |
|
| 258 | + * Creating a race |
|
| 259 | + * Course definition |
|
| 260 | + * Setting start time |
|
| 261 | + * Inviting users |
|
| 262 | + * Defining visibility |
|
| 263 | +* Regatta Management |
|
| 264 | + * Retrieving list |
|
| 265 | + * Creating a regatta |
|
| 266 | + * Adding a race |
|
| 267 | + * Defining visibility |
|
| 268 | +* RaceBoard |
|
| 269 | + * Variety of measurements and calculated figures |
|
| 270 | + |
|
| 271 | + |
|
| 220 | 272 | ### Backend Implementation Details |
| 221 | 273 | |
| 222 | 274 | #### Receiving and storing Tracking Data |
wiki/racelog-tracking/app-spec/app-spec.pdf
| ... | ... | Binary files /dev/null and b/wiki/racelog-tracking/app-spec/app-spec.pdf differ |
wiki/racelog-tracking/app-spec/login.png
| ... | ... | Binary files /dev/null and b/wiki/racelog-tracking/app-spec/login.png differ |
wiki/racelog-tracking/app-spec/training.dia
| ... | ... | Binary files /dev/null and b/wiki/racelog-tracking/app-spec/training.dia differ |
wiki/racelog-tracking/app-spec/training.graphml
| ... | ... | @@ -1,892 +0,0 @@ |
| 1 | -<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
|
| 2 | -<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd"> |
|
| 3 | - <!--Created by yEd 3.12.2--> |
|
| 4 | - <key for="graphml" id="d0" yfiles.type="resources"/> |
|
| 5 | - <key for="port" id="d1" yfiles.type="portgraphics"/> |
|
| 6 | - <key for="port" id="d2" yfiles.type="portgeometry"/> |
|
| 7 | - <key for="port" id="d3" yfiles.type="portuserdata"/> |
|
| 8 | - <key attr.name="url" attr.type="string" for="node" id="d4"/> |
|
| 9 | - <key attr.name="description" attr.type="string" for="node" id="d5"/> |
|
| 10 | - <key for="node" id="d6" yfiles.type="nodegraphics"/> |
|
| 11 | - <key attr.name="Description" attr.type="string" for="graph" id="d7"/> |
|
| 12 | - <key attr.name="url" attr.type="string" for="edge" id="d8"/> |
|
| 13 | - <key attr.name="description" attr.type="string" for="edge" id="d9"/> |
|
| 14 | - <key for="edge" id="d10" yfiles.type="edgegraphics"/> |
|
| 15 | - <graph edgedefault="directed" id="G"> |
|
| 16 | - <data key="d7"/> |
|
| 17 | - <node id="n0"> |
|
| 18 | - <data key="d5"/> |
|
| 19 | - <data key="d6"> |
|
| 20 | - <y:ShapeNode> |
|
| 21 | - <y:Geometry height="10.0" width="87.11275724687482" x="401.2160000000001" y="708.7535999999998"/> |
|
| 22 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 23 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 24 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="15.640625" modelName="custom" textColor="#000000" visible="true" width="49.712890625" x="18.69993331093741" y="-2.8203125">Statistics<y:LabelModel> |
|
| 25 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 26 | - </y:LabelModel> |
|
| 27 | - <y:ModelParameter> |
|
| 28 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 29 | - </y:ModelParameter> |
|
| 30 | - </y:NodeLabel> |
|
| 31 | - <y:Shape type="rectangle"/> |
|
| 32 | - </y:ShapeNode> |
|
| 33 | - </data> |
|
| 34 | - </node> |
|
| 35 | - <node id="n1" yfiles.foldertype="group"> |
|
| 36 | - <data key="d4"/> |
|
| 37 | - <data key="d5"/> |
|
| 38 | - <data key="d6"> |
|
| 39 | - <y:ProxyAutoBoundsNode> |
|
| 40 | - <y:Realizers active="0"> |
|
| 41 | - <y:GroupNode> |
|
| 42 | - <y:Geometry height="210.0" width="142.0" x="371.0" y="425.0"/> |
|
| 43 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 44 | - <y:BorderStyle hasColor="false" type="dashed" width="1.0"/> |
|
| 45 | - <y:NodeLabel alignment="right" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="142.0" x="0.0" y="0.0"/> |
|
| 46 | - <y:Shape type="roundrectangle"/> |
|
| 47 | - <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
|
| 48 | - <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/> |
|
| 49 | - <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
|
| 50 | - </y:GroupNode> |
|
| 51 | - <y:GroupNode> |
|
| 52 | - <y:Geometry height="50.0" width="50.0" x="176.0" y="318.4287999999998"/> |
|
| 53 | - <y:Fill color="#F5F5F5" transparent="false"/> |
|
| 54 | - <y:BorderStyle color="#000000" type="dashed" width="1.0"/> |
|
| 55 | - <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="65.201171875" x="-7.6005859375" y="0.0">Folder 1</y:NodeLabel> |
|
| 56 | - <y:Shape type="roundrectangle"/> |
|
| 57 | - <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
|
| 58 | - <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/> |
|
| 59 | - <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
|
| 60 | - </y:GroupNode> |
|
| 61 | - </y:Realizers> |
|
| 62 | - </y:ProxyAutoBoundsNode> |
|
| 63 | - </data> |
|
| 64 | - <graph edgedefault="directed" id="n1:"> |
|
| 65 | - <node id="n1::n0"> |
|
| 66 | - <data key="d5"/> |
|
| 67 | - <data key="d6"> |
|
| 68 | - <y:ShapeNode> |
|
| 69 | - <y:Geometry height="176.0" width="112.0" x="386.0" y="444.0"/> |
|
| 70 | - <y:Fill color="#000000" transparent="false"/> |
|
| 71 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 72 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="54.0" y="86.0"> |
|
| 73 | - <y:LabelModel> |
|
| 74 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 75 | - </y:LabelModel> |
|
| 76 | - <y:ModelParameter> |
|
| 77 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 78 | - </y:ModelParameter> |
|
| 79 | - </y:NodeLabel> |
|
| 80 | - <y:Shape type="roundrectangle"/> |
|
| 81 | - </y:ShapeNode> |
|
| 82 | - </data> |
|
| 83 | - </node> |
|
| 84 | - <node id="n1::n1"> |
|
| 85 | - <data key="d5"/> |
|
| 86 | - <data key="d6"> |
|
| 87 | - <y:ShapeNode> |
|
| 88 | - <y:Geometry height="137.0" width="101.0" x="391.5" y="450.0"/> |
|
| 89 | - <y:Fill color="#FFFFFF" transparent="false"/> |
|
| 90 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 91 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="48.5" y="66.5"> |
|
| 92 | - <y:LabelModel> |
|
| 93 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 94 | - </y:LabelModel> |
|
| 95 | - <y:ModelParameter> |
|
| 96 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 97 | - </y:ModelParameter> |
|
| 98 | - </y:NodeLabel> |
|
| 99 | - <y:Shape type="rectangle"/> |
|
| 100 | - </y:ShapeNode> |
|
| 101 | - </data> |
|
| 102 | - </node> |
|
| 103 | - <node id="n1::n2"> |
|
| 104 | - <data key="d5"/> |
|
| 105 | - <data key="d6"> |
|
| 106 | - <y:ShapeNode> |
|
| 107 | - <y:Geometry height="21.0" width="21.0" x="431.5" y="594.0"/> |
|
| 108 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 109 | - <y:BorderStyle color="#FFFFFF" type="line" width="1.0"/> |
|
| 110 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="8.5" y="8.5"> |
|
| 111 | - <y:LabelModel> |
|
| 112 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 113 | - </y:LabelModel> |
|
| 114 | - <y:ModelParameter> |
|
| 115 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 116 | - </y:ModelParameter> |
|
| 117 | - </y:NodeLabel> |
|
| 118 | - <y:Shape type="ellipse"/> |
|
| 119 | - </y:ShapeNode> |
|
| 120 | - </data> |
|
| 121 | - </node> |
|
| 122 | - </graph> |
|
| 123 | - </node> |
|
| 124 | - <node id="n2"> |
|
| 125 | - <data key="d5"/> |
|
| 126 | - <data key="d6"> |
|
| 127 | - <y:ShapeNode> |
|
| 128 | - <y:Geometry height="16.0" width="87.0" x="398.5" y="475.0"/> |
|
| 129 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 130 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 131 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="41.5" y="6.0"> |
|
| 132 | - <y:LabelModel> |
|
| 133 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 134 | - </y:LabelModel> |
|
| 135 | - <y:ModelParameter> |
|
| 136 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 137 | - </y:ModelParameter> |
|
| 138 | - </y:NodeLabel> |
|
| 139 | - <y:Shape type="rectangle"/> |
|
| 140 | - </y:ShapeNode> |
|
| 141 | - </data> |
|
| 142 | - </node> |
|
| 143 | - <node id="n3"> |
|
| 144 | - <data key="d5"/> |
|
| 145 | - <data key="d6"> |
|
| 146 | - <y:ShapeNode> |
|
| 147 | - <y:Geometry height="16.0" width="87.0" x="398.5" y="496.5"/> |
|
| 148 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 149 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 150 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="41.5" y="6.0"> |
|
| 151 | - <y:LabelModel> |
|
| 152 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 153 | - </y:LabelModel> |
|
| 154 | - <y:ModelParameter> |
|
| 155 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 156 | - </y:ModelParameter> |
|
| 157 | - </y:NodeLabel> |
|
| 158 | - <y:Shape type="rectangle"/> |
|
| 159 | - </y:ShapeNode> |
|
| 160 | - </data> |
|
| 161 | - </node> |
|
| 162 | - <node id="n4"> |
|
| 163 | - <data key="d5"/> |
|
| 164 | - <data key="d6"> |
|
| 165 | - <y:ShapeNode> |
|
| 166 | - <y:Geometry height="16.0" width="49.0" x="417.5" y="520.0"/> |
|
| 167 | - <y:Fill color="#C0C0C0" transparent="false"/> |
|
| 168 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 169 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="36.583984375" x="6.2080078125" y="-0.984375">Login<y:LabelModel> |
|
| 170 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 171 | - </y:LabelModel> |
|
| 172 | - <y:ModelParameter> |
|
| 173 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 174 | - </y:ModelParameter> |
|
| 175 | - </y:NodeLabel> |
|
| 176 | - <y:Shape type="rectangle"/> |
|
| 177 | - </y:ShapeNode> |
|
| 178 | - </data> |
|
| 179 | - </node> |
|
| 180 | - <node id="n5"> |
|
| 181 | - <data key="d5"/> |
|
| 182 | - <data key="d6"> |
|
| 183 | - <y:ShapeNode> |
|
| 184 | - <y:Geometry height="10.0" width="93.0" x="395.5" y="559.4464"/> |
|
| 185 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 186 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 187 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="27.28125" modelName="custom" textColor="#000000" visible="true" width="69.2783203125" x="11.86083984375" y="-8.640625">already have |
|
| 188 | -an account?<y:LabelModel> |
|
| 189 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 190 | - </y:LabelModel> |
|
| 191 | - <y:ModelParameter> |
|
| 192 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 193 | - </y:ModelParameter> |
|
| 194 | - </y:NodeLabel> |
|
| 195 | - <y:Shape type="rectangle"/> |
|
| 196 | - </y:ShapeNode> |
|
| 197 | - </data> |
|
| 198 | - </node> |
|
| 199 | - <node id="n6" yfiles.foldertype="group"> |
|
| 200 | - <data key="d4"/> |
|
| 201 | - <data key="d5"/> |
|
| 202 | - <data key="d6"> |
|
| 203 | - <y:ProxyAutoBoundsNode> |
|
| 204 | - <y:Realizers active="0"> |
|
| 205 | - <y:GroupNode> |
|
| 206 | - <y:Geometry height="209.99999999999994" width="283.82875724687494" x="522.5" y="425.00000000000006"/> |
|
| 207 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 208 | - <y:BorderStyle hasColor="false" type="dashed" width="1.0"/> |
|
| 209 | - <y:NodeLabel alignment="right" autoSizePolicy="node_width" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="283.82875724687494" x="0.0" y="0.0"/> |
|
| 210 | - <y:Shape type="roundrectangle"/> |
|
| 211 | - <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
|
| 212 | - <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/> |
|
| 213 | - <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
|
| 214 | - </y:GroupNode> |
|
| 215 | - <y:GroupNode> |
|
| 216 | - <y:Geometry height="50.0" width="50.0" x="557.2799999999999" y="326.16"/> |
|
| 217 | - <y:Fill color="#F5F5F5" transparent="false"/> |
|
| 218 | - <y:BorderStyle color="#000000" type="dashed" width="1.0"/> |
|
| 219 | - <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="65.201171875" x="-7.6005859375" y="0.0">Folder 1</y:NodeLabel> |
|
| 220 | - <y:Shape type="roundrectangle"/> |
|
| 221 | - <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
|
| 222 | - <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/> |
|
| 223 | - <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
|
| 224 | - </y:GroupNode> |
|
| 225 | - </y:Realizers> |
|
| 226 | - </y:ProxyAutoBoundsNode> |
|
| 227 | - </data> |
|
| 228 | - <graph edgedefault="directed" id="n6:"> |
|
| 229 | - <node id="n6::n0"> |
|
| 230 | - <data key="d5"/> |
|
| 231 | - <data key="d6"> |
|
| 232 | - <y:ShapeNode> |
|
| 233 | - <y:Geometry height="176.0" width="112.0" x="537.5" y="444.00000000000006"/> |
|
| 234 | - <y:Fill color="#000000" transparent="false"/> |
|
| 235 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 236 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="54.0" y="85.99999999999994"> |
|
| 237 | - <y:LabelModel> |
|
| 238 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 239 | - </y:LabelModel> |
|
| 240 | - <y:ModelParameter> |
|
| 241 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 242 | - </y:ModelParameter> |
|
| 243 | - </y:NodeLabel> |
|
| 244 | - <y:Shape type="roundrectangle"/> |
|
| 245 | - </y:ShapeNode> |
|
| 246 | - </data> |
|
| 247 | - </node> |
|
| 248 | - <node id="n6::n1"> |
|
| 249 | - <data key="d5"/> |
|
| 250 | - <data key="d6"> |
|
| 251 | - <y:ShapeNode> |
|
| 252 | - <y:Geometry height="137.0" width="101.0" x="543.0" y="450.00000000000006"/> |
|
| 253 | - <y:Fill color="#FFFFFF" transparent="false"/> |
|
| 254 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 255 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="48.5" y="66.49999999999994"> |
|
| 256 | - <y:LabelModel> |
|
| 257 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 258 | - </y:LabelModel> |
|
| 259 | - <y:ModelParameter> |
|
| 260 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 261 | - </y:ModelParameter> |
|
| 262 | - </y:NodeLabel> |
|
| 263 | - <y:Shape type="rectangle"/> |
|
| 264 | - </y:ShapeNode> |
|
| 265 | - </data> |
|
| 266 | - </node> |
|
| 267 | - <node id="n6::n2"> |
|
| 268 | - <data key="d5"/> |
|
| 269 | - <data key="d6"> |
|
| 270 | - <y:ShapeNode> |
|
| 271 | - <y:Geometry height="21.0" width="21.0" x="583.0" y="594.0"/> |
|
| 272 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 273 | - <y:BorderStyle color="#FFFFFF" type="line" width="1.0"/> |
|
| 274 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="8.5" y="8.5"> |
|
| 275 | - <y:LabelModel> |
|
| 276 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 277 | - </y:LabelModel> |
|
| 278 | - <y:ModelParameter> |
|
| 279 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 280 | - </y:ModelParameter> |
|
| 281 | - </y:NodeLabel> |
|
| 282 | - <y:Shape type="ellipse"/> |
|
| 283 | - </y:ShapeNode> |
|
| 284 | - </data> |
|
| 285 | - </node> |
|
| 286 | - <node id="n6::n3"> |
|
| 287 | - <data key="d5"/> |
|
| 288 | - <data key="d6"> |
|
| 289 | - <y:ShapeNode> |
|
| 290 | - <y:Geometry height="3.785599999999988" width="53.603999999999814" x="734.2160000000001" y="481.3104000000001"/> |
|
| 291 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 292 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 293 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="4.0" x="24.801999999999907" y="-0.10720000000003438"/> |
|
| 294 | - <y:Shape type="rectangle"/> |
|
| 295 | - </y:ShapeNode> |
|
| 296 | - </data> |
|
| 297 | - </node> |
|
| 298 | - <node id="n6::n4"> |
|
| 299 | - <data key="d5"/> |
|
| 300 | - <data key="d6"> |
|
| 301 | - <y:ShapeNode> |
|
| 302 | - <y:Geometry height="15.463360000000023" width="92.32997959270392" x="698.998777654171" y="568.7535999999999"/> |
|
| 303 | - <y:Fill color="#C0C0C0" transparent="false"/> |
|
| 304 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 305 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="15.640625" modelName="custom" textColor="#000000" visible="true" width="73.013671875" x="9.65815385885196" y="-0.08863250000001699">Stop Tracking<y:LabelModel> |
|
| 306 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 307 | - </y:LabelModel> |
|
| 308 | - <y:ModelParameter> |
|
| 309 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 310 | - </y:ModelParameter> |
|
| 311 | - </y:NodeLabel> |
|
| 312 | - <y:Shape type="rectangle"/> |
|
| 313 | - </y:ShapeNode> |
|
| 314 | - </data> |
|
| 315 | - </node> |
|
| 316 | - </graph> |
|
| 317 | - </node> |
|
| 318 | - <node id="n7"> |
|
| 319 | - <data key="d5"/> |
|
| 320 | - <data key="d6"> |
|
| 321 | - <y:ShapeNode> |
|
| 322 | - <y:Geometry height="3.785599999999988" width="53.603999999999814" x="582.7160000000001" y="481.31040000000013"/> |
|
| 323 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 324 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 325 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="4.0" x="24.801999999999907" y="-0.10719999999997754"/> |
|
| 326 | - <y:Shape type="rectangle"/> |
|
| 327 | - </y:ShapeNode> |
|
| 328 | - </data> |
|
| 329 | - </node> |
|
| 330 | - <node id="n8" yfiles.foldertype="group"> |
|
| 331 | - <data key="d4"/> |
|
| 332 | - <data key="d5"/> |
|
| 333 | - <data key="d6"> |
|
| 334 | - <y:ProxyAutoBoundsNode> |
|
| 335 | - <y:Realizers active="0"> |
|
| 336 | - <y:GroupNode> |
|
| 337 | - <y:Geometry height="46.0" width="123.34248550625011" x="531.8287572468749" y="460.2032000000001"/> |
|
| 338 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 339 | - <y:BorderStyle hasColor="false" type="dashed" width="1.0"/> |
|
| 340 | - <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="false" width="123.34248550625011" x="0.0" y="0.0">Group 3</y:NodeLabel> |
|
| 341 | - <y:Shape type="roundrectangle"/> |
|
| 342 | - <y:State closed="false" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
|
| 343 | - <y:Insets bottom="15" bottomF="15.0" left="15" leftF="15.0" right="15" rightF="15.0" top="15" topF="15.0"/> |
|
| 344 | - <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
|
| 345 | - </y:GroupNode> |
|
| 346 | - <y:GroupNode> |
|
| 347 | - <y:Geometry height="50.0" width="50.0" x="0.0" y="60.0"/> |
|
| 348 | - <y:Fill color="#F5F5F5" transparent="false"/> |
|
| 349 | - <y:BorderStyle color="#000000" type="dashed" width="1.0"/> |
|
| 350 | - <y:NodeLabel alignment="right" autoSizePolicy="node_width" backgroundColor="#EBEBEB" borderDistance="0.0" fontFamily="Dialog" fontSize="15" fontStyle="plain" hasLineColor="false" height="21.4609375" modelName="internal" modelPosition="t" textColor="#000000" visible="true" width="65.201171875" x="-7.6005859375" y="0.0">Folder 3</y:NodeLabel> |
|
| 351 | - <y:Shape type="roundrectangle"/> |
|
| 352 | - <y:State closed="true" closedHeight="50.0" closedWidth="50.0" innerGraphDisplayEnabled="false"/> |
|
| 353 | - <y:Insets bottom="5" bottomF="5.0" left="5" leftF="5.0" right="5" rightF="5.0" top="5" topF="5.0"/> |
|
| 354 | - <y:BorderInsets bottom="0" bottomF="0.0" left="0" leftF="0.0" right="0" rightF="0.0" top="0" topF="0.0"/> |
|
| 355 | - </y:GroupNode> |
|
| 356 | - </y:Realizers> |
|
| 357 | - </y:ProxyAutoBoundsNode> |
|
| 358 | - </data> |
|
| 359 | - <graph edgedefault="directed" id="n8:"> |
|
| 360 | - <node id="n8::n0"> |
|
| 361 | - <data key="d5"/> |
|
| 362 | - <data key="d6"> |
|
| 363 | - <y:ShapeNode> |
|
| 364 | - <y:Geometry height="16.0" width="93.0" x="546.8287572468749" y="475.2032000000001"/> |
|
| 365 | - <y:Fill color="#C0C0C0" transparent="false"/> |
|
| 366 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 367 | - <y:NodeLabel alignment="right" autoSizePolicy="content" borderDistance="0.0" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="44.5" y="6.0"> |
|
| 368 | - <y:LabelModel> |
|
| 369 | - <y:SmartNodeLabelModel distance="0.0"/> |
|
| 370 | - </y:LabelModel> |
|
| 371 | - <y:ModelParameter> |
|
| 372 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 373 | - </y:ModelParameter> |
|
| 374 | - </y:NodeLabel> |
|
| 375 | - <y:Shape type="rectangle"/> |
|
| 376 | - </y:ShapeNode> |
|
| 377 | - </data> |
|
| 378 | - </node> |
|
| 379 | - <node id="n8::n1"> |
|
| 380 | - <data key="d5"/> |
|
| 381 | - <data key="d6"> |
|
| 382 | - <y:ShapeNode> |
|
| 383 | - <y:Geometry height="16.0" width="68.72320320000017" x="568.455637246875" y="475.2032000000001"/> |
|
| 384 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 385 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 386 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="15.640625" modelName="custom" textColor="#000000" visible="true" width="74.7080078125" x="-2.9924023062499145" y="0.1796875">Start Tracking<y:LabelModel> |
|
| 387 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 388 | - </y:LabelModel> |
|
| 389 | - <y:ModelParameter> |
|
| 390 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 391 | - </y:ModelParameter> |
|
| 392 | - </y:NodeLabel> |
|
| 393 | - <y:Shape type="rectangle"/> |
|
| 394 | - </y:ShapeNode> |
|
| 395 | - </data> |
|
| 396 | - </node> |
|
| 397 | - <node id="n8::n2"> |
|
| 398 | - <data key="d5"/> |
|
| 399 | - <data key="d6"> |
|
| 400 | - <y:ShapeNode> |
|
| 401 | - <y:Geometry height="14.926720000000046" width="14.926720000000046" x="547.498777654171" y="475.7398400000001"/> |
|
| 402 | - <y:Fill color="#FF0000" transparent="false"/> |
|
| 403 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 404 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="5.46336000000008" y="5.463360000000023"> |
|
| 405 | - <y:LabelModel> |
|
| 406 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 407 | - </y:LabelModel> |
|
| 408 | - <y:ModelParameter> |
|
| 409 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 410 | - </y:ModelParameter> |
|
| 411 | - </y:NodeLabel> |
|
| 412 | - <y:Shape type="ellipse"/> |
|
| 413 | - </y:ShapeNode> |
|
| 414 | - </data> |
|
| 415 | - </node> |
|
| 416 | - </graph> |
|
| 417 | - </node> |
|
| 418 | - <node id="n9"> |
|
| 419 | - <data key="d5"/> |
|
| 420 | - <data key="d6"> |
|
| 421 | - <y:ShapeNode> |
|
| 422 | - <y:Geometry height="15.463360000000023" width="92.32997959270392" x="547.498777654171" y="515.096"/> |
|
| 423 | - <y:Fill color="#C0C0C0" transparent="false"/> |
|
| 424 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 425 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="15.640625" modelName="custom" textColor="#000000" visible="true" width="54.5712890625" x="18.87934526510196" y="-0.08863250000001699">My Tracks<y:LabelModel> |
|
| 426 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 427 | - </y:LabelModel> |
|
| 428 | - <y:ModelParameter> |
|
| 429 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 430 | - </y:ModelParameter> |
|
| 431 | - </y:NodeLabel> |
|
| 432 | - <y:Shape type="rectangle"/> |
|
| 433 | - </y:ShapeNode> |
|
| 434 | - </data> |
|
| 435 | - </node> |
|
| 436 | - <node id="n10"> |
|
| 437 | - <data key="d5"/> |
|
| 438 | - <data key="d6"> |
|
| 439 | - <y:ShapeNode> |
|
| 440 | - <y:Geometry height="116.01600000000008" width="101.0" x="694.5" y="450.0"/> |
|
| 441 | - <y:Fill color="#99CCFF" transparent="false"/> |
|
| 442 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 443 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#3366FF" visible="true" width="4.0" x="48.5" y="56.00800000000004"> |
|
| 444 | - <y:LabelModel> |
|
| 445 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 446 | - </y:LabelModel> |
|
| 447 | - <y:ModelParameter> |
|
| 448 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 449 | - </y:ModelParameter> |
|
| 450 | - </y:NodeLabel> |
|
| 451 | - <y:Shape type="rectangle"/> |
|
| 452 | - </y:ShapeNode> |
|
| 453 | - </data> |
|
| 454 | - </node> |
|
| 455 | - <node id="n11"> |
|
| 456 | - <data key="d5"/> |
|
| 457 | - <data key="d6"> |
|
| 458 | - <y:ShapeNode> |
|
| 459 | - <y:Geometry height="30.0" width="30.0" x="711.6919630120962" y="527.2550478827525"/> |
|
| 460 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 461 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 462 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="13.0" y="13.0"> |
|
| 463 | - <y:LabelModel> |
|
| 464 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 465 | - </y:LabelModel> |
|
| 466 | - <y:ModelParameter> |
|
| 467 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 468 | - </y:ModelParameter> |
|
| 469 | - </y:NodeLabel> |
|
| 470 | - <y:Shape type="rectangle"/> |
|
| 471 | - </y:ShapeNode> |
|
| 472 | - </data> |
|
| 473 | - </node> |
|
| 474 | - <node id="n12"> |
|
| 475 | - <data key="d5"/> |
|
| 476 | - <data key="d6"> |
|
| 477 | - <y:ShapeNode> |
|
| 478 | - <y:Geometry height="37.96672000000001" width="92.32997959270392" x="395.998777654171" y="708.7536"/> |
|
| 479 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 480 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 481 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="38.921875" modelName="custom" textColor="#000000" visible="true" width="53.08203125" x="19.62397417135196" y="-0.47757750000005217">Statistics: |
|
| 482 | -... |
|
| 483 | -...<y:LabelModel> |
|
| 484 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 485 | - </y:LabelModel> |
|
| 486 | - <y:ModelParameter> |
|
| 487 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 488 | - </y:ModelParameter> |
|
| 489 | - </y:NodeLabel> |
|
| 490 | - <y:Shape type="rectangle"/> |
|
| 491 | - </y:ShapeNode> |
|
| 492 | - </data> |
|
| 493 | - </node> |
|
| 494 | - <node id="n13"> |
|
| 495 | - <data key="d5"/> |
|
| 496 | - <data key="d6"> |
|
| 497 | - <y:ShapeNode> |
|
| 498 | - <y:Geometry height="176.0" width="112.0" x="386.0" y="644.0"/> |
|
| 499 | - <y:Fill color="#000000" transparent="false"/> |
|
| 500 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 501 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="54.0" y="86.0"> |
|
| 502 | - <y:LabelModel> |
|
| 503 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 504 | - </y:LabelModel> |
|
| 505 | - <y:ModelParameter> |
|
| 506 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 507 | - </y:ModelParameter> |
|
| 508 | - </y:NodeLabel> |
|
| 509 | - <y:Shape type="roundrectangle"/> |
|
| 510 | - </y:ShapeNode> |
|
| 511 | - </data> |
|
| 512 | - </node> |
|
| 513 | - <node id="n14"> |
|
| 514 | - <data key="d5"/> |
|
| 515 | - <data key="d6"> |
|
| 516 | - <y:ShapeNode> |
|
| 517 | - <y:Geometry height="137.0" width="101.0" x="391.5" y="650.0"/> |
|
| 518 | - <y:Fill color="#FFFFFF" transparent="false"/> |
|
| 519 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 520 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="48.5" y="66.5"> |
|
| 521 | - <y:LabelModel> |
|
| 522 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 523 | - </y:LabelModel> |
|
| 524 | - <y:ModelParameter> |
|
| 525 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 526 | - </y:ModelParameter> |
|
| 527 | - </y:NodeLabel> |
|
| 528 | - <y:Shape type="rectangle"/> |
|
| 529 | - </y:ShapeNode> |
|
| 530 | - </data> |
|
| 531 | - </node> |
|
| 532 | - <node id="n15"> |
|
| 533 | - <data key="d5"/> |
|
| 534 | - <data key="d6"> |
|
| 535 | - <y:ShapeNode> |
|
| 536 | - <y:Geometry height="176.0" width="112.0" x="689.0" y="444.0"/> |
|
| 537 | - <y:Fill color="#000000" transparent="false"/> |
|
| 538 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 539 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="54.0" y="86.0"> |
|
| 540 | - <y:LabelModel> |
|
| 541 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 542 | - </y:LabelModel> |
|
| 543 | - <y:ModelParameter> |
|
| 544 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 545 | - </y:ModelParameter> |
|
| 546 | - </y:NodeLabel> |
|
| 547 | - <y:Shape type="roundrectangle"/> |
|
| 548 | - </y:ShapeNode> |
|
| 549 | - </data> |
|
| 550 | - </node> |
|
| 551 | - <node id="n16"> |
|
| 552 | - <data key="d5"/> |
|
| 553 | - <data key="d6"> |
|
| 554 | - <y:ShapeNode> |
|
| 555 | - <y:Geometry height="137.0" width="101.0" x="694.5" y="450.0"/> |
|
| 556 | - <y:Fill color="#FFFFFF" transparent="false"/> |
|
| 557 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 558 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="48.5" y="66.5"> |
|
| 559 | - <y:LabelModel> |
|
| 560 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 561 | - </y:LabelModel> |
|
| 562 | - <y:ModelParameter> |
|
| 563 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 564 | - </y:ModelParameter> |
|
| 565 | - </y:NodeLabel> |
|
| 566 | - <y:Shape type="rectangle"/> |
|
| 567 | - </y:ShapeNode> |
|
| 568 | - </data> |
|
| 569 | - </node> |
|
| 570 | - <node id="n17"> |
|
| 571 | - <data key="d5"/> |
|
| 572 | - <data key="d6"> |
|
| 573 | - <y:ShapeNode> |
|
| 574 | - <y:Geometry height="21.0" width="21.0" x="734.5" y="593.9999999999999"/> |
|
| 575 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 576 | - <y:BorderStyle color="#FFFFFF" type="line" width="1.0"/> |
|
| 577 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="8.5" y="8.5"> |
|
| 578 | - <y:LabelModel> |
|
| 579 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 580 | - </y:LabelModel> |
|
| 581 | - <y:ModelParameter> |
|
| 582 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 583 | - </y:ModelParameter> |
|
| 584 | - </y:NodeLabel> |
|
| 585 | - <y:Shape type="ellipse"/> |
|
| 586 | - </y:ShapeNode> |
|
| 587 | - </data> |
|
| 588 | - </node> |
|
| 589 | - <node id="n18"> |
|
| 590 | - <data key="d5"/> |
|
| 591 | - <data key="d6"> |
|
| 592 | - <y:ShapeNode> |
|
| 593 | - <y:Geometry height="3.785599999999988" width="53.603999999999814" x="431.2160000000001" y="681.3104000000001"/> |
|
| 594 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 595 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 596 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="4.0" x="24.801999999999907" y="-0.10720000000003438"/> |
|
| 597 | - <y:Shape type="rectangle"/> |
|
| 598 | - </y:ShapeNode> |
|
| 599 | - </data> |
|
| 600 | - </node> |
|
| 601 | - <node id="n19"> |
|
| 602 | - <data key="d5"/> |
|
| 603 | - <data key="d6"> |
|
| 604 | - <y:ShapeNode> |
|
| 605 | - <y:Geometry height="21.0" width="21.0" x="431.5" y="793.9999999999999"/> |
|
| 606 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 607 | - <y:BorderStyle color="#FFFFFF" type="line" width="1.0"/> |
|
| 608 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="8.5" y="8.5"> |
|
| 609 | - <y:LabelModel> |
|
| 610 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 611 | - </y:LabelModel> |
|
| 612 | - <y:ModelParameter> |
|
| 613 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 614 | - </y:ModelParameter> |
|
| 615 | - </y:NodeLabel> |
|
| 616 | - <y:Shape type="ellipse"/> |
|
| 617 | - </y:ShapeNode> |
|
| 618 | - </data> |
|
| 619 | - </node> |
|
| 620 | - <node id="n20"> |
|
| 621 | - <data key="d5"/> |
|
| 622 | - <data key="d6"> |
|
| 623 | - <y:ShapeNode> |
|
| 624 | - <y:Geometry height="15.463360000000023" width="92.32997959270392" x="395.998777654171" y="768.7535999999999"/> |
|
| 625 | - <y:Fill color="#C0C0C0" transparent="false"/> |
|
| 626 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 627 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="15.640625" modelName="custom" textColor="#000000" visible="true" width="43.404296875" x="24.46284135885196" y="-0.08863250000001699">Analyze<y:LabelModel> |
|
| 628 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 629 | - </y:LabelModel> |
|
| 630 | - <y:ModelParameter> |
|
| 631 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 632 | - </y:ModelParameter> |
|
| 633 | - </y:NodeLabel> |
|
| 634 | - <y:Shape type="rectangle"/> |
|
| 635 | - </y:ShapeNode> |
|
| 636 | - </data> |
|
| 637 | - </node> |
|
| 638 | - <node id="n21"> |
|
| 639 | - <data key="d5"/> |
|
| 640 | - <data key="d6"> |
|
| 641 | - <y:ShapeNode> |
|
| 642 | - <y:Geometry height="46.0" width="92.32997959270392" x="395.998777654171" y="654.9152"/> |
|
| 643 | - <y:Fill color="#99CCFF" transparent="false"/> |
|
| 644 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 645 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#3366FF" visible="true" width="4.0" x="44.16498979635196" y="21.0"> |
|
| 646 | - <y:LabelModel> |
|
| 647 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 648 | - </y:LabelModel> |
|
| 649 | - <y:ModelParameter> |
|
| 650 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 651 | - </y:ModelParameter> |
|
| 652 | - </y:NodeLabel> |
|
| 653 | - <y:Shape type="rectangle"/> |
|
| 654 | - </y:ShapeNode> |
|
| 655 | - </data> |
|
| 656 | - </node> |
|
| 657 | - <node id="n22"> |
|
| 658 | - <data key="d5"/> |
|
| 659 | - <data key="d6"> |
|
| 660 | - <y:ShapeNode> |
|
| 661 | - <y:Geometry height="1.0" width="1.0" x="408.69196301209615" y="727.2550478827525"/> |
|
| 662 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 663 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 664 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="-1.5" y="-1.5"> |
|
| 665 | - <y:LabelModel> |
|
| 666 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 667 | - </y:LabelModel> |
|
| 668 | - <y:ModelParameter> |
|
| 669 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 670 | - </y:ModelParameter> |
|
| 671 | - </y:NodeLabel> |
|
| 672 | - <y:Shape type="rectangle"/> |
|
| 673 | - </y:ShapeNode> |
|
| 674 | - </data> |
|
| 675 | - </node> |
|
| 676 | - <node id="n23"> |
|
| 677 | - <data key="d5"/> |
|
| 678 | - <data key="d6"> |
|
| 679 | - <y:ShapeNode> |
|
| 680 | - <y:Geometry height="15.463360000000023" width="92.32997959270392" x="698.998777654171" y="568.7535999999999"/> |
|
| 681 | - <y:Fill color="#C0C0C0" transparent="false"/> |
|
| 682 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 683 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="10" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="15.640625" modelName="custom" textColor="#000000" visible="true" width="73.013671875" x="9.65815385885196" y="-0.08863250000001699">Stop Tracking<y:LabelModel> |
|
| 684 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 685 | - </y:LabelModel> |
|
| 686 | - <y:ModelParameter> |
|
| 687 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 688 | - </y:ModelParameter> |
|
| 689 | - </y:NodeLabel> |
|
| 690 | - <y:Shape type="rectangle"/> |
|
| 691 | - </y:ShapeNode> |
|
| 692 | - </data> |
|
| 693 | - </node> |
|
| 694 | - <node id="n24"> |
|
| 695 | - <data key="d5"/> |
|
| 696 | - <data key="d6"> |
|
| 697 | - <y:ShapeNode> |
|
| 698 | - <y:Geometry height="176.0" width="112.0" x="538.1550698020919" y="643.9819121172475"/> |
|
| 699 | - <y:Fill color="#000000" transparent="false"/> |
|
| 700 | - <y:BorderStyle color="#000000" type="line" width="1.0"/> |
|
| 701 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="54.0" y="86.0"> |
|
| 702 | - <y:LabelModel> |
|
| 703 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 704 | - </y:LabelModel> |
|
| 705 | - <y:ModelParameter> |
|
| 706 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 707 | - </y:ModelParameter> |
|
| 708 | - </y:NodeLabel> |
|
| 709 | - <y:Shape type="roundrectangle"/> |
|
| 710 | - </y:ShapeNode> |
|
| 711 | - </data> |
|
| 712 | - </node> |
|
| 713 | - <node id="n25"> |
|
| 714 | - <data key="d5"/> |
|
| 715 | - <data key="d6"> |
|
| 716 | - <y:ShapeNode> |
|
| 717 | - <y:Geometry height="1.0" width="1.0" x="560.847032814188" y="727.23696"/> |
|
| 718 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 719 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 720 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="-1.5" y="-1.5"> |
|
| 721 | - <y:LabelModel> |
|
| 722 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 723 | - </y:LabelModel> |
|
| 724 | - <y:ModelParameter> |
|
| 725 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 726 | - </y:ModelParameter> |
|
| 727 | - </y:NodeLabel> |
|
| 728 | - <y:Shape type="rectangle"/> |
|
| 729 | - </y:ShapeNode> |
|
| 730 | - </data> |
|
| 731 | - </node> |
|
| 732 | - <node id="n26"> |
|
| 733 | - <data key="d5"/> |
|
| 734 | - <data key="d6"> |
|
| 735 | - <y:ShapeNode> |
|
| 736 | - <y:Geometry height="21.0" width="21.0" x="583.6550698020917" y="793.9819121172474"/> |
|
| 737 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 738 | - <y:BorderStyle color="#FFFFFF" type="line" width="1.0"/> |
|
| 739 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="8.5" y="8.5"> |
|
| 740 | - <y:LabelModel> |
|
| 741 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 742 | - </y:LabelModel> |
|
| 743 | - <y:ModelParameter> |
|
| 744 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 745 | - </y:ModelParameter> |
|
| 746 | - </y:NodeLabel> |
|
| 747 | - <y:Shape type="ellipse"/> |
|
| 748 | - </y:ShapeNode> |
|
| 749 | - </data> |
|
| 750 | - </node> |
|
| 751 | - <node id="n27"> |
|
| 752 | - <data key="d5"/> |
|
| 753 | - <data key="d6"> |
|
| 754 | - <y:ShapeNode> |
|
| 755 | - <y:Geometry height="10.0" width="87.11275724687482" x="553.3710698020918" y="708.7355121172473"/> |
|
| 756 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 757 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 758 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="internal" modelPosition="c" textColor="#000000" visible="true" width="4.0" x="41.55637862343747" y="3.0"/> |
|
| 759 | - <y:Shape type="rectangle"/> |
|
| 760 | - </y:ShapeNode> |
|
| 761 | - </data> |
|
| 762 | - </node> |
|
| 763 | - <node id="n28"> |
|
| 764 | - <data key="d5"/> |
|
| 765 | - <data key="d6"> |
|
| 766 | - <y:ShapeNode> |
|
| 767 | - <y:Geometry height="137.0" width="101.0" x="543.6550698020919" y="649.9819121172475"/> |
|
| 768 | - <y:Fill color="#FFFFFF" transparent="false"/> |
|
| 769 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 770 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#000000" visible="true" width="4.0" x="48.5" y="66.5"> |
|
| 771 | - <y:LabelModel> |
|
| 772 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 773 | - </y:LabelModel> |
|
| 774 | - <y:ModelParameter> |
|
| 775 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 776 | - </y:ModelParameter> |
|
| 777 | - </y:NodeLabel> |
|
| 778 | - <y:Shape type="rectangle"/> |
|
| 779 | - </y:ShapeNode> |
|
| 780 | - </data> |
|
| 781 | - </node> |
|
| 782 | - <node id="n29"> |
|
| 783 | - <data key="d5"/> |
|
| 784 | - <data key="d6"> |
|
| 785 | - <y:ShapeNode> |
|
| 786 | - <y:Geometry height="58.736959999999954" width="82.94893019790811" x="552.6806047031379" y="668.5"/> |
|
| 787 | - <y:Fill hasColor="false" transparent="false"/> |
|
| 788 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 789 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="17.96875" modelName="custom" textColor="#000000" visible="true" width="58.99609375" x="11.976418223954056" y="20.384104999999977">RaceMap<y:LabelModel> |
|
| 790 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 791 | - </y:LabelModel> |
|
| 792 | - <y:ModelParameter> |
|
| 793 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 794 | - </y:ModelParameter> |
|
| 795 | - </y:NodeLabel> |
|
| 796 | - <y:Shape type="rectangle"/> |
|
| 797 | - </y:ShapeNode> |
|
| 798 | - </data> |
|
| 799 | - </node> |
|
| 800 | - <node id="n30"> |
|
| 801 | - <data key="d5"/> |
|
| 802 | - <data key="d6"> |
|
| 803 | - <y:ShapeNode> |
|
| 804 | - <y:Geometry height="116.01600000000008" width="101.0" x="694.5" y="450.0"/> |
|
| 805 | - <y:Fill color="#99CCFF" transparent="false"/> |
|
| 806 | - <y:BorderStyle hasColor="false" type="line" width="1.0"/> |
|
| 807 | - <y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" hasText="false" height="4.0" modelName="custom" textColor="#3366FF" visible="true" width="4.0" x="48.5" y="56.00800000000004"> |
|
| 808 | - <y:LabelModel> |
|
| 809 | - <y:SmartNodeLabelModel distance="4.0"/> |
|
| 810 | - </y:LabelModel> |
|
| 811 | - <y:ModelParameter> |
|
| 812 | - <y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/> |
|
| 813 | - </y:ModelParameter> |
|
| 814 | - </y:NodeLabel> |
|
| 815 | - <y:Shape type="rectangle"/> |
|
| 816 | - </y:ShapeNode> |
|
| 817 | - </data> |
|
| 818 | - </node> |
|
| 819 | - <edge id="e0" source="n1::n0" target="n6::n0"> |
|
| 820 | - <data key="d9"/> |
|
| 821 | - <data key="d10"> |
|
| 822 | - <y:PolyLineEdge> |
|
| 823 | - <y:Path sx="0.0" sy="0.0" tx="-2.2737367544323206E-13" ty="-1.1368683772161603E-13"/> |
|
| 824 | - <y:LineStyle color="#000000" type="line" width="1.0"/> |
|
| 825 | - <y:Arrows source="none" target="standard"/> |
|
| 826 | - <y:BendStyle smoothed="false"/> |
|
| 827 | - </y:PolyLineEdge> |
|
| 828 | - </data> |
|
| 829 | - </edge> |
|
| 830 | - <edge id="e1" source="n11" target="n11"> |
|
| 831 | - <data key="d9"/> |
|
| 832 | - <data key="d10"> |
|
| 833 | - <y:QuadCurveEdge straightness="0.1"> |
|
| 834 | - <y:Path sx="0.0" sy="0.0" tx="-24.576000000000022" ty="4.096000000000004"> |
|
| 835 | - <y:Point x="769.2903630120961" y="530.3766478827524"/> |
|
| 836 | - <y:Point x="751.6775630120961" y="494.3318478827525"/> |
|
| 837 | - <y:Point x="791.4087630120961" y="488.5974478827525"/> |
|
| 838 | - <y:Point x="784.8551630120961" y="461.15424788275243"/> |
|
| 839 | - <y:Point x="741.8471630120961" y="467.7078478827525"/> |
|
| 840 | - <y:Point x="745.9431630120961" y="501.7046478827524"/> |
|
| 841 | - <y:Point x="719.319163012096" y="513.5830478827525"/> |
|
| 842 | - <y:Point x="753.3159630120961" y="510.3062478827525"/> |
|
| 843 | - <y:Point x="746.3527630120961" y="475.88560000000007"/> |
|
| 844 | - <y:Point x="796.7335630120962" y="468.11744788275246"/> |
|
| 845 | - <y:Point x="783.6263630120961" y="515.6310478827525"/> |
|
| 846 | - </y:Path> |
|
| 847 | - <y:LineStyle color="#000000" type="line" width="1.0"/> |
|
| 848 | - <y:Arrows source="none" target="none"/> |
|
| 849 | - </y:QuadCurveEdge> |
|
| 850 | - </data> |
|
| 851 | - </edge> |
|
| 852 | - <edge id="e2" source="n6::n0" target="n15"> |
|
| 853 | - <data key="d9"/> |
|
| 854 | - <data key="d10"> |
|
| 855 | - <y:PolyLineEdge> |
|
| 856 | - <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
|
| 857 | - <y:LineStyle color="#000000" type="line" width="1.0"/> |
|
| 858 | - <y:Arrows source="none" target="standard"/> |
|
| 859 | - <y:BendStyle smoothed="false"/> |
|
| 860 | - </y:PolyLineEdge> |
|
| 861 | - </data> |
|
| 862 | - </edge> |
|
| 863 | - <edge id="e3" source="n13" target="n24"> |
|
| 864 | - <data key="d9"/> |
|
| 865 | - <data key="d10"> |
|
| 866 | - <y:PolyLineEdge> |
|
| 867 | - <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/> |
|
| 868 | - <y:LineStyle color="#000000" type="line" width="1.0"/> |
|
| 869 | - <y:Arrows source="none" target="standard"/> |
|
| 870 | - <y:BendStyle smoothed="false"/> |
|
| 871 | - </y:PolyLineEdge> |
|
| 872 | - </data> |
|
| 873 | - </edge> |
|
| 874 | - <edge id="e4" source="n15" target="n13"> |
|
| 875 | - <data key="d9"/> |
|
| 876 | - <data key="d10"> |
|
| 877 | - <y:PolyLineEdge> |
|
| 878 | - <y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"> |
|
| 879 | - <y:Point x="745.0" y="628.1400000000001"/> |
|
| 880 | - <y:Point x="442.0" y="628.1400000000001"/> |
|
| 881 | - </y:Path> |
|
| 882 | - <y:LineStyle color="#000000" type="line" width="1.0"/> |
|
| 883 | - <y:Arrows source="none" target="standard"/> |
|
| 884 | - <y:BendStyle smoothed="false"/> |
|
| 885 | - </y:PolyLineEdge> |
|
| 886 | - </data> |
|
| 887 | - </edge> |
|
| 888 | - </graph> |
|
| 889 | - <data key="d0"> |
|
| 890 | - <y:Resources/> |
|
| 891 | - </data> |
|
| 892 | -</graphml> |
wiki/racelog-tracking/app-spec/training.png
| ... | ... | Binary files /dev/null and b/wiki/racelog-tracking/app-spec/training.png differ |
wiki/racelog-tracking/app-spec/training.svg
| ... | ... | @@ -1,123 +0,0 @@ |
| 1 | -<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill-opacity="1" color-rendering="auto" color-interpolation="auto" stroke="black" text-rendering="auto" stroke-linecap="square" width="466" stroke-miterlimit="10" stroke-opacity="1" shape-rendering="auto" fill="black" stroke-dasharray="none" font-weight="normal" stroke-width="1" height="425" font-family="'Dialog'" font-style="normal" stroke-linejoin="miter" font-size="12" stroke-dashoffset="0" image-rendering="auto"> |
|
| 2 | - <!--Generated by ySVG 2.5--> |
|
| 3 | - <defs id="genericDefs"/> |
|
| 4 | - <g> |
|
| 5 | - <defs id="defs1"> |
|
| 6 | - <clipPath clipPathUnits="userSpaceOnUse" id="clipPath1"> |
|
| 7 | - <path d="M0 0 L466 0 L466 425 L0 425 L0 0 Z"/> |
|
| 8 | - </clipPath> |
|
| 9 | - <clipPath clipPathUnits="userSpaceOnUse" id="clipPath2"> |
|
| 10 | - <path d="M356 410 L822 410 L822 835 L356 835 L356 410 Z"/> |
|
| 11 | - </clipPath> |
|
| 12 | - </defs> |
|
| 13 | - <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="translate(-356,-410)" stroke="white"> |
|
| 14 | - <rect x="356" width="466" height="425" y="410" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 15 | - </g> |
|
| 16 | - <g font-size="10" stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45"> |
|
| 17 | - <text x="421.9159" xml:space="preserve" y="717.2155" clip-path="url(#clipPath2)" stroke="none">Statistics</text> |
|
| 18 | - </g> |
|
| 19 | - <g shape-rendering="geometricPrecision" text-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)"> |
|
| 20 | - <rect x="386" y="444" clip-path="url(#clipPath2)" width="112" rx="4" ry="4" height="176" stroke="none"/> |
|
| 21 | - <rect stroke-linecap="butt" x="386" y="444" clip-path="url(#clipPath2)" fill="none" width="112" rx="4" ry="4" height="176" stroke-miterlimit="1.45"/> |
|
| 22 | - <rect x="391.5" y="450" clip-path="url(#clipPath2)" fill="white" width="101" height="137" stroke="none"/> |
|
| 23 | - </g> |
|
| 24 | - <g stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" stroke="white" stroke-miterlimit="1.45"> |
|
| 25 | - <circle fill="none" r="10.5" clip-path="url(#clipPath2)" cx="442" cy="604.5"/> |
|
| 26 | - <rect x="398.5" y="475" clip-path="url(#clipPath2)" fill="none" width="87" height="16" stroke="black"/> |
|
| 27 | - <rect x="398.5" y="496.5" clip-path="url(#clipPath2)" fill="none" width="87" height="16" stroke="black"/> |
|
| 28 | - </g> |
|
| 29 | - <g fill="silver" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="silver"> |
|
| 30 | - <rect x="417.5" width="49" height="16" y="520" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 31 | - </g> |
|
| 32 | - <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" font-family="sans-serif" transform="matrix(1,0,0,1,-356,-410)" stroke-linecap="butt"> |
|
| 33 | - <text x="425.708" xml:space="preserve" y="532.1543" clip-path="url(#clipPath2)" stroke="none">Login</text> |
|
| 34 | - <rect fill="none" x="417.5" width="49" height="16" y="520" clip-path="url(#clipPath2)"/> |
|
| 35 | - <text x="409.3608" xml:space="preserve" font-size="10" y="562.088" clip-path="url(#clipPath2)" stroke="none">already have</text> |
|
| 36 | - <text x="411.6045" xml:space="preserve" font-size="10" y="573.7286" clip-path="url(#clipPath2)" stroke="none">an account?</text> |
|
| 37 | - <rect stroke-linecap="square" x="537.5" y="444" clip-path="url(#clipPath2)" width="112" rx="4" ry="4" height="176" stroke="none" stroke-miterlimit="10"/> |
|
| 38 | - <rect x="537.5" y="444" clip-path="url(#clipPath2)" fill="none" width="112" rx="4" ry="4" height="176"/> |
|
| 39 | - </g> |
|
| 40 | - <g fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="white"> |
|
| 41 | - <rect x="543" width="101" height="137" y="450" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 42 | - <circle stroke-linecap="butt" clip-path="url(#clipPath2)" fill="none" r="10.5" cx="593.5" cy="604.5" stroke-miterlimit="1.45"/> |
|
| 43 | - <rect x="698.9988" y="568.7536" clip-path="url(#clipPath2)" fill="silver" width="92.33" height="15.4634" stroke="none"/> |
|
| 44 | - </g> |
|
| 45 | - <g font-size="10" stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45"> |
|
| 46 | - <text x="710.6569" xml:space="preserve" y="579.9472" clip-path="url(#clipPath2)" stroke="none">Stop Tracking</text> |
|
| 47 | - <rect fill="none" x="698.9988" width="92.33" height="15.4634" y="568.7536" clip-path="url(#clipPath2)"/> |
|
| 48 | - <path fill="none" d="M498.0358 532 L529.5173 532" clip-path="url(#clipPath2)"/> |
|
| 49 | - <path d="M537.5173 532 L525.5173 527 L528.5173 532 L525.5173 537 Z" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 50 | - </g> |
|
| 51 | - <g fill="silver" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="silver"> |
|
| 52 | - <rect x="546.8288" width="93" height="16" y="475.2032" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 53 | - </g> |
|
| 54 | - <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke-linecap="butt"> |
|
| 55 | - <rect fill="none" x="546.8288" width="93" height="16" y="475.2032" clip-path="url(#clipPath2)"/> |
|
| 56 | - <text x="567.4633" font-size="10" y="486.6651" clip-path="url(#clipPath2)" font-family="sans-serif" stroke="none" xml:space="preserve">Start Tracking</text> |
|
| 57 | - </g> |
|
| 58 | - <g fill="red" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="red"> |
|
| 59 | - <circle r="7.4634" clip-path="url(#clipPath2)" cx="554.9621" cy="483.2032" stroke="none"/> |
|
| 60 | - <rect x="547.4988" y="515.096" clip-path="url(#clipPath2)" fill="silver" width="92.33" height="15.4634" stroke="none"/> |
|
| 61 | - </g> |
|
| 62 | - <g font-size="10" stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45"> |
|
| 63 | - <text x="568.3781" xml:space="preserve" y="526.2896" clip-path="url(#clipPath2)" stroke="none">My Tracks</text> |
|
| 64 | - <rect fill="none" x="547.4988" width="92.33" height="15.4634" y="515.096" clip-path="url(#clipPath2)"/> |
|
| 65 | - </g> |
|
| 66 | - <g fill="rgb(153,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="rgb(153,204,255)"> |
|
| 67 | - <rect x="694.5" width="101" height="116.016" y="450" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 68 | - </g> |
|
| 69 | - <g font-size="10" stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45"> |
|
| 70 | - <text x="417.6227" xml:space="preserve" y="719.5582" clip-path="url(#clipPath2)" stroke="none">Statistics:</text> |
|
| 71 | - <text x="437.3957" xml:space="preserve" y="731.1989" clip-path="url(#clipPath2)" stroke="none">...</text> |
|
| 72 | - <text x="437.3957" xml:space="preserve" y="742.8395" clip-path="url(#clipPath2)" stroke="none">...</text> |
|
| 73 | - </g> |
|
| 74 | - <g shape-rendering="geometricPrecision" text-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)"> |
|
| 75 | - <rect x="386" y="644" clip-path="url(#clipPath2)" width="112" rx="4" ry="4" height="176" stroke="none"/> |
|
| 76 | - <rect stroke-linecap="butt" x="386" y="644" clip-path="url(#clipPath2)" fill="none" width="112" rx="4" ry="4" height="176" stroke-miterlimit="1.45"/> |
|
| 77 | - <rect x="391.5" y="650" clip-path="url(#clipPath2)" fill="white" width="101" height="137" stroke="none"/> |
|
| 78 | - <rect x="689" y="444" clip-path="url(#clipPath2)" width="112" rx="4" ry="4" height="176" stroke="none"/> |
|
| 79 | - <rect stroke-linecap="butt" x="689" y="444" clip-path="url(#clipPath2)" fill="none" width="112" rx="4" ry="4" height="176" stroke-miterlimit="1.45"/> |
|
| 80 | - <rect x="694.5" y="450" clip-path="url(#clipPath2)" fill="white" width="101" height="137" stroke="none"/> |
|
| 81 | - </g> |
|
| 82 | - <g stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" stroke="white" stroke-miterlimit="1.45"> |
|
| 83 | - <circle fill="none" r="10.5" clip-path="url(#clipPath2)" cx="745" cy="604.5"/> |
|
| 84 | - <circle fill="none" r="10.5" clip-path="url(#clipPath2)" cx="442" cy="804.5"/> |
|
| 85 | - </g> |
|
| 86 | - <g fill="silver" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="silver"> |
|
| 87 | - <rect x="395.9988" width="92.33" height="15.4634" y="768.7536" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 88 | - </g> |
|
| 89 | - <g font-size="10" stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45"> |
|
| 90 | - <text x="422.4616" xml:space="preserve" y="779.9472" clip-path="url(#clipPath2)" stroke="none">Analyze</text> |
|
| 91 | - <rect fill="none" x="395.9988" width="92.33" height="15.4634" y="768.7536" clip-path="url(#clipPath2)"/> |
|
| 92 | - </g> |
|
| 93 | - <g fill="rgb(153,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="rgb(153,204,255)"> |
|
| 94 | - <rect x="395.9988" width="92.33" height="46" y="654.9152" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 95 | - <rect x="698.9988" y="568.7536" clip-path="url(#clipPath2)" fill="silver" width="92.33" height="15.4634" stroke="none"/> |
|
| 96 | - </g> |
|
| 97 | - <g font-size="10" stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" text-rendering="geometricPrecision" font-family="sans-serif" shape-rendering="geometricPrecision" stroke-miterlimit="1.45"> |
|
| 98 | - <text x="710.6569" xml:space="preserve" y="579.9472" clip-path="url(#clipPath2)" stroke="none">Stop Tracking</text> |
|
| 99 | - <rect fill="none" x="698.9988" width="92.33" height="15.4634" y="568.7536" clip-path="url(#clipPath2)"/> |
|
| 100 | - </g> |
|
| 101 | - <g shape-rendering="geometricPrecision" text-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)"> |
|
| 102 | - <rect x="538.1551" y="643.9819" clip-path="url(#clipPath2)" width="112" rx="4" ry="4" height="176" stroke="none"/> |
|
| 103 | - <rect stroke-linecap="butt" x="538.1551" y="643.9819" clip-path="url(#clipPath2)" fill="none" width="112" rx="4" ry="4" height="176" stroke-miterlimit="1.45"/> |
|
| 104 | - </g> |
|
| 105 | - <g stroke-linecap="butt" transform="matrix(1,0,0,1,-356,-410)" fill="white" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" stroke="white" stroke-miterlimit="1.45"> |
|
| 106 | - <circle fill="none" r="10.5" clip-path="url(#clipPath2)" cx="594.1551" cy="804.4819"/> |
|
| 107 | - <rect stroke-linecap="square" x="543.6551" y="649.9819" clip-path="url(#clipPath2)" width="101" height="137" stroke="none" stroke-miterlimit="10"/> |
|
| 108 | - <text x="566.657" y="702.0228" clip-path="url(#clipPath2)" fill="black" font-family="sans-serif" stroke="none" xml:space="preserve">RaceMap</text> |
|
| 109 | - </g> |
|
| 110 | - <g fill="rgb(153,204,255)" text-rendering="geometricPrecision" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke="rgb(153,204,255)"> |
|
| 111 | - <rect x="694.5" width="101" height="116.016" y="450" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 112 | - </g> |
|
| 113 | - <g text-rendering="geometricPrecision" stroke-miterlimit="1.45" shape-rendering="geometricPrecision" transform="matrix(1,0,0,1,-356,-410)" stroke-linecap="butt"> |
|
| 114 | - <path fill="none" d="M741.6555 538.0825 L750.1211 535.7219 L754.4901 534.2157 L758.0123 532.3696 L760.6878 530.1837 L762.5166 527.658 L763.4987 524.7924 L763.634 521.5869 L762.9227 518.0416 L761.3646 514.1565 L759.6033 510.552 L758.025 506.7101 L757.2532 503.2944 L757.2878 500.305 L758.1288 497.7418 L759.7761 495.6048 L762.2299 493.8941 L765.4901 492.6096 L769.5566 491.7513 L773.5297 491.1779 L781.1674 489.2771 L784.01 487.8688 L786.2017 486.1552 L787.7425 484.1363 L788.6324 481.8122 L788.8715 479.1827 L788.4596 476.248 L787.8043 473.5037 L786.8107 470.6554 L785.3044 468.2851 L783.2856 466.393 L780.754 464.9789 L777.7099 464.0429 L774.1531 463.585 L765.5016 464.1034 L761.2008 464.7587 L756.6936 465.689 L752.8488 467.0051 L749.6664 468.7072 L747.1464 470.7952 L745.2888 473.2691 L744.0936 476.129 L743.5608 479.3747 L743.6904 483.0064 L744.1 486.4061 L744.1576 493.4333 L743.5384 496.4803 L742.4872 499.2163 L741.004 501.6413 L739.0888 503.7552 L733.9624 507.0499 L731.2999 508.2378 L727.0145 510.4842 L726.1505 511.2877 L726.139 511.8781 L728.6734 512.4196 L734.6177 512.1085 L738.0174 511.7808 L741.554 511.1932 L744.5147 510.1676 L746.8994 508.7041 L748.7079 506.8026 L749.9406 504.4631 L750.5972 501.6857 L750.6779 498.4703 L750.1825 494.817 L749.4862 491.3749 L749.1061 487.6899 L749.5323 484.3798 L750.765 481.4445 L752.804 478.884 L755.6494 476.6983 L759.3013 474.8874 L763.7595 473.4513 L769.0242 472.39 L774.0623 471.6131 L779.2837 471.1279 L783.6123 471.4201 L787.0482 472.4897 L789.5912 474.3366 L791.2414 476.961 L791.9988 480.3628 L791.8635 484.542 L790.8353 489.4986 L789.5246 494.2499 L787.5691 499.4771 L784.6516 504.4682 L780.7723 509.2231 L775.931 513.7418 L770.1278 518.0244 L763.3627 522.0707 L755.6356 525.881 L746.9467 529.4551 L702.116 546.3511" clip-path="url(#clipPath2)"/> |
|
| 115 | - <path fill="none" d="M649.5358 532 L681.0173 532" clip-path="url(#clipPath2)"/> |
|
| 116 | - <path d="M689.0173 532 L677.0173 527 L680.0173 532 L677.0173 537 Z" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 117 | - <path fill="none" d="M745 620.0188 L745 628.14 L442 628.14 L442 635.9878" clip-path="url(#clipPath2)"/> |
|
| 118 | - <path d="M442 643.9878 L447 631.9878 L442 634.9878 L437 631.9878 Z" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 119 | - <path fill="none" d="M497.9809 731.9933 L530.1317 731.9895" clip-path="url(#clipPath2)"/> |
|
| 120 | - <path d="M538.1317 731.9886 L526.1312 726.99 L529.1317 731.9896 L526.1323 736.9899 Z" clip-path="url(#clipPath2)" stroke="none"/> |
|
| 121 | - </g> |
|
| 122 | - </g> |
|
| 123 | -</svg> |
wiki/uploading-media-content.md
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +# Uploading Media Content |
|
| 2 | + |
|
| 3 | +We have a place to store media content such as images and videos, e.g., created at sailing events. This place is the directory `/var/www/static` on our web server, and it is publicly reachable by HTTP at `http://static.sapsailing.com/...`. For example, the file `/var/www/static/images/splash-screen-compressed-02.png` is reachable via the URL `http://static.sapsailing.com/images/splash-screen-compressed-02.png`. |
|
| 4 | + |
|
| 5 | +Uploading content to this directory works best using the "secure copy" (SCP) protocol, sometimes also referred to as SFTP. It is basically sending files through a secure shell (SSH) connection. Command line aficionados will know the "scp" command and its options and will understand what it means to have a public/private key pair that allows them to log on to the `sapsailing.com` web server. |
|
| 6 | + |
|
| 7 | +For those who don't, we can easily create username/password accounts on the web server which spares the technical tidbits of public/private key pair creation and registration. A good graphical tool (for the less command line-oriented folks) is WinSCP which can be downloaded at [http://winscp.net](http://winscp.net/eng/download.php#download2). |
|
| 8 | + |
|
| 9 | +For those in an open Internet environment (outside the SAP VPN or logged on to the "SAP Internet" WiFi) the hostname to use is `sapsailing.com` and the port number is `22`. Inside the SAP VPN we have to use a little trick to make the connection. Use `10.18.22.50` as the hostname and `12349` as the port number. |
|
| 10 | + |
|
| 11 | +## Quick Introduction to the Use of WinSCP |
|
| 12 | + |
|
| 13 | +After downloading WinSCP from the URL shown above, run the installer. When it asks you about your preferred user interface style you may want to choose "Explorer" which may be most familiar for Windows users. |
|
| 14 | + |
|
| 15 | + |
|
| 16 | + |
|
| 17 | +Then, launch WinSCP. It welcomes you with a login screen: |
|
| 18 | + |
|
| 19 | + |
|
| 20 | + |
|
| 21 | +Enter the hostname and port number as described above, depending on your network environment (within SAP network or outside of SAP network). Add your user name and password, then click the "Login" button. You should then see an Explorer-like window that shows the web server's file system hierarchy. |
|
| 22 | + |
|
| 23 | +Navigate to /var/www/static: |
|
| 24 | + |
|
| 25 | + |
|
| 26 | + |
|
| 27 | +You can create new folders using the "Create Folder" button from the toolbar: |
|
| 28 | + |
|
| 29 | + |
|
| 30 | + |
|
| 31 | +Files can be uploaded from your local disk by dragging and dropping them from a local Windows Explorer running on your desktop onto the content panel in the right part of the WinSCP window. |
|
| ... | ... | \ No newline at end of file |