Canonicalize the given path.  Removes all {@code .} and {@code ..} segments from the path.

@param path the relative or absolute possibly non-canonical path
@return the canonical path
174783
@SuppressWarnings("UnusedLabel") // for documentation
    public static String canonicalize(final String path) {
        final int length = path.length();
        // 0 - start
        // 1 - got one .
        // 2 - got two .
        // 3 - got /
        int state = 0;
        if (length == 0) {
            return path;
        }
        final char[] targetBuf = new char[length];
        // string segment end exclusive
        int e = length;
        // string cursor position
        int i = length;
        // buffer cursor position
        int a = length - 1;
        // number of segments to skip
        int skip = 0;
        loop:
        while (--i >= 0) {
            char c = path.charAt(i);
            outer:
            switch (c) {
                case '/': {
                    inner:
                    switch (state) {
                        case 0:
                            state = 3;
                            e = i;
                            break outer;
                        case 1:
                            state = 3;
                            e = i;
                            break outer;
                        case 2:
                            state = 3;
                            e = i;
                            skip++;
                            break outer;
                        case 3:
                            e = i;
                            break outer;
                        default:
                            throw new IllegalStateException();
                    }
                    // not reached!
                }
                case '.': {
                    inner:
                    switch (state) {
                        case 0:
                            state = 1;
                            break outer;
                        case 1:
                            state = 2;
                            break outer;
                        case 2:
                            break inner; // emit!
                        case 3:
                            state = 1;
                            break outer;
                        default:
                            throw new IllegalStateException();
                    }
                    // fall thru
                }
                default: {
                    final int newE = e > 0 ? path.lastIndexOf('/', e - 1) : -1;
                    final int segmentLength = e - newE - 1;
                    if (skip > 0) {
                        skip--;
                    } else {
                        if (state == 3) {
                            targetBuf[a--] = '/';
                        }
                        path.getChars(newE + 1, e, targetBuf, (a -= segmentLength) + 1);
                    }
                    state = 0;
                    i = newE + 1;
                    e = newE;
                    break;
                }
            }
        }
        if (state == 3) {
            targetBuf[a--] = '/';
        }
        return new String(targetBuf, a + 1, length - a - 1);
    }
378048
public static String parentPathString(String path) {
        String[] split = componentsFromPathString(path);
        if (split.length > 1) {
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < split.length - 1; i++) {
                if (i > 0) {
                    stringBuilder.append(SEPARATOR);
                }
                stringBuilder.append(split[i]);
            }
            return stringBuilder.toString();
        }
        return "";
    }
751158
@Nonnull
    public static String normalizeFolder(@Nullable String path) {
        if (path == null) {
            path = "";
        } else {
            path = normalizeFile(path, null);
        }
        if (!path.endsWith("/")) {
            path = path + "/";
        }
        return path;
    }
735445
protected function canonicalize($path)
    {
        $parts = array_filter(explode('/', $path));
        $canonicalized = array();

        foreach ($parts as $part) {
            if ('.' == $part) {
                continue;
            }

            if ('..' == $part) {
                array_pop($canonicalized);
            } else {
                $canonicalized[] = $part;
            }
        }

        return implode('/', $canonicalized);
    }
1088012
public static String normalizePath(String path, String separator) {
    return path.endsWith(separator) ? path : path + separator;
  }
390719
public static Path getRootPath(Path path) {
    if (path.isRoot()) {
      return path;
    }
    return getRootPath(path.getParent());
  }
861683
public ScanDestination getDestinationWithSubpath(String path) {
        if (isDiscarding()) {
            return discard();
        }
        if (path == null) {
            return new ScanDestination(_uri);
        }
        return new ScanDestination(UriBuilder.fromUri(_uri).path(path).build());
    }
791924
private static String normalize(final List<String> path, boolean absolute) {
        assert path != null : "path must be specified";

        // Remove unnecessary references to this dir
        if (path.contains(DIR_THIS)) {
            path.remove(DIR_THIS);
            normalize(path, absolute);
        }

        // Remove unnecessary references to the back dir, and its parent
        final int indexDirBack = path.indexOf(DIR_BACK);
        if (indexDirBack != -1) {
            if (indexDirBack > 0) {
                path.remove(indexDirBack);
                path.remove(indexDirBack - 1);
                normalize(path, absolute);
            } else {
                throw new IllegalArgumentException("Cannot specify to go back \"../\" past the root");
            }
        }

        // Nothing left to do; reconstruct
        final StringBuilder sb = new StringBuilder();
        if (absolute) {
            sb.append(ArchivePath.SEPARATOR);
        }
        for (int i = 0; i < path.size(); i++) {
            if (i > 0) {
                sb.append(ArchivePath.SEPARATOR);
            }
            sb.append(path.get(i));
        }
        return sb.toString();
    }
1335813
public static String removeRelativeParts(final String path) {

		String partialPath = "";

		for (String part : PathHelper.getParts(path)) {

			// ignore ".." and "." in paths
			if ("..".equals(part) || ".".equals(part)) {
				continue;
			}

			partialPath += PathHelper.PATH_SEP + part;
		}

		return partialPath;
	}
