Skip to content

Instantly share code, notes, and snippets.

@nobeans
Created August 18, 2011 16:30
Show Gist options
  • Save nobeans/1154465 to your computer and use it in GitHub Desktop.
Save nobeans/1154465 to your computer and use it in GitHub Desktop.
class PathResolver {
String relativePath(String path1, String path2) {
[path1, path2].each{ checkPath(it) }
def (from, to) = cutCommonBaseDir(path1, path2)
(baseDir(from).replaceAll('[^/]+/', '../') ?: './') + to
}
private checkPath(path) {
if (!path.startsWith('/')) {
throw new IllegalArgumentException("It's not an absolute path: $path")
}
if (!path) {
throw new IllegalArgumentException("Empty: $path")
}
if (path.find($/[\?*:|<>]/$)) {
throw new IllegalArgumentException("Invalid chars: $path")
}
}
private cutCommonBaseDir(path1, path2) {
def commonBaseDir = commonBaseDir(path1, path2)
[path1, path2].collect{ it.replaceFirst(commonBaseDir, '') }
}
private String commonBaseDir(path1, path2) {
def result = []
def splitPathToken = { baseDir(it).split('/') }
def tokens1 = splitPathToken(path1)
def tokens2 = splitPathToken(path2)
for (int i : 0..<[tokens1, tokens2]*.size().min()) {
if (tokens1[i] != tokens2[i]) break
result << tokens1[i]
}
result << ''
result.join('/')
}
private String baseDir(path) {
path.replaceFirst('[^/]*$', '')
}
}
@Grab('org.spockframework:spock-core:0.5-groovy-1.8')
@GrabExclude('org.codehaus.groovy:groovy-all')
@GrabConfig(systemClassLoader=true)
import spock.lang.*
class PathResolverSpec extends Specification {
def resolver = new PathResolver()
def "relativePathで正常に相対パスに変換する"() {
when:
def actual = resolver.relativePath(path1, path2)
then:
actual == expected
where:
path1 | path2 | expected
"/aaa/bbb/from.txt" | "/aaa/bbb/to.txt" | "./to.txt" // 例1) 同じディレクトリにあるファイル
"/aaa/bbb/from.txt" | "/aaa/to.txt" | "../to.txt" // 例2) 親ディレクトリにあるファイル
"/aaa/bbb/from.txt" | "/aaa/bbb/ccc/to.txt" | "./ccc/to.txt" // 例3) 子ディレクトリにあるファイル
"/aaa/bbb/from.txt" | "/aaa/ccc/to.txt" | "../ccc/to.txt" // 例4) 親の子の子にあるファイル
"/aaa/bbb/from.txt" | "/ddd/ccc/to.txt" | "../../ddd/ccc/to.txt" // 例5) ルート越え
"/aaa/bbb/" | "/aaa/ddd/to" | "../ddd/to" // 例6) ディレクトリからファイル
"/aaa/bbb/from" | "/aaa/ccc/" | "../ccc/" // 例7) ファイルからディレクトリ
"/aaa/bbb/" | "/aaa/ccc/" | "../ccc/" // 例8) ディレクトリからディレクトリ (サンプルのexpectedが誤っていた)
"/aaa/bbb/ccc.txt" | "/aaa/bbb/ccc.txt" | "./ccc.txt" // 例9) 同じパス
"/aaa/bbb/ccc/from.txt" | "/aaa/xxx/ccc/to.txt" | "../../xxx/ccc/to.txt" // 例?) 途中のパスが違う引っかけ
}
def "relativePathの入力値エラーの場合はIllegalArgumentExceptionをスローする"() {
when:
def actual = resolver.relativePath(path1, path2)
then:
thrown(IllegalArgumentException)
where:
path1 | path2
"" | "" // 例10) 空
"/aaa/g*" | "/bbb/to.txt" // 例11) 使用不可能な文字
"aaa" | "bbb" // 例??) 絶対パスではない
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment