public static String simplifyPath(String pathname) {
        if (N.isNullOrEmpty(pathname)) {
            return ".";
        }

        pathname = pathname.replace('\\', '/');

        // split the path apart
        String[] components = pathSplitter.splitToArray(pathname);
        List<String> path = new ArrayList<>();

        // resolve ., .., and //
        for (String component : components) {
            if (component.length() == 0 || component.equals(".")) {
                continue;
            } else if (component.equals("..")) {
                if (path.size() > 0 && !path.get(path.size() - 1).equals("..")) {
                    path.remove(path.size() - 1);
                } else {
                    path.add("..");
                }
            } else {
                path.add(component);
            }
        }

        // put it back together
        String result = StringUtil.join(path, '/');

        if (pathname.charAt(0) == '/') {
            result = "/" + result;
        }

        while (result.startsWith("/../")) {
            result = result.substring(3);
        }

        if (result.equals("/..")) {
            result = "/";
        } else if ("".equals(result)) {
            result = ".";
        }

        return result;
    }
public static String simplifyPath ( String pathname ) { if ( N . isNullOrEmpty ( pathname ) ) { return "." ; } pathname = pathname . replace ( '\\' , '/' ) ; String [ ] components = pathSplitter . splitToArray ( pathname ) ; List < String > path = new ArrayList < > ( ) ; for ( String component : components ) { if ( component . length ( ) == 0 || component . equals ( "." ) ) { continue ; } else if ( component . equals ( ".." ) ) { if ( path . size ( ) > 0 && ! path . get ( path . size ( ) - 1 ) . equals ( ".." ) ) { path . remove ( path . size ( ) - 1 ) ; } else { path . add ( ".." ) ; } } else { path . add ( component ) ; } } String result = StringUtil . join ( path , '/' ) ; if ( pathname . charAt ( 0 ) == '/' ) { result = "/" + result ; } while ( result . startsWith ( "/../" ) ) { result = result . substring ( 3 ) ; } if ( result . equals ( "/.." ) ) { result = "/" ; } else if ( "" . equals ( result ) ) { result = "." ; } return result ; }
Note: copied from Google Guava under Apache License v2.

Returns the lexically cleaned form of the path name, <i>usually</i> (but
not always) equivalent to the original. The following heuristics are used:

<ul>
<li>empty string becomes .
<li>. stays as .
<li>fold out ./
<li>fold out ../ when possible
<li>collapse multiple slashes
<li>delete trailing slashes (unless the path is just "/")
</ul>

<p>These heuristics do not always match the behavior of the filesystem. In
particular, consider the path {@code a/../b}, which {@code simplifyPath}
will change to {@code b}. If {@code a} is a symlink to {@code x}, {@code
a/../b} may refer to a sibling of {@code x}, rather than the sibling of
{@code a} referred to by {@code b}.

@since 11.0