853586
static Path makeRelative(Path path) {
    if (!path.isAbsolute()) {
      return path;
    }          
    String p = path.toUri().getPath();
    String relative = p.substring(1, p.length());
    return new Path(relative);
  }
800944
@Restricted(NoExternalUse.class)
    public static String normalize(@Nonnull String path) {
        StringBuilder buf = new StringBuilder();
        // Check for prefix designating absolute path
        Matcher m = ABSOLUTE_PREFIX_PATTERN.matcher(path);
        if (m.find()) {
            buf.append(m.group(1));
            path = path.substring(m.end());
        }
        boolean isAbsolute = buf.length() > 0;
        // Split remaining path into tokens, trimming any duplicate or trailing separators
        List<String> tokens = new ArrayList<>();
        int s = 0, end = path.length();
        for (int i = 0; i < end; i++) {
            char c = path.charAt(i);
            if (c == '/' || c == '\\') {
                tokens.add(path.substring(s, i));
                s = i;
                // Skip any extra separator chars
                while (++i < end && ((c = path.charAt(i)) == '/' || c == '\\')) { }
                // Add token for separator unless we reached the end
                if (i < end) tokens.add(path.substring(s, s+1));
                s = i;
            }
        }
        if (s < end) tokens.add(path.substring(s));
        // Look through tokens for "." or ".."
        for (int i = 0; i < tokens.size();) {
            String token = tokens.get(i);
            if (token.equals(".")) {
                tokens.remove(i);
                if (tokens.size() > 0)
                    tokens.remove(i > 0 ? i - 1 : i);
            } else if (token.equals("..")) {
                if (i == 0) {
                    // If absolute path, just remove: /../something
                    // If relative path, not collapsible so leave as-is
                    tokens.remove(0);
                    if (tokens.size() > 0) token += tokens.remove(0);
                    if (!isAbsolute) buf.append(token);
                } else {
                    // Normalize: remove something/.. plus separator before/after
                    i -= 2;
                    for (int j = 0; j < 3; j++) tokens.remove(i);
                    if (i > 0) tokens.remove(i-1);
                    else if (tokens.size() > 0) tokens.remove(0);
                }
            } else
                i += 2;
        }
        // Recombine tokens
        for (String token : tokens) buf.append(token);
        if (buf.length() == 0) buf.append('.');
        return buf.toString();
    }
1045617
private Path fromString(final String path) {
        if (path == null) {
            throw new IllegalArgumentException("path must be specified");
        }
        // Delegate
        return new ShrinkWrapPath(path, fileSystem);
    }
300866
protected function canonicalizePath($path)
    {
        if ('' === $path || '/' === $path) {
            return $path;
        }

        if ('.' === substr($path, -1)) {
            $path .= '/';
        }

        $output = [];

        foreach (explode('/', $path) as $segment) {
            if ('..' === $segment) {
                array_pop($output);
            } elseif ('.' !== $segment) {
                $output[] = $segment;
            }
        }

        return implode('/', $output);
    }
904708
public Metadata remove(String path) {
        this.values.remove(this.pathToProperty(path));
        this.addOp("remove", path, (String) null);
        return this;
    }
821317
@CheckForNull
  public static String sanitize(@Nullable String path) {
    return FilenameUtils.normalize(path, true);
  }
548932
static String canonicalizePath(String path) {
    String[] parts = path.split(MODULE_SLASH);
    String[] buffer = new String[parts.length];
    int position = 0;
    int available = 0;

    boolean absolutePath = (parts.length > 1 && parts[0].isEmpty());
    if (absolutePath) {
      // If the path starts with "/" (so the left side, index zero, is empty), then the path will
      // always remain absolute. Make the first segment unavailable to touch.
      --available;
    }

    for (String part : parts) {
      if (part.equals(".")) {
        continue;
      }

      if (part.equals("..")) {
        if (available > 0) {
          // Consume the previous segment.
          --position;
          --available;
          buffer[position] = null;
        } else if (!absolutePath) {
          // If this is a relative path, retain "..", as it can't be consumed on the left.
          buffer[position] = part;
          ++position;
        }
        continue;
      }

      buffer[position] = part;
      ++position;
      ++available;
    }

    if (absolutePath && position == 1) {
      return MODULE_SLASH;  // special-case single absolute segment as joining [""] doesn't work
    }
    return MODULE_JOINER.join(Arrays.copyOf(buffer, position));
  }
869185
function normalizePath(path)
{
    if (path.indexOf("..") === -1 && path.indexOf('.') === -1)
        return path;

    var normalizedSegments = [];
    var segments = path.split("/");
    for (var i = 0; i < segments.length; i++) {
        var segment = segments[i];
        if (segment === ".")
            continue;
        else if (segment === "..")
            normalizedSegments.pop();
        else if (segment)
            normalizedSegments.push(segment);
    }
    var normalizedPath = normalizedSegments.join("/");
    if (normalizedPath[normalizedPath.length - 1] === "/")
        return normalizedPath;
    if (path[0] === "/" && normalizedPath)
        normalizedPath = "/" + normalizedPath;
    if ((path[path.length - 1] === "/") || (segments[segments.length - 1] === ".") || (segments[segments.length - 1] === ".."))
        normalizedPath = normalizedPath + "/";

    return normalizedPath;
}