Skip to content

Commit 4a2506a

Browse files
committed
add resolve() distinct from canonical()
1 parent 3d1f66a commit 4a2506a

File tree

9 files changed

+372
-22
lines changed

9 files changed

+372
-22
lines changed

+stdlib/+fileio/canonical.m

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,23 @@
1717
end
1818

1919
if ~stdlib.fileio.is_absolute_path(c)
20-
% .toAbsolutePath() default is Documents/Matlab, which is probably not wanted.
21-
c = fullfile(pwd, c);
20+
if isfile(c) || isfolder(c)
21+
% workaround Java/Matlab limitations
22+
c = fullfile(pwd, c);
23+
else
24+
% for non-existing path, return normalized relative path
25+
% like C++ filesystem weakly_canonical()
26+
c = stdlib.fileio.normalize(c);
27+
return
28+
end
2229
end
2330

2431
% similar benchmark time as java method
2532
% REQUIRES path to exist, while java method does not.
26-
% abspath = builtin('_canonicalizepath', abspath);
33+
% c = builtin('_canonicalizepath', c);
2734

28-
c = string(java.io.File(c).getCanonicalPath());
35+
% https://docs.oracle.com/javase/9/docs/api/java/io/File.html#getCanonicalPath--
36+
37+
c = stdlib.fileio.posix(string(java.io.File(c).getCanonicalPath()));
2938

3039
end % function

+stdlib/+fileio/normalize.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
return
1010
end
1111

12-
n = string(java.io.File(stdlib.fileio.expanduser(n)).toPath().normalize());
12+
n = stdlib.fileio.posix(string(...
13+
java.io.File(stdlib.fileio.expanduser(n)).toPath().normalize()));
1314

1415
end

+stdlib/+fileio/resolve.m

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
function c = resolve(p)
2+
% distinct from canonical(), resolve() always returns absolute path
3+
% non-existant path is made absolute relative to pwd
4+
arguments
5+
p string {mustBeScalarOrEmpty}
6+
end
7+
8+
% have to expand ~ first (like C++ filesystem::path::absolute)
9+
c = stdlib.fileio.expanduser(p);
10+
11+
if isempty(c)
12+
return
13+
end
14+
15+
if ispc && startsWith(c, "\\")
16+
% UNC path is not canonicalized
17+
return
18+
end
19+
20+
if ~stdlib.fileio.is_absolute_path(c)
21+
% .toAbsolutePath() default is Documents/Matlab, which is probably not wanted.
22+
c = fullfile(pwd, c);
23+
end
24+
25+
% similar benchmark time as java method
26+
% REQUIRES path to exist, while java method does not.
27+
% c = builtin('_canonicalizepath', c);
28+
29+
% https://docs.oracle.com/javase/9/docs/api/java/io/File.html#getCanonicalPath--
30+
31+
c = stdlib.fileio.posix(string(java.io.File(c).getCanonicalPath()));
32+
33+
end % function

+stdlib/+test/TestFileImpure.m

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,12 @@ function test_makedir(tc)
2323

2424

2525
function test_samepath(tc)
26-
import stdlib.fileio.samepath
27-
tc.assumeTrue(usejava("jvm"), "Java required")
2826

29-
tc.verifyEmpty(samepath(string.empty, string.empty))
30-
tc.verifyTrue(samepath("", ""))
31-
tc.verifyFalse(samepath(tempname, tempname))
32-
tc.verifyTrue(samepath("~/b/..", "~/c/.."), "tilde path ..")
33-
tc.verifyTrue(samepath(".", "a/.."))
27+
tc.verifyEmpty(stdlib.samepath(string.empty, string.empty))
28+
tc.verifyTrue(stdlib.samepath("", ""))
29+
tc.verifyFalse(stdlib.samepath(tempname, tempname))
30+
tc.verifyTrue(stdlib.samepath("~/b/..", "~/c/.."), "tilde path ..")
31+
tc.verifyTrue(stdlib.samepath(".", fullfile(pwd, "a/..")))
3432
end
3533

3634

+stdlib/+test/TestFilePure.m

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,34 +93,76 @@ function test_absolute_path(tc)
9393

9494

9595
function test_canonical(tc)
96-
96+
import matlab.unittest.fixtures.TemporaryFolderFixture
97+
import matlab.unittest.fixtures.CurrentFolderFixture
9798
import stdlib.canonical
99+
98100
tc.assumeTrue(usejava("jvm"), "Java required")
99101

