Skip to content

Commit

Permalink
Merge pull request HaxeFoundation#2678 from kasoki/enhance_path
Browse files Browse the repository at this point in the history
Add ability to normalize and join paths to haxe.io.Path
  • Loading branch information
Simn committed Feb 27, 2014
2 parents e04d6f5 + 48360fe commit 54ebe3c
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 29 deletions.
107 changes: 78 additions & 29 deletions std/haxe/io/Path.hx
Original file line number Diff line number Diff line change
Expand Up @@ -24,52 +24,52 @@ package haxe.io;
/**
This class provides a convenient way of working with paths. It supports the
common path formats:
- directory1/directory2/filename.extension
- directory1\directory2\filename.excention
**/
class Path {

/**
The directory.
This is the leading part of the path that is not part of the file name
and the extension.
Does not end with a `/` or `\` separator.
If the path has no directory, the value is null.
**/
public var dir : String;

/**
The file name.
This is the part of the part between the directory and the extension.
If there is no file name, e.g. for ".htaccess" or "/dir/", the value
is the empty String "".
**/
public var file : String;

/**
The file extension.
It is separated from the file name by a dot. This dot is not part of
the extension.
If the path has no extension, the value is null.
**/
public var ext : String;

/**
True if the last directory separator is a backslash, false otherwise.
**/
public var backslash : Bool;

/**
Creates a new Path instance by parsing `path`.
Path information can be retrieved by accessing the dir, file and ext
properties.
**/
Expand Down Expand Up @@ -97,11 +97,11 @@ class Path {

/**
Returns a String representation of `this` path.
If `this.backslash` is true, backslash is used as directory separator,
otherwise slash is used. This only affects the separator between
`this.dir` and `this.file`.
If `this.directory` or `this.extension` is null, their representation
is the empty String "".
**/
Expand All @@ -111,7 +111,7 @@ class Path {

/**
Returns the String representation of `path` without the file extension.
If `path` is null, the result is unspecified.
**/
public static function withoutExtension( path : String ) {
Expand All @@ -122,7 +122,7 @@ class Path {

/**
Returns the String representation of `path` without the directory.
If `path` is null, the result is unspecified.
**/
public static function withoutDirectory( path ) {
Expand All @@ -133,9 +133,9 @@ class Path {

/**
Returns the directory of `path`.
If the directory is null, the empty String "" is returned.
If `path` is null, the result is unspecified.
**/
public static function directory( path ) {
Expand All @@ -147,9 +147,9 @@ class Path {

/**
Returns the extension of `path`.
If the extension is null, the empty String "" is returned.
If `path` is null, the result is unspecified.
**/
public static function extension( path ) {
Expand All @@ -161,9 +161,9 @@ class Path {

/**
Returns a String representation of `path` where the extension is `ext`.
If `path` has no extension, `ext` is added as extension.
If `path` or `ext` are null, the result is unspecified.
**/
public static function withExtension( path, ext ) {
Expand All @@ -172,16 +172,65 @@ class Path {
return s.toString();
}

/**
Join two paths together and normalize them
e.g. 'assets/maps' and '../textures/img.png' = 'assets/textures/img.png'
**/
public static function join( path1 : String, path2 : String ) : String {
path1 = Path.addTrailingSlash(path1);

return Path.normalize(path1 + path2);
}

/**
Normalize a given `path` (e.g. make '/usr/local/../lib' to '/usr/lib')
**/
public static function normalize( path : String) : String {
var slash = '/';

if( path == null || path == slash ) {
return slash;
}

var prependSlash = (path.charAt(0) == slash ||
path.charAt(0) == '.');
var target = [];
var src;
var parts;
var token;

src = path.split(slash);

for( i in 0...src.length ) {
token = src[i];

if(token == '..') {
target.pop();
} else if(token != '' && token != '.') {
target.push(token);
}
}

var regex = ~/[\/]{2,}/g;

var tmp = target.join(slash);

var result = regex.replace(tmp, slash);

return (prependSlash ? slash : '') + result;
}

/**
Adds a trailing slash to `path`, if it does not have one already.
If the last slash in `path` is a backslash, a backslash is appended to
`path`.
If the last slash in `path` is a slash, or if no slash is found, a slash
is appended to `path`. In particular, this applies to the empty String
"".
If `path` is null, the result is unspecified.
**/
public static function addTrailingSlash( path : String ) : String {
Expand All @@ -197,15 +246,15 @@ class Path {
else path;
}
}

/**
Removes trailing slashes from `path`.
If `path` does not end with a `/` or `\`, `path` is returned unchanged.
Otherwise the substring of `path` excluding the trailing slashes or
backslashes is returned.
If `path` is null, the result is unspecified.
**/
@:require(haxe_ver >= 3.01)
Expand Down
9 changes: 9 additions & 0 deletions tests/unit/unitstd/haxe/io/Path.unit.hx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,15 @@ haxe.io.Path.withExtension(path2, "foo") == "/dir1/dir.with.dots\\file.foo";
haxe.io.Path.withExtension(path3, "foo") == ".foo";
haxe.io.Path.withExtension(path4, "foo") == "/dir/.foo";

// normalize
haxe.io.Path.normalize("dir1/dir2/../dir3") == "dir1/dir3";
haxe.io.Path.normalize("/dir1/dir2/../../test.foo") == "/test.foo";
haxe.io.Path.normalize("dir1/dir2/dir3/dir4/../../../dir5") == "dir1/dir5";

// join
haxe.io.Path.join("dir1/dir2", "dir3/dir4") == "dir1/dir2/dir3/dir4";
haxe.io.Path.join("dir1/dir2/bad_dir/", "../dir3/dir4") == "dir1/dir2/dir3/dir4";

// addTrailingSlash
haxe.io.Path.addTrailingSlash("") == "/";
haxe.io.Path.addTrailingSlash("a") == "a/";
Expand Down

0 comments on commit 54ebe3c

Please sign in to comment.