📄 Update filenames in file headers
[Sone.git] / src / main / java / net / pterodactylus / sone / freenet / wot / IdentityChangeDetector.kt
1 /*
2  * Sone - IdentityChangeDetector.kt - Copyright Â© 2013–2019 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.sone.freenet.wot
19
20 /**
21  * Detects changes between two lists of [Identity]s. The detector can find
22  * added and removed identities, and for identities that exist in both list
23  * their contexts and properties are checked for added, removed, or (in case of
24  * properties) changed values.
25  */
26 class IdentityChangeDetector(oldIdentities: Collection<Identity>) {
27
28         private val oldIdentities: Map<String, Identity> = oldIdentities.associateBy { it.id }
29         var onNewIdentity: IdentityProcessor? = null
30         var onRemovedIdentity: IdentityProcessor? = null
31         var onChangedIdentity: IdentityProcessor? = null
32         var onUnchangedIdentity: IdentityProcessor? = null
33
34         fun detectChanges(newIdentities: Collection<Identity>) {
35                 onRemovedIdentity.notify(oldIdentities.values.filter { it !in newIdentities })
36                 onNewIdentity.notify(newIdentities.filter { it !in oldIdentities.values })
37                 onChangedIdentity.notify(newIdentities.filter { it.id in oldIdentities }.filter { identityHasChanged(oldIdentities[it.id]!!, it) })
38                 onUnchangedIdentity.notify(newIdentities.filter { it.id in oldIdentities }.filterNot { identityHasChanged(oldIdentities[it.id]!!, it) })
39         }
40
41         private fun identityHasChanged(oldIdentity: Identity, newIdentity: Identity?) =
42                         identityHasNewContexts(oldIdentity, newIdentity!!)
43                                         || identityHasRemovedContexts(oldIdentity, newIdentity)
44                                         || identityHasNewProperties(oldIdentity, newIdentity)
45                                         || identityHasRemovedProperties(oldIdentity, newIdentity)
46                                         || identityHasChangedProperties(oldIdentity, newIdentity)
47
48         private fun identityHasNewContexts(oldIdentity: Identity, newIdentity: Identity) =
49                         newIdentity.contexts.any { it !in oldIdentity.contexts }
50
51         private fun identityHasRemovedContexts(oldIdentity: Identity, newIdentity: Identity) =
52                         oldIdentity.contexts.any { it !in newIdentity.contexts }
53
54         private fun identityHasNewProperties(oldIdentity: Identity, newIdentity: Identity) =
55                         newIdentity.properties.keys.any { it !in oldIdentity.properties }
56
57         private fun identityHasRemovedProperties(oldIdentity: Identity, newIdentity: Identity) =
58                         oldIdentity.properties.keys.any { it !in newIdentity.properties }
59
60         private fun identityHasChangedProperties(oldIdentity: Identity, newIdentity: Identity) =
61                         oldIdentity.properties.entries.any { newIdentity.properties[it.key] != it.value }
62
63 }
64
65 typealias IdentityProcessor = (Identity) -> Unit
66
67 private fun IdentityProcessor?.notify(identities: Iterable<Identity>) =
68                 this?.let { identities.forEach(this::invoke) }