102+
td = tc.applyFixture(TemporaryFolderFixture).Folder;
103+
tc.applyFixture(CurrentFolderFixture(td))
104+
105+
% all non-existing files
100106
tc.verifyEmpty(canonical(string.empty))
101-
tc.verifyEqual(canonical(""), string(pwd))
107+
tc.verifyEqual(canonical(""), "")
102108

103109
pabs = canonical('2foo');
104-
pabs2 = canonical('4foo');
110+
tc.verifyTrue(startsWith(pabs, "2foo"))
111+
112+
par1 = canonical("../2foo");
113+
tc.verifyNotEmpty(par1)
114+
tc.verifyTrue(startsWith(par1, ".."))
115+
116+
pt1 = canonical("bar/../2foo");
117+
tc.verifyEqual(pt1, "2foo")
118+
119+
% test existing file
120+
r = stdlib.fileio.parent(mfilename('fullpath'));
121+
rp = fullfile(r, "..");
122+
tc.verifyEqual(canonical(rp), stdlib.fileio.parent(r))
123+
124+
end
125+
126+
127+
function test_resolve(tc)
128+
import matlab.unittest.fixtures.TemporaryFolderFixture
129+
import matlab.unittest.fixtures.CurrentFolderFixture
130+
import stdlib.resolve
131+
tc.assumeTrue(usejava("jvm"), "Java required")
132+
133+
td = tc.applyFixture(TemporaryFolderFixture).Folder;
134+
tc.applyFixture(CurrentFolderFixture(td))
135+
136+
% all non-existing files
137+
tc.verifyEmpty(resolve(string.empty))
138+
tc.verifyEqual(resolve(""), string(stdlib.fileio.posix(pwd)))
139+
140+
pabs = resolve('2foo');
141+
pabs2 = resolve('4foo');
105142
tc.verifyFalse(startsWith(pabs, "2"))
106143
tc.verifyTrue(strncmp(pabs, pabs2, 2))
107144

108-
par1 = canonical("../2foo");
145+
par1 = resolve("../2foo");
109146
tc.verifyNotEmpty(par1)
110147
tc.verifyFalse(contains(par1, ".."))
111148

112-
par2 = canonical("../4foo");
149+
par2 = resolve("../4foo");
113150
tc.verifyTrue(strncmp(par2, pabs2, 2))
114151

115-
pt1 = canonical("bar/../2foo");
152+
pt1 = resolve("bar/../2foo");
116153
tc.verifyNotEmpty(pt1)
117154
tc.verifyFalse(contains(pt1, ".."))
118155

119-
va = canonical("2foo");
120-
vb = canonical("4foo");
156+
va = resolve("2foo");
157+
vb = resolve("4foo");
121158
tc.verifyFalse(startsWith(va, "2"))
122159
tc.verifyTrue(strncmp(va, vb, 2))
123160

161+
% test existing file
162+
r = stdlib.fileio.parent(mfilename('fullpath'));
163+
rp = fullfile(r, "..");
164+
tc.verifyEqual(resolve(rp), stdlib.fileio.parent(r))
165+
124166
end
125167

126168

+stdlib/+test/TestWindowsCOM.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ function test_shortname(tc)
66

77
tc.assumeTrue(ispc, "Windows only")
88

9-
test_file = string(fullfile(getenv("LocalAppData"), 'Microsoft\WindowsApps\notepad.exe'));
9+
test_file = stdlib.fileio.posix(fullfile(getenv("LocalAppData"), "Microsoft/WindowsApps/notepad.exe"));
1010
tc.assumeTrue(isfile(test_file))
1111

1212
short = stdlib.fileio.windows_shortname(test_file);

+stdlib/canonical.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
function c = canonical(p)
22
%% canonical(p)
3-
% path need not exist, but canonical path is returned
3+
% If exists, canonical absolute path is returned
4+
% if path does not exist, normalized relative path is returned
45
%
56
% NOTE: some network file systems are not resolvable by Matlab Java
67
% subsystem, but are sometimes still valid--so return

+stdlib/resolve.m

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
function c = resolve(p)
2+
%% resolve(p)
3+
% path need not exist--absolute path will be relative to pwd if not exist
4+
% if path exists, same result as canonical()
5+
%
6+
% NOTE: some network file systems are not resolvable by Matlab Java
7+
% subsystem, but are sometimes still valid--so return
8+
% unmodified path if this occurs.
9+
%
10+
% This also resolves Windows short paths to full long paths.
11+
%
12+
%%% Inputs
13+
% * p: path to resolve
14+
%%% Outputs
15+
% * c: resolved path
16+
17+
arguments
18+
p string {mustBeScalarOrEmpty}
19+
end
20+
21+
c = stdlib.fileio.resolve(p);
22+
23+
end

0 commit comments

Comments
 (0)