Implement more robust way of determining binary directory

Fixes #34
This commit is contained in:
Daniel Wolf 2018-03-29 22:31:53 +02:00
parent f39f9c71d9
commit e4a661513e
3 changed files with 95 additions and 8 deletions

View File

@ -2,6 +2,7 @@
## Unreleased
* Fixed [issue #34](https://github.com/DanielSWolf/rhubarb-lip-sync/issues/34): Generic error message in Rhubarb for Spine
* More helpful error dialogs for internal errors in Rhubarb Lip Sync for Spine
* Internal errors in Rhubarb Lip Sync for Spine are logged to stderr

View File

@ -10,8 +10,6 @@ import java.io.*
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.time.Duration
import java.util.concurrent.Callable
class RhubarbTask(
@ -106,12 +104,8 @@ class RhubarbTask(
}
private val guiBinDirectory: Path by lazy {
var path: String = ClassLoader.getSystemClassLoader().getResource(".")!!.path
if (path.length >= 3 && path[2] == ':') {
// Workaround for https://stackoverflow.com/questions/9834776/java-nio-file-path-issue
path = path.substring(1)
}
return@lazy Paths.get(path)
val path = urlToPath(getLocation(RhubarbTask::class.java))
return@lazy if (Files.isDirectory(path)) path.parent else path
}
private val rhubarbBinFilePath: Path by lazy {

View File

@ -0,0 +1,92 @@
package com.rhubarb_lip_sync.rhubarb_for_spine
import java.io.FileInputStream
import java.net.MalformedURLException
import java.net.URISyntaxException
import java.net.URL
import org.apache.commons.lang3.SystemUtils.IS_OS_WINDOWS
import java.nio.file.Path
import java.nio.file.Paths
// The following code is adapted from https://stackoverflow.com/a/12733172/52041
/**
* Gets the base location of the given class.
*
* If the class is directly on the file system (e.g.,
* "/path/to/my/package/MyClass.class") then it will return the base directory
* (e.g., "file:/path/to").
*
* If the class is within a JAR file (e.g.,
* "/path/to/my-jar.jar!/my/package/MyClass.class") then it will return the
* path to the JAR (e.g., "file:/path/to/my-jar.jar").
*
* @param c The class whose location is desired.
*/
fun getLocation(c: Class<*>): URL {
// Try the easy way first
try {
val codeSourceLocation = c.protectionDomain.codeSource.location
if (codeSourceLocation != null) return codeSourceLocation
} catch (e: SecurityException) {
// Cannot access protection domain
} catch (e: NullPointerException) {
// Protection domain or code source is null
}
// The easy way failed, so we try the hard way. We ask for the class
// itself as a resource, then strip the class's path from the URL string,
// leaving the base path.
// Get the class's raw resource path
val classResource = c.getResource(c.simpleName + ".class")
?: throw Exception("Cannot find class resource.")
val url = classResource.toString()
val suffix = c.canonicalName.replace('.', '/') + ".class"
if (!url.endsWith(suffix)) throw Exception("Malformed URL.")
// strip the class's path from the URL string
val base = url.substring(0, url.length - suffix.length)
var path = base
// remove the "jar:" prefix and "!/" suffix, if present
if (path.startsWith("jar:")) path = path.substring(4, path.length - 2)
return URL(path)
}
/**
* Converts the given URL to its corresponding [Path].
*
* @param url The URL to convert.
* @return A file path suitable for use with e.g. [FileInputStream]
*/
fun urlToPath(url: URL): Path {
var pathString = url.toString()
if (pathString.startsWith("jar:")) {
// Remove "jar:" prefix and "!/" suffix
val index = pathString.indexOf("!/")
pathString = pathString.substring(4, index)
}
try {
if (IS_OS_WINDOWS && pathString.matches("file:[A-Za-z]:.*".toRegex())) {
pathString = "file:/" + pathString.substring(5)
}
return Paths.get(URL(pathString).toURI())
} catch (e: MalformedURLException) {
// URL is not completely well-formed.
} catch (e: URISyntaxException) {
// URL is not completely well-formed.
}
if (pathString.startsWith("file:")) {
// Pass through the URL as-is, minus "file:" prefix
pathString = pathString.substring(5)
return Paths.get(pathString)
}
throw IllegalArgumentException("Invalid URL: $url")
}