Skip to content

Commit 8c1bfeb

Browse files
authored
Fix File.toUrl and DirectoryContainer (#727)
1 parent 8bf84c9 commit 8c1bfeb

File tree

8 files changed

+37
-13
lines changed

8 files changed

+37
-13
lines changed

readium/lcp/src/main/java/org/readium/r2/lcp/license/container/LcplLicenseContainer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ internal class LcplLicenseContainer(private val licenseFile: File) : WritableLic
3131
try {
3232
licenseFile.writeBytes(license.toByteArray())
3333
} catch (e: Exception) {
34-
throw LcpException(LcpError.Container.WriteFailed(licenseFile.toUrl()))
34+
throw LcpException(LcpError.Container.WriteFailed(licenseFile.toUrl(isDirectory = false)))
3535
}
3636
}
3737
}

readium/shared/src/main/java/org/readium/r2/shared/util/Url.kt

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,12 +166,16 @@ public sealed class Url : Parcelable {
166166
* Relativizes the given [url] against this URL.
167167
*
168168
* For example:
169-
* this = "http://example.com/foo"
169+
* this = "http://example.com/foo/"
170170
* url = "http://example.com/foo/bar/baz"
171171
* result = "bar/baz"
172172
*/
173-
public open fun relativize(url: Url): Url =
174-
checkNotNull(toURI().relativize(url.toURI()).toUrl())
173+
public open fun relativize(url: Url): Url {
174+
// Unlike the regular JRE (used in unit tests), the Android implementation of URI doesn't
175+
// add "/" at the end of the base if it's missing. We might need to align the behaviors
176+
// at some point.
177+
return checkNotNull(toURI().relativize(url.toURI()).toUrl())
178+
}
175179

176180
/**
177181
* Normalizes the URL using a subset of the RFC-3986 rules.
@@ -365,8 +369,23 @@ public fun Url.Companion.fromLegacyHref(href: String): Url? =
365369
public fun Url.Companion.fromEpubHref(href: String): Url? =
366370
Url(href) ?: fromDecodedPath(href)
367371

368-
public fun File.toUrl(): AbsoluteUrl =
369-
checkNotNull(AbsoluteUrl(Uri.fromFile(this)))
372+
/**
373+
* Creates a URL pointing to this [File] which must denote an absolute path.
374+
*
375+
* @param isDirectory If the URL must end with a trailing slash because it points to a directory.
376+
*/
377+
public fun File.toUrl(isDirectory: Boolean): AbsoluteUrl {
378+
require(isAbsolute)
379+
380+
val uri = Uri.Builder().also {
381+
it.scheme("file")
382+
it.authority("")
383+
it.path(path)
384+
if (isDirectory) it.appendPath("")
385+
}.build()
386+
387+
return checkNotNull(AbsoluteUrl(uri))
388+
}
370389

371390
public fun Uri.toUrl(): Url? =
372391
Url(this)

readium/shared/src/main/java/org/readium/r2/shared/util/file/DirectoryContainer.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ public class DirectoryContainer(
3535
public companion object {
3636

3737
public suspend operator fun invoke(root: File): Try<DirectoryContainer, FileSystemError> {
38-
val rootUrl = root.toUrl()
38+
val rootUrl = root.toUrl(isDirectory = true)
3939
val entries =
4040
try {
4141
withContext(Dispatchers.IO) {
4242
root.walk()
4343
.filter { it.isFile }
44-
.map { rootUrl.relativize(it.toUrl()) }
44+
.map { rootUrl.relativize(it.toUrl(isDirectory = false)) }
4545
.toSet()
4646
}
4747
} catch (e: SecurityException) {

readium/shared/src/main/java/org/readium/r2/shared/util/file/FileResource.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public class FileResource(
5555
}
5656
)
5757

58-
override val sourceUrl: AbsoluteUrl = file.toUrl()
58+
override val sourceUrl: AbsoluteUrl = file.toUrl(isDirectory = false)
5959

6060
public override suspend fun properties(): Try<Resource.Properties, ReadError> {
6161
return Try.success(properties)

readium/shared/src/main/java/org/readium/r2/shared/util/zip/FileZipContainer.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ internal class FileZipContainer(
137137
}
138138
}
139139

140-
override val sourceUrl: AbsoluteUrl = file.toUrl()
140+
override val sourceUrl: AbsoluteUrl = file.toUrl(isDirectory = false)
141141

142142
override val entries: Set<Url> =
143143
tryOrLog { archive.entries().toList() }

readium/shared/src/main/java/org/readium/r2/shared/util/zip/StreamingZipArchiveProvider.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ internal class StreamingZipArchiveProvider {
9191
internal suspend fun openFile(file: File): Container<Resource> = withContext(Dispatchers.IO) {
9292
val fileChannel = FileChannelAdapter(file, "r")
9393
val channel = wrapBaseChannel(fileChannel)
94-
StreamingZipContainer(ZipFile(channel), file.toUrl())
94+
StreamingZipContainer(ZipFile(channel), file.toUrl(isDirectory = false))
9595
}
9696

9797
private fun wrapBaseChannel(channel: SeekableByteChannel): SeekableByteChannel {

readium/shared/src/test/java/org/readium/r2/shared/util/UrlTest.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ class UrlTest {
321321

322322
@Test
323323
fun fromFile() {
324-
assertEquals(AbsoluteUrl(Uri.parse("file:///tmp/test.txt")), File("/tmp/test.txt").toUrl())
324+
assertEquals(AbsoluteUrl(Uri.parse("file:///tmp/test.txt")), File("/tmp/test.txt").toUrl(isDirectory = false))
325325
}
326326

327327
@Test
@@ -332,6 +332,11 @@ class UrlTest {
332332
)
333333
}
334334

335+
@Test
336+
fun fromDirectory() {
337+
assertEquals(AbsoluteUrl(Uri.parse("file:///tmp/")), File("/tmp").toUrl(isDirectory = true))
338+
}
339+
335340
@Test
336341
fun fromURI() {
337342
assertEquals(RelativeUrl(Uri.parse("foo/bar")), URI("foo/bar").toUrl())

test-app/src/main/java/org/readium/r2/testapp/domain/Bookshelf.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class Bookshelf(
9999
retrieverResult: Try<PublicationRetriever.Result, ImportError>,
100100
) {
101101
retrieverResult
102-
.map { addBook(it.publication.toUrl(), it.format, it.coverUrl) }
102+
.map { addBook(it.publication.toUrl(isDirectory = false), it.format, it.coverUrl) }
103103
.onSuccess { channel.send(Event.ImportPublicationSuccess) }
104104
.onFailure { channel.send(Event.ImportPublicationError(it)) }
105105
}

0 commit comments

Comments
 (0)