public final static String canonicalize()

in plugin/src/com/microsoft/alm/plugin/versioncontrol/path/ServerPath.java [122:261]


    public final static String canonicalize(String serverPath, boolean checkForIllegalDollar) throws ServerPathFormatException {
        ArgumentHelper.checkNotNull(serverPath, "serverPath");

        int serverPathLength = serverPath.length();

        // empty string is not valid
        if (serverPathLength == 0) {
            logger.warn("Server path is empty");
            throw new ServerPathFormatException(serverPath);
        }

        /*
         * Do a quicker check up front to see if the path is OK. If it's not,
         * we'll try to fix it up and/or throw the appropriate exception.
         *
         * This is a huge win, since almost all of the server paths we ever
         * encounter in the program (and especially at large scale, like
         * translating hundreds of thousands of server paths to local paths) are
         * already canonicalized.
         */
        if (isCanonicalizedPath(serverPath, true)) {
            return serverPath;
        }

        final List<String> newComponents = new ArrayList<String>();
        final StringBuilder currentComponent = new StringBuilder(serverPathLength);
        int position = 0;

        // A simple conversion for $ to $/
        if (serverPath.equals(ServerPath.ROOT_NAME_ONLY)) {
            serverPath = ServerPath.ROOT;
            serverPathLength = 2;
        }

        // prepend a $ if necessary (next check will take care of the following /)
        if (serverPath.charAt(0) == '$') {
            position++;
        }

        newComponents.add("$");

        // the path must begin with one of: /, $/, \, $\
        if (position >= serverPathLength || ServerPath.isSeparator(serverPath.charAt(position)) == false) {
            logger.warn("The server path is not absolute: " + serverPath);
            throw new ServerPathFormatException(serverPath);
        }

        boolean illegalDollarInPath = false;

        // walk the rest of the given path
        for (; position <= serverPathLength; position++) {
            // end of the string or directory separators, append the current
            // component to the list
            if (position == serverPathLength || ServerPath.isSeparator(serverPath.charAt(position))) {
                // squash multiple concurrent separators
                if (currentComponent.length() == 0) {
                    // Ignore
                }
                // single dot components are thrown away
                else if (currentComponent.toString().equals(".")) {
                    // Ignore
                }
                // double dot components mean to pop the last off the stack
                else if (currentComponent.toString().equals("..")) {
                    if (newComponents.size() <= 1) {
                        logger.warn(String.format("Server path %s refers to invalid path outside %s", serverPath, ROOT));
                        throw new ServerPathFormatException(serverPath);
                    }

                    newComponents.remove(newComponents.size() - 1);
                }
                // otherwise, pop this bad boy on the directory stack
                else {
                    final String cleaned = ServerPath.cleanupComponent(currentComponent).toString();

                    /*
                     * Match Visual Studio behavior of ignoring directories that
                     * contain nothing but spaces and dots.
                     */
                    if (cleaned.length() == 0) {
                        // Ignore
                    } else if (cleaned.length() > MAXIMUM_COMPONENT_LENGTH) {
                        logger.warn(String.format("Server path component %s is longer than the maximum character length %s", cleaned, MAXIMUM_COMPONENT_LENGTH));
                        throw new ServerPathFormatException(cleaned);
                    } else if (FileHelper.isReservedName(cleaned)) {
                        logger.warn(String.format("Server path %s contains an invalid directory component: %s", serverPath, cleaned));
                        throw new ServerPathFormatException(cleaned);
                    } else {
                        if (cleaned.charAt(0) == '$') {
                            illegalDollarInPath = true;
                        }

                        // Good component, just add.
                        newComponents.add(cleaned);
                    }
                }

                currentComponent.setLength(0);
            }
            // a non-directory separator, non-dot, but valid in NTFS filenames
            else if (ServerPath.isValidPathCharacter(serverPath.charAt(position)) == true) {
                currentComponent.append(serverPath.charAt(position));
            }
            // invalid character
            else {
                char c = serverPath.charAt(position);
                char c_safe = c < ' ' ? '?' : c;
                logger.warn(String.format("The character (%s) is not permitted in server paths %s.",
                        c_safe, serverPath.replace(c, c_safe)));
                throw new ServerPathFormatException(serverPath.replace(c, c_safe));
            }
        }

        if (newComponents.size() == 1) {
            return ServerPath.ROOT;
        }

        // join components with a slash
        final StringBuilder newPath = new StringBuilder();
        for (int i = 0; i < newComponents.size(); i++) {
            if (i > 0) {
                newPath.append(ServerPath.PREFERRED_SEPARATOR_CHARACTER);
            }

            newPath.append(newComponents.get(i));
        }

        /*
         * We were checking for illegal dollar in the path during the loop
         * through the string. Throw the same exception as checkForIllegalDollar
         * would, if the flag was raised stating we had an illegal dollar
         * somewhere.
         */
        if (illegalDollarInPath && checkForIllegalDollar) {
            logger.warn(String.format("TF10122: The path %s contains a $ at the beginning of a path component.", newPath.toString()));
            throw new ServerPathFormatException(newPath.toString());
        }

        return newPath.toString();
    }