Merge branch 'release/0.9-rc1'
[Sone.git] / src / main / java / net / pterodactylus / sone / core / SoneModificationDetector.java
1 package net.pterodactylus.sone.core;
2
3 import static com.google.common.base.Optional.absent;
4 import static com.google.common.base.Optional.of;
5 import static com.google.common.base.Ticker.systemTicker;
6 import static java.util.concurrent.TimeUnit.NANOSECONDS;
7
8 import java.util.concurrent.atomic.AtomicInteger;
9
10 import net.pterodactylus.sone.data.Sone;
11
12 import com.google.common.annotations.VisibleForTesting;
13 import com.google.common.base.Optional;
14 import com.google.common.base.Ticker;
15
16 /**
17  * Class that detects {@link Sone} modifications (as per their {@link
18  * Sone#getFingerprint() fingerprints} and determines when a modified Sone may
19  * be inserted.
20  *
21  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
22  */
23 class SoneModificationDetector {
24
25         private final Ticker ticker;
26         private final LockableFingerprintProvider lockableFingerprintProvider;
27         private final AtomicInteger insertionDelay;
28         private Optional<Long> lastModificationTime;
29         private String originalFingerprint;
30         private String lastFingerprint;
31
32         SoneModificationDetector(LockableFingerprintProvider lockableFingerprintProvider, AtomicInteger insertionDelay) {
33                 this(systemTicker(), lockableFingerprintProvider, insertionDelay);
34         }
35
36         @VisibleForTesting
37         SoneModificationDetector(Ticker ticker, LockableFingerprintProvider lockableFingerprintProvider, AtomicInteger insertionDelay) {
38                 this.ticker = ticker;
39                 this.lockableFingerprintProvider = lockableFingerprintProvider;
40                 this.insertionDelay = insertionDelay;
41                 lastFingerprint = originalFingerprint;
42         }
43
44         public boolean isEligibleForInsert() {
45                 if (lockableFingerprintProvider.isLocked()) {
46                         lastModificationTime = absent();
47                         lastFingerprint = "";
48                         return false;
49                 }
50                 String fingerprint = lockableFingerprintProvider.getFingerprint();
51                 if (originalFingerprint.equals(fingerprint)) {
52                         lastModificationTime = absent();
53                         lastFingerprint = fingerprint;
54                         return false;
55                 }
56                 if (!lastFingerprint.equals(fingerprint)) {
57                         lastModificationTime = of(ticker.read());
58                         lastFingerprint = fingerprint;
59                         return false;
60                 }
61                 return insertionDelayHasPassed();
62         }
63
64         public String getOriginalFingerprint() {
65                 return originalFingerprint;
66         }
67
68         public void setFingerprint(String fingerprint) {
69                 originalFingerprint = fingerprint;
70                 lastFingerprint = originalFingerprint;
71                 lastModificationTime = absent();
72         }
73
74         private boolean insertionDelayHasPassed() {
75                 return NANOSECONDS.toSeconds(ticker.read() - lastModificationTime.get()) >= insertionDelay.get();
76         }
77
78         public boolean isModified() {
79                 return !lockableFingerprintProvider.getFingerprint().equals(originalFingerprint);
80         }
81
82         /**
83          * Provider for a fingerprint and the information if a {@link Sone} is locked. This
84          * prevents us from having to lug a Sone object around.
85          *
86          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
87          */
88         static interface LockableFingerprintProvider {
89
90                 boolean isLocked();
91                 String getFingerprint();
92
93         }
94
95 }