Created
August 18, 2011 16:30
-
-
Save nobeans/1154465 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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('[^/]*$', '') | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@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