8066943: (fs) Path.relativize() gives incorrect result for ".."
Reviewed-by: prappo, bpb
This commit is contained in:
parent
7b8ef7554e
commit
97fca016ab
jdk
src/java.base
test/java/nio/file/Path
@ -252,6 +252,21 @@ class UnixPath implements Path {
|
||||
return new UnixPath(getFileSystem(), new byte[0]);
|
||||
}
|
||||
|
||||
|
||||
// return true if this path has "." or ".."
|
||||
private boolean hasDotOrDotDot() {
|
||||
int n = getNameCount();
|
||||
for (int i=0; i<n; i++) {
|
||||
byte[] bytes = getName(i).path;
|
||||
if ((bytes.length == 1 && bytes[0] == '.'))
|
||||
return true;
|
||||
if ((bytes.length == 2 && bytes[0] == '.') && bytes[1] == '.') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnixFileSystem getFileSystem() {
|
||||
return fs;
|
||||
@ -405,80 +420,94 @@ class UnixPath implements Path {
|
||||
|
||||
@Override
|
||||
public UnixPath relativize(Path obj) {
|
||||
UnixPath other = toUnixPath(obj);
|
||||
if (other.equals(this))
|
||||
UnixPath child = toUnixPath(obj);
|
||||
if (child.equals(this))
|
||||
return emptyPath();
|
||||
|
||||
// can only relativize paths of the same type
|
||||
if (this.isAbsolute() != other.isAbsolute())
|
||||
if (this.isAbsolute() != child.isAbsolute())
|
||||
throw new IllegalArgumentException("'other' is different type of Path");
|
||||
|
||||
// this path is the empty path
|
||||
if (this.isEmpty())
|
||||
return other;
|
||||
return child;
|
||||
|
||||
int bn = this.getNameCount();
|
||||
int cn = other.getNameCount();
|
||||
UnixPath base = this;
|
||||
if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) {
|
||||
base = base.normalize();
|
||||
child = child.normalize();
|
||||
}
|
||||
|
||||
int baseCount = base.getNameCount();
|
||||
int childCount = child.getNameCount();
|
||||
|
||||
// skip matching names
|
||||
int n = (bn > cn) ? cn : bn;
|
||||
int n = Math.min(baseCount, childCount);
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
if (!this.getName(i).equals(other.getName(i)))
|
||||
if (!base.getName(i).equals(child.getName(i)))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
int dotdots = bn - i;
|
||||
if (i < cn) {
|
||||
// remaining name components in other
|
||||
UnixPath remainder = other.subpath(i, cn);
|
||||
if (dotdots == 0)
|
||||
return remainder;
|
||||
|
||||
// other is the empty path
|
||||
boolean isOtherEmpty = other.isEmpty();
|
||||
|
||||
// result is a "../" for each remaining name in base
|
||||
// followed by the remaining names in other. If the remainder is
|
||||
// the empty path then we don't add the final trailing slash.
|
||||
int len = dotdots*3 + remainder.path.length;
|
||||
if (isOtherEmpty) {
|
||||
assert remainder.isEmpty();
|
||||
len--;
|
||||
}
|
||||
byte[] result = new byte[len];
|
||||
int pos = 0;
|
||||
while (dotdots > 0) {
|
||||
result[pos++] = (byte)'.';
|
||||
result[pos++] = (byte)'.';
|
||||
if (isOtherEmpty) {
|
||||
if (dotdots > 1) result[pos++] = (byte)'/';
|
||||
} else {
|
||||
result[pos++] = (byte)'/';
|
||||
}
|
||||
dotdots--;
|
||||
}
|
||||
System.arraycopy(remainder.path, 0, result, pos, remainder.path.length);
|
||||
return new UnixPath(getFileSystem(), result);
|
||||
// remaining elements in child
|
||||
UnixPath childRemaining;
|
||||
boolean isChildEmpty;
|
||||
if (i == childCount) {
|
||||
childRemaining = emptyPath();
|
||||
isChildEmpty = true;
|
||||
} else {
|
||||
// no remaining names in other so result is simply a sequence of ".."
|
||||
byte[] result = new byte[dotdots*3 - 1];
|
||||
int pos = 0;
|
||||
while (dotdots > 0) {
|
||||
result[pos++] = (byte)'.';
|
||||
result[pos++] = (byte)'.';
|
||||
// no tailing slash at the end
|
||||
if (dotdots > 1)
|
||||
result[pos++] = (byte)'/';
|
||||
dotdots--;
|
||||
}
|
||||
return new UnixPath(getFileSystem(), result);
|
||||
childRemaining = child.subpath(i, childCount);
|
||||
isChildEmpty = childRemaining.isEmpty();
|
||||
}
|
||||
|
||||
// matched all of base
|
||||
if (i == baseCount) {
|
||||
return childRemaining;
|
||||
}
|
||||
|
||||
// the remainder of base cannot contain ".."
|
||||
UnixPath baseRemaining = base.subpath(i, baseCount);
|
||||
if (baseRemaining.hasDotOrDotDot()) {
|
||||
throw new IllegalArgumentException("Unable to compute relative "
|
||||
+ " path from " + this + " to " + obj);
|
||||
}
|
||||
if (baseRemaining.isEmpty())
|
||||
return childRemaining;
|
||||
|
||||
// number of ".." needed
|
||||
int dotdots = baseRemaining.getNameCount();
|
||||
if (dotdots == 0) {
|
||||
return childRemaining;
|
||||
}
|
||||
|
||||
// result is a "../" for each remaining name in base followed by the
|
||||
// remaining names in child. If the remainder is the empty path
|
||||
// then we don't add the final trailing slash.
|
||||
int len = dotdots*3 + childRemaining.path.length;
|
||||
if (isChildEmpty) {
|
||||
assert childRemaining.isEmpty();
|
||||
len--;
|
||||
}
|
||||
byte[] result = new byte[len];
|
||||
int pos = 0;
|
||||
while (dotdots > 0) {
|
||||
result[pos++] = (byte)'.';
|
||||
result[pos++] = (byte)'.';
|
||||
if (isChildEmpty) {
|
||||
if (dotdots > 1) result[pos++] = (byte)'/';
|
||||
} else {
|
||||
result[pos++] = (byte)'/';
|
||||
}
|
||||
dotdots--;
|
||||
}
|
||||
System.arraycopy(childRemaining.path,0, result, pos,
|
||||
childRemaining.path.length);
|
||||
return new UnixPath(getFileSystem(), result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path normalize() {
|
||||
public UnixPath normalize() {
|
||||
final int count = getNameCount();
|
||||
if (count == 0 || isEmpty())
|
||||
return this;
|
||||
|
@ -375,57 +375,108 @@ class WindowsPath implements Path {
|
||||
return (WindowsPath)path;
|
||||
}
|
||||
|
||||
// return true if this path has "." or ".."
|
||||
private boolean hasDotOrDotDot() {
|
||||
int n = getNameCount();
|
||||
for (int i=0; i<n; i++) {
|
||||
String name = elementAsString(i);
|
||||
if (name.length() == 1 && name.charAt(0) == '.')
|
||||
return true;
|
||||
if (name.length() == 2
|
||||
&& name.charAt(0) == '.' && name.charAt(1) == '.')
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindowsPath relativize(Path obj) {
|
||||
WindowsPath other = toWindowsPath(obj);
|
||||
if (this.equals(other))
|
||||
WindowsPath child = toWindowsPath(obj);
|
||||
if (this.equals(child))
|
||||
return emptyPath();
|
||||
|
||||
// can only relativize paths of the same type
|
||||
if (this.type != other.type)
|
||||
if (this.type != child.type)
|
||||
throw new IllegalArgumentException("'other' is different type of Path");
|
||||
|
||||
// can only relativize paths if root component matches
|
||||
if (!this.root.equalsIgnoreCase(other.root))
|
||||
if (!this.root.equalsIgnoreCase(child.root))
|
||||
throw new IllegalArgumentException("'other' has different root");
|
||||
|
||||
// this path is the empty path
|
||||
if (this.isEmpty())
|
||||
return other;
|
||||
return child;
|
||||
|
||||
int bn = this.getNameCount();
|
||||
int cn = other.getNameCount();
|
||||
|
||||
WindowsPath base = this;
|
||||
if (base.hasDotOrDotDot() || child.hasDotOrDotDot()) {
|
||||
base = base.normalize();
|
||||
child = child.normalize();
|
||||
}
|
||||
|
||||
int baseCount = base.getNameCount();
|
||||
int childCount = child.getNameCount();
|
||||
|
||||
// skip matching names
|
||||
int n = (bn > cn) ? cn : bn;
|
||||
int n = Math.min(baseCount, childCount);
|
||||
int i = 0;
|
||||
while (i < n) {
|
||||
if (!this.getName(i).equals(other.getName(i)))
|
||||
if (!base.getName(i).equals(child.getName(i)))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
|
||||
// append ..\ for remaining names in the base
|
||||
// remaining elements in child
|
||||
WindowsPath childRemaining;
|
||||
boolean isChildEmpty;
|
||||
if (i == childCount) {
|
||||
childRemaining = emptyPath();
|
||||
isChildEmpty = true;
|
||||
} else {
|
||||
childRemaining = child.subpath(i, childCount);
|
||||
isChildEmpty = childRemaining.isEmpty();
|
||||
}
|
||||
|
||||
// matched all of base
|
||||
if (i == baseCount) {
|
||||
return childRemaining;
|
||||
}
|
||||
|
||||
// the remainder of base cannot contain ".."
|
||||
WindowsPath baseRemaining = base.subpath(i, baseCount);
|
||||
if (baseRemaining.hasDotOrDotDot()) {
|
||||
throw new IllegalArgumentException("Unable to compute relative "
|
||||
+ " path from " + this + " to " + obj);
|
||||
}
|
||||
if (baseRemaining.isEmpty())
|
||||
return childRemaining;
|
||||
|
||||
// number of ".." needed
|
||||
int dotdots = baseRemaining.getNameCount();
|
||||
if (dotdots == 0) {
|
||||
return childRemaining;
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int j=i; j<bn; j++) {
|
||||
for (int j=0; j<dotdots; j++) {
|
||||
result.append("..\\");
|
||||
}
|
||||
|
||||
// append remaining names in child
|
||||
if (!other.isEmpty()) {
|
||||
for (int j=i; j<cn; j++) {
|
||||
result.append(other.getName(j).toString());
|
||||
if (!isChildEmpty) {
|
||||
for (int j=0; j<childRemaining.getNameCount(); j++) {
|
||||
result.append(childRemaining.getName(j).toString());
|
||||
result.append("\\");
|
||||
}
|
||||
}
|
||||
|
||||
// drop trailing slash in result
|
||||
// drop trailing slash
|
||||
result.setLength(result.length()-1);
|
||||
return createFromNormalizedPath(getFileSystem(), result.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path normalize() {
|
||||
public WindowsPath normalize() {
|
||||
final int count = getNameCount();
|
||||
if (count == 0 || isEmpty())
|
||||
return this;
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user