From 83610c20bbf3549e9d8b8a5cd74c73592c673acd Mon Sep 17 00:00:00 2001 From: Fredrik Teschke Date: Thu, 24 Apr 2014 01:12:55 +0200 Subject: [PATCH] simplified derivation of device mappings from mapping events --- .../test/DeviceMappingFinderTest.java | 68 ----------- .../sap/sailing/domain/racelog/RaceLog.java | 1 + .../racelog/tracking/DeviceMapping.java | 3 +- .../racelog/tracking/DeviceMappingEvent.java | 3 + .../analyzing/impl/DeviceMappingFinder.java | 115 ++---------------- .../gwt/ui/server/SailingServiceImpl.java | 2 + 6 files changed, 17 insertions(+), 175 deletions(-) diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/tracking/analyzing/test/DeviceMappingFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/tracking/analyzing/test/DeviceMappingFinderTest.java index 98a7b374f3a..9a561e23d91 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/tracking/analyzing/test/DeviceMappingFinderTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/tracking/analyzing/test/DeviceMappingFinderTest.java @@ -3,9 +3,6 @@ package com.sap.sailing.domain.racelog.tracking.analyzing.test; import static org.junit.Assert.assertEquals; import java.io.Serializable; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.List; import java.util.UUID; @@ -14,9 +11,7 @@ import junit.framework.Assert; import org.junit.Test; import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.impl.CompetitorImpl; -import com.sap.sailing.domain.base.impl.MarkImpl; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.domain.racelog.RaceLogEvent; @@ -29,7 +24,6 @@ import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifie public class DeviceMappingFinderTest extends AbstractRaceLogTrackingTest { private final Competitor competitor = new CompetitorImpl("comp", "Comp", null, null, null); private final Competitor competitor2 = new CompetitorImpl("comp2", "Comp2", null, null, null); - private final Mark mark = new MarkImpl("mark"); private final DeviceIdentifier device = new SmartphoneImeiIdentifier("imei"); private int time = 0; @@ -53,13 +47,6 @@ public class DeviceMappingFinderTest extends AbstractRaceLogTrackingTest { return mapping.getId(); } - private Serializable addMapping(RaceLogEventAuthor author, DeviceIdentifier device, Long from, Long to, Mark item) { - RaceLogEvent mapping = factory.createDeviceMarkMappingEvent(t(), author, t(), UUID.randomUUID(), device, item, 0, - t(from), t(to)); - log.add(mapping); - return mapping.getId(); - } - private void closeMapping(RaceLogEventAuthor author, DeviceIdentifier device, Serializable mappingId, long millis) { RaceLogEvent mapping = factory.createCloseOpenEndedDeviceMappingEvent(t(), author, t(), UUID.randomUUID(), 0, mappingId, new MillisecondsTimePoint(millis)); @@ -75,30 +62,9 @@ public class DeviceMappingFinderTest extends AbstractRaceLogTrackingTest { addMapping(author, device, 50L, 90L, competitor); Assert.assertEquals(2, getMappings().size()); - //are mappings merged? (mark mapping should not "distract") - addMapping(author, device, 5L, 100L, competitor); - addMapping(author, device, 15L, 90L, mark); - Assert.assertEquals(1, getMappings().size()); - - //overlap with same device -> should be merged - addMapping(author, device, 30L, 101L, competitor); - Assert.assertEquals(1, getMappings().size()); - //non-overlap with new device addMapping(author, new SmartphoneImeiIdentifier("imei2"), 110L, 150L, competitor); - Assert.assertEquals(2, getMappings().size()); - - //different device at start with overlap - addMapping(author, new SmartphoneImeiIdentifier("imei3"), 0L, 50L, competitor); Assert.assertEquals(3, getMappings().size()); - - //different device in middle -> split - addMapping(author, new SmartphoneImeiIdentifier("imei3"), 60L, 70L, competitor); - Assert.assertEquals(5, getMappings().size()); - - //different device at end with overlap - addMapping(author, new SmartphoneImeiIdentifier("imei4"), 140L, 160L, competitor); - Assert.assertEquals(6, getMappings().size()); } @Test @@ -126,39 +92,5 @@ public class DeviceMappingFinderTest extends AbstractRaceLogTrackingTest { //open-ended at end, not closed addMapping(author, device, 30L, null, competitor); assertEquals(3, getMappings().size()); - - //open-ended at beginning, eats others up - addMapping(author, device, -10L, null, competitor); - assertEquals(1, getMappings().size()); - } - - @Test - public void doesLaterMappingThatIncludesOtherOverwrite() throws ParseException { - DeviceIdentifier cde = new SmartphoneImeiIdentifier("cde"); - DeviceIdentifier abc = new SmartphoneImeiIdentifier("abc"); - DateFormat df = new SimpleDateFormat("mm:ss"); - long m1600 = df.parse("16:00").getTime(); - long m1550 = df.parse("15:50").getTime(); - long m1615 = df.parse("16:15").getTime(); - long m1610 = df.parse("16:10").getTime(); - addMapping(author, abc, m1600, m1610, competitor); - addMapping(author, cde, m1550, m1615, competitor); - - assertEquals(1, getMappings().size()); - } - - @Test - public void doesLaterMappingThatLiesWithinBreakOtherIntoPieces() throws ParseException { - DeviceIdentifier cde = new SmartphoneImeiIdentifier("cde"); - DeviceIdentifier abc = new SmartphoneImeiIdentifier("abc"); - DateFormat df = new SimpleDateFormat("mm:ss"); - long m1600 = df.parse("16:00").getTime(); - long m1550 = df.parse("15:50").getTime(); - long m1615 = df.parse("16:15").getTime(); - long m1610 = df.parse("16:10").getTime(); - addMapping(author, cde, m1550, m1615, competitor); - addMapping(author, abc, m1600, m1610, competitor); - - assertEquals(3, getMappings().size()); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/RaceLog.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/RaceLog.java index ae8e148a372..374ced6b736 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/RaceLog.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/RaceLog.java @@ -104,6 +104,7 @@ public interface RaceLog extends Track, WithID { /** * Search for the event by its {@link RaceLogEvent#getId() id}. + * Caller needs to hold the read lock. */ RaceLogEvent getEventById(Serializable id); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMapping.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMapping.java index 9815f35af01..3e1b2429ac2 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMapping.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMapping.java @@ -6,9 +6,10 @@ import java.util.List; import com.sap.sailing.domain.base.Timed; import com.sap.sailing.domain.common.TimeRange; import com.sap.sailing.domain.common.WithID; +import com.sap.sailing.domain.racelog.tracking.analyzing.impl.DeviceMappingFinder; /** - * Refer to the documentation of {@link DeviceMappingEvent} for details on how have-open ranges are resolved. + * @see DeviceMappingFinder {@code DeviceMappings} are derived from {@code DeviceMappingEvents}. * @author Fredrik Teschke */ public interface DeviceMapping extends Timed { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMappingEvent.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMappingEvent.java index bbf829d5182..853712e28f1 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMappingEvent.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/DeviceMappingEvent.java @@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.WithID; import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.Revokable; +import com.sap.sailing.domain.racelog.tracking.analyzing.impl.DefinedMarkFinder; /** * Event type for mapping {@link DeviceIdentifier devices} to {@code items} ({@link Competitor competitors} or {@link Mark marks}). @@ -15,6 +16,8 @@ import com.sap.sailing.domain.racelog.Revokable; *

* If one end of the time range enclosed by {@link #getFrom()} and {@link #getTo()} is open, this can be closed * by a {@link CloseOpenEndedDeviceMappingEvent}. + * + * @see DefinedMarkFinder for rules by which {@link DeviceMapping}s are derived from these events. * @author Fredrik Teschke */ public interface DeviceMappingEvent extends RaceLogEvent, Revokable { diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/analyzing/impl/DeviceMappingFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/analyzing/impl/DeviceMappingFinder.java index a1e47c2b9e7..80b5b6c6d61 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/analyzing/impl/DeviceMappingFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/racelog/tracking/analyzing/impl/DeviceMappingFinder.java @@ -14,7 +14,6 @@ import com.sap.sailing.domain.common.impl.TimeRangeImpl; import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLogEvent; import com.sap.sailing.domain.racelog.analyzing.impl.RaceLogAnalyzer; -import com.sap.sailing.domain.racelog.impl.RaceLogEventComparator; import com.sap.sailing.domain.racelog.tracking.CloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.racelog.tracking.DeviceIdentifier; import com.sap.sailing.domain.racelog.tracking.DeviceMapping; @@ -24,25 +23,15 @@ import com.sap.sailing.domain.racelog.tracking.impl.DeviceMappingImpl; /** * Extracts the {@link DeviceMapping}s with the appropriate {@link TimeRange}s from the {@link RaceLog}. * Based on the {@link DeviceMappingEvent}s found in the {@code RaceLog} for the tracked {@code items} - * ({@code Competitor}s and {@code Marks}), the actual mappings are created by removing conflicting - * overlaps of these mappings by the rules defined below. + * ({@code Competitor}s and {@code Marks}), the actual mappings are created. *

* Mappings can be defined with an open-ended time range (e.g. track this from now on), which should then be * closed by a corresponding {@link CloseOpenEndedDeviceMappingEvent}. *

- * The events in the {@code RaceLog} are ordered by their priority through the {@link RaceLogEventComparator}. - * Therefore, the rules for resolving conflicts are applied by inspecting the events in order of increasing - * priority one by one. Each inspected {@code DeviceMappingEvent} is resolved against the set of mappings - * deducted so far from the events with lower priority. The resulting set of mappings from this conflict - * resolution is then used as the new mapping basis against which the next higher-priority event is resolved. - *

    - *
  1. Mappings that have no overlap in time are never in conflict.
  2. - *
  3. Mappings for different items are never in conflict.
  4. - *
  5. Mappings for both the same device and item with overlapping {@code TimeRanges} can be merged.
  6. - *
  7. Mappings for the same item, but different devices, are in conflict if they have overlapping {@code TimeRanges}. - * The higher-priority mapping of the two takes precedence, and overwrites the lower-priority mapping for the - * duration of the overlap.
  8. - *
+ * The process of removing conflicts between mappings to ensure that only one mapping was active for + * one tracked item has been removed. Instead, every {@code DeviceMappingEvent} now results directly in one + * {@code DeviceMapping}, the only changes being introduced by closing open time ranges in cases where an + * according {@code CloseOpenEndedDeviceMappingEvent} exists. */ public class DeviceMappingFinder extends RaceLogAnalyzer>>> { public DeviceMappingFinder(RaceLog raceLog) { @@ -53,24 +42,10 @@ public class DeviceMappingFinder extends RaceLogAnalyzer getMapping(DeviceMappingEvent event) { - return new DeviceMappingImpl(event.getMappedTo(), event.getDevice(), - new TimeRangeImpl(event.getFrom(), event.getTo()), event.getId()); - } - - protected DeviceMapping getMapping(DeviceIdentifier device, ItemT item, - TimePoint from, TimePoint to, List originalEventIds) { - return new DeviceMappingImpl(item, device, new TimeRangeImpl(from, to), originalEventIds); - } - protected DeviceMapping getMapping(DeviceIdentifier device, ItemT item, TimePoint from, TimePoint to, Serializable originalEventId) { - return getMapping(device, item, from, to, Collections.singletonList(originalEventId)); - } - - protected DeviceMapping getMapping(DeviceIdentifier device, ItemT item, - TimeRange range, List originalEventIds) { - return new DeviceMappingImpl(item, device, range, originalEventIds); + return new DeviceMappingImpl(item, device, new TimeRangeImpl(from, to), + Collections.singletonList(originalEventId)); } private List getItemSet(Map> map, T item) { @@ -81,72 +56,6 @@ public class DeviceMappingFinder extends RaceLogAnalyzer> getOverlapFreeMappings(List> initial, - DeviceMapping toBeAdded) { - TimeRange timeRange = toBeAdded.getTimeRange(); - ItemT item = toBeAdded.getMappedTo(); - DeviceIdentifier device = toBeAdded.getDevice(); - List> result = new ArrayList>(); - - List originalEventIds = new ArrayList(); - originalEventIds.addAll(toBeAdded.getOriginalRaceLogEventIds()); - - - for (DeviceMapping otherMapping : initial) { - TimeRange otherTimeRange = otherMapping.getTimeRange(); - - //time ranges intersect? further investigate - if (otherTimeRange.intersects(timeRange)) { - - //same device - if (otherMapping.getDevice().equals(device)) { - - //two open ranges that close each other - if ((otherTimeRange.openEnd() && timeRange.openBeginning() && otherTimeRange.from().before(timeRange.to())) || - (timeRange.openEnd() && otherTimeRange.openBeginning() && timeRange.from().before(otherTimeRange.to()))) { - //shouldn't happen here any more -> dealt with in combineOpenRanges - //timeRange = timeRange.intersection(otherTimeRange); - - //otherwise merge - } else { - timeRange = timeRange.union(otherTimeRange); - originalEventIds.addAll(otherMapping.getOriginalRaceLogEventIds()); - } - - //different device: higher prio overwrites other - } else { - if (otherTimeRange.liesWithin(timeRange)) { - //just ignore the other mapping, as it is completely overwritten by the new one - } else { - if (otherTimeRange.startsBefore(timeRange)) { - //add the part of the lower-prio mapping, that lies before the higher-prio mapping - result.add(getMapping(otherMapping.getDevice(), item, otherTimeRange.from(), timeRange.from().minus(1), - otherMapping.getOriginalRaceLogEventIds())); - } - if (otherTimeRange.endsAfter(timeRange)) { - //add the part of the lower-prio mapping, that lies after the higher-prio mapping - result.add(getMapping(otherMapping.getDevice(), item, timeRange.to().plus(1), otherTimeRange.to(), - otherMapping.getOriginalRaceLogEventIds())); - } - } - } - - //no intersection -> add both - } else { - result.add(otherMapping); - } - } - - //other mappings have been checked for conflicts, now add toBeAdded (time range may have grown through merges) - result.add(getMapping(device, item, timeRange, toBeAdded.getOriginalRaceLogEventIds())); - - return result; - } - @Override protected Map>> performAnalysis() { Map>> events = new HashMap>>(); @@ -159,20 +68,14 @@ public class DeviceMappingFinder extends RaceLogAnalyzer mappingEvent = (DeviceMappingEvent) e; getItemSet(events, mappingEvent.getMappedTo()).add(mappingEvent); } else if (e instanceof CloseOpenEndedDeviceMappingEvent) { + //a higher priority closing events for the same mapping event overwrites the lower priority one CloseOpenEndedDeviceMappingEvent closingEvent = (CloseOpenEndedDeviceMappingEvent) e; closingEvents.put(closingEvent.getDeviceMappingEventId(), closingEvent); } } for (ItemT item : events.keySet()) { - List> closedRanges = closeOpenRanges(events.get(item), item, closingEvents); - - List> iterativelyResolved = new ArrayList>(); - for (DeviceMapping mapping : closedRanges) { - iterativelyResolved = getOverlapFreeMappings(iterativelyResolved, mapping); - } - - mappings.put(item, iterativelyResolved); + mappings.put(item, closeOpenRanges(events.get(item), item, closingEvents)); } return mappings; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 63f1c41ab15..85fab77da28 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -4360,7 +4360,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S List eventIds) throws NotRevokableException { RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName); for (Serializable idToRevoke : eventIds) { + raceLog.lockForRead(); RaceLogEvent event = raceLog.getEventById(idToRevoke); + raceLog.unlockAfterRead(); if (event instanceof Revokable) { raceLog.revokeEvent(getService().getServerAuthor(), (Revokable) event); }