@@ -21,7 +21,7 @@ import org.readium.r2.shared.util.logging.WarningLogger
21
21
import org.readium.r2.shared.util.logging.log
22
22
23
23
/* *
24
- * Provides a precise location in a publication in a format that can be stored and shared.
24
+ * Represents a precise location in a publication in a format that can be stored and shared.
25
25
*
26
26
* There are many different use cases for locators:
27
27
* - getting back to the last position in a publication
@@ -30,7 +30,7 @@ import org.readium.r2.shared.util.logging.log
30
30
* - search results
31
31
* - human-readable (and shareable) reference in a publication
32
32
*
33
- * https://github.com/readium/ architecture/tree/master/ locators
33
+ * https://readium.org/ architecture/models/ locators/
34
34
*/
35
35
@Parcelize
36
36
data class Locator (
@@ -41,6 +41,45 @@ data class Locator(
41
41
val text : Text = Text ()
42
42
) : JSONable, Parcelable {
43
43
44
+ /* *
45
+ * Builder for a [Locator] object.
46
+ */
47
+ data class Builder (
48
+ var href : String ,
49
+ var type : String ,
50
+ var title : String? = null ,
51
+ var locations : Locations .Builder = Locations .Builder (),
52
+ var text : Text .Builder = Text .Builder ()
53
+ ) {
54
+ constructor (locator: Locator ): this (
55
+ href = locator.href,
56
+ type = locator.type,
57
+ title = locator.title,
58
+ locations = Locations .Builder (locator.locations),
59
+ text = Text .Builder (locator.text),
60
+ )
61
+
62
+ /* *
63
+ * Merges the given [locator], replacing any non null properties.
64
+ */
65
+ fun merge (locator : Locator ): Builder {
66
+ href = locator.href
67
+ type = locator.type
68
+ locator.title?.let { title = it }
69
+ locations.merge(locator.locations)
70
+ text.merge(locator.text)
71
+ return this
72
+ }
73
+
74
+ fun build (): Locator = Locator (
75
+ href = href,
76
+ type = type,
77
+ title = title,
78
+ locations = locations.build(),
79
+ text = text.build()
80
+ )
81
+ }
82
+
44
83
/* *
45
84
* One or more alternative expressions of the location.
46
85
* https://github.com/readium/architecture/tree/master/models/locators#the-location-object
@@ -61,6 +100,51 @@ data class Locator(
61
100
val otherLocations : @WriteWith<JSONParceler > Map <String , Any > = emptyMap()
62
101
) : JSONable, Parcelable {
63
102
103
+ /* *
104
+ * Builder for a [Locations] object.
105
+ */
106
+ data class Builder (
107
+ var fragments : MutableList <String > = mutableListOf(),
108
+ var progression : Double? = null ,
109
+ var position : Int? = null ,
110
+ var totalProgression : Double? = null ,
111
+ var otherLocations : MutableMap <String , Any > = mutableMapOf()
112
+ ) {
113
+ constructor (locations: Locations ) : this (
114
+ fragments = locations.fragments.toMutableList(),
115
+ progression = locations.progression,
116
+ position = locations.position,
117
+ totalProgression = locations.totalProgression,
118
+ otherLocations = locations.otherLocations.toMutableMap()
119
+ )
120
+
121
+ fun merge (locations : Locations ): Builder {
122
+ locations.progression?.let { progression = it }
123
+ locations.position?.let { position = it }
124
+ locations.totalProgression?.let { totalProgression = it }
125
+
126
+ if (locations.fragments.isNotEmpty()) {
127
+ fragments = (locations.fragments + fragments)
128
+ .distinct()
129
+ .toMutableList()
130
+ }
131
+
132
+ for ((k, v) in locations.otherLocations) {
133
+ otherLocations[k] = v
134
+ }
135
+
136
+ return this
137
+ }
138
+
139
+ fun build (): Locations = Locations (
140
+ fragments = fragments.toList(),
141
+ progression = progression,
142
+ position = position,
143
+ totalProgression = totalProgression,
144
+ otherLocations = otherLocations.toMap()
145
+ )
146
+ }
147
+
64
148
override fun toJSON () = JSONObject (otherLocations).apply {
65
149
putIfNotEmpty(" fragments" , fragments)
66
150
put(" progression" , progression)
@@ -123,6 +207,38 @@ data class Locator(
123
207
val after : String? = null
124
208
) : JSONable, Parcelable {
125
209
210
+ /* *
211
+ * Builder for a [Text] object.
212
+ */
213
+ data class Builder (
214
+ var before : String? = null ,
215
+ var highlight : String? = null ,
216
+ var after : String? = null
217
+ ) {
218
+ constructor (text: Text ) : this (
219
+ before = text.before,
220
+ highlight = text.highlight,
221
+ after = text.after,
222
+ )
223
+
224
+ fun merge (text : Text ): Builder {
225
+ // It doesn't make sense to merge partially a [Text] object, as the [before],
226
+ // [highlight] and [after] properties are related to each other.
227
+ if (text.before != null || text.highlight != null || text.after != null ) {
228
+ before = text.before
229
+ highlight = text.highlight
230
+ after = text.after
231
+ }
232
+ return this
233
+ }
234
+
235
+ fun build (): Text = Text (
236
+ before = before,
237
+ highlight = highlight,
238
+ after = after
239
+ )
240
+ }
241
+
126
242
override fun toJSON () = JSONObject ().apply {
127
243
put(" before" , before)
128
244
put(" highlight" , highlight)
@@ -136,11 +252,21 @@ data class Locator(
136
252
highlight = json?.optNullableString(" highlight" ),
137
253
after = json?.optNullableString(" after" )
138
254
)
139
-
140
255
}
141
-
142
256
}
143
257
258
+ /* *
259
+ * Makes a copy of the `Locator` after modifying its properties using a `Builder`.
260
+ *
261
+ * ```
262
+ * locator.copy {
263
+ * locations.progression = 0.5
264
+ * }
265
+ * ```
266
+ */
267
+ fun copy (build : Builder .() -> Unit ): Locator =
268
+ Builder (this ).apply (build).build()
269
+
144
270
/* *
145
271
* Shortcut to get a copy of the [Locator] with different [Locations] sub-properties.
146
272
*/
0 commit comments