jetbrick-commons/src/main/java/jetbrick/io/PathUtils.java
public static String normalize(final String path) {
        if (path == null) {
            return null;
        }
        if (path.indexOf('.') == -1 && path.indexOf('/') == -1) {
            return path;
        }

        boolean wasNormalized = true;

        // 1. count number of nonempty segments in path
        // Note that this step is not really necessary because we could simply
        // estimate the number of segments as path.length() and do the empty
        // segment check in step two ;-).
        int numSegments = 0;
        int lastChar = path.length() - 1;
        for (int src = lastChar; src >= 0;) {
            int slash = path.lastIndexOf('/', src);
            if (slash != -1) {
                // empty segment? (two adjacent slashes?)
                if (slash == src) {
                    if (src != lastChar) { // ignore the first slash occurence
                        // (when numSegments == 0)
                        wasNormalized = false;
                    }
                } else {
                    numSegments++;
                }
            } else {
                numSegments++;
            }
            src = slash - 1;
        }

        // 2. split path to segments skipping empty segments
        int[] segments = new int[numSegments];
        char[] chars = new char[path.length()];
        path.getChars(0, chars.length, chars, 0);
        numSegments = 0;
        for (int src = 0; src < chars.length;) {
            // skip empty segments
            while (src < chars.length && chars[src] == '/') {
                src++;
            }
            if (src < chars.length) {
                // note the segment start
                segments[numSegments++] = src;
                // seek to the end of the segment
                while (src < chars.length && chars[src] != '/') {
                    src++;
                }
            }
        }
        // assert (numSegments == segments.length);

        // 3. scan segments and remove all "." segments and "foo",".." segment pairs
        final int DELETED = -1;
        for (int segment = 0; segment < numSegments; segment++) {
            int src = segments[segment];
            if (chars[src++] == '.') {
                if (src == chars.length || chars[src] == '/') { // "." or"./"
                    // delete the "." segment
                    segments[segment] = DELETED;
                    wasNormalized = false;
                } else { // ".something"
                    if (chars[src++] == '.' && (src == chars.length || chars[src] == '/')) { // ".." or "../"
                        // we have the ".." segment scan backwards for segment to delete together with ".."
                        for (int toDelete = segment - 1; toDelete >= 0; toDelete--) {
                            if (segments[toDelete] != DELETED) {
                                if (chars[segments[toDelete]] != '.') {
                                    // delete the two segments
                                    segments[toDelete] = DELETED;
                                    segments[segment] = DELETED;
                                    wasNormalized = false;
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }

        // 4. join the result, if necessary
        if (wasNormalized) { // already normalized? nothing to do...
            return path;
        } else {
            // join the resulting normalized path, retain the leading and ending slash
            int dst = (chars[0] == '/') ? 1 : 0;
            for (int segment = 0; segment < numSegments; segment++) {
                int segmentStart = segments[segment];
                if (segmentStart != DELETED) {
                    // if we remembered segment lengths in step 2, we could use
                    // System.arraycopy method now but we had to allocate one
                    // more array
                    for (int src = segmentStart; src < chars.length; src++) {
                        char ch = chars[src];
                        chars[dst++] = ch;
                        if (ch == '/') {
                            break;
                        }
                    }
                }
            }
            return new String(chars, 0, dst);
        }
    }
public static String normalize(final String path) {
        if (path == null) {
            return null;
        }
        if (path.indexOf('.') == -1 && path.indexOf('/') == -1) {
            return path;
        }

        boolean wasNormalized = true;

        // 1. count number of nonempty segments in path
        // Note that this step is not really necessary because we could simply
        // estimate the number of segments as path.length() and do the empty
        // segment check in step two ;-).
        int numSegments = 0;
        int lastChar = path.length() - 1;
        for (int src = lastChar; src >= 0;) {
            int slash = path.lastIndexOf('/', src);
            if (slash != -1) {
                // empty segment? (two adjacent slashes?)
                if (slash == src) {
                    if (src != lastChar) { // ignore the first slash occurence
                        // (when numSegments == 0)
                        wasNormalized = false;
                    }
                } else {
                    numSegments++;
                }
            } else {
                numSegments++;
            }
            src = slash - 1;
        }

        // 2. split path to segments skipping empty segments
        int[] segments = new int[numSegments];
        char[] chars = new char[path.length()];
        path.getChars(0, chars.length, chars, 0);
        numSegments = 0;
        for (int src = 0; src < chars.length;) {
            // skip empty segments
            while (src < chars.length && chars[src] == '/') {
                src++;
            }
            if (src < chars.length) {
                // note the segment start
                segments[numSegments++] = src;
                // seek to the end of the segment
                while (src < chars.length && chars[src] != '/') {
                    src++;
                }
            }
        }
        // assert (numSegments == segments.length);

        // 3. scan segments and remove all "." segments and "foo",".." segment pairs
        final int DELETED = -1;
        for (int segment = 0; segment < numSegments; segment++) {
            int src = segments[segment];
            if (chars[src++] == '.') {
                if (src == chars.length || chars[src] == '/') { // "." or"./"
                    // delete the "." segment
                    segments[segment] = DELETED;
                    wasNormalized = false;
                } else { // ".something"
                    if (chars[src++] == '.' && (src == chars.length || chars[src] == '/')) { // ".." or "../"
                        // we have the ".." segment scan backwards for segment to delete together with ".."
                        for (int toDelete = segment - 1; toDelete >= 0; toDelete--) {
                            if (segments[toDelete] != DELETED) {
                                if (chars[segments[toDelete]] != '.') {
                                    // delete the two segments
                                    segments[toDelete] = DELETED;
                                    segments[segment] = DELETED;
                                    wasNormalized = false;
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }

        // 4. join the result, if necessary
        if (wasNormalized) { // already normalized? nothing to do...
            return path;
        } else {
            // join the resulting normalized path, retain the leading and ending slash
            int dst = (chars[0] == '/') ? 1 : 0;
            for (int segment = 0; segment < numSegments; segment++) {
                int segmentStart = segments[segment];
                if (segmentStart != DELETED) {
                    // if we remembered segment lengths in step 2, we could use
                    // System.arraycopy method now but we had to allocate one
                    // more array
                    for (int src = segmentStart; src < chars.length; src++) {
                        char ch = chars[src];
                        chars[dst++] = ch;
                        if (ch == '/') {
                            break;
                        }
                    }
                }
            }
            return new String(chars, 0, dst);
        }
    }
public static String normalize ( final String path ) { if ( path == null ) { return null ; } if ( path . indexOf ( ' ' ) == - 1 && path . indexOf ( ' ' ) == - 1 ) { return path ; } boolean wasNormalized = true ; // 1. count number of nonempty segments in path // Note that this step is not really necessary because we could simply // estimate the number of segments as path.length() and do the empty // segment check in step two ;-). int numSegments = 0 ; int lastChar = path . length ( ) - 1 ; for ( int src = lastChar ; src >= 0 ; ) { int slash = path . lastIndexOf ( ' ' , src ) ; if ( slash != - 1 ) { // empty segment? (two adjacent slashes?) if ( slash == src ) { if ( src != lastChar ) { // ignore the first slash occurence // (when numSegments == 0) wasNormalized = false ; } } else { numSegments ++ ; } } else { numSegments ++ ; } src = slash - 1 ; } // 2. split path to segments skipping empty segments int [ ] segments = new int [ numSegments ] ; char [ ] chars = new char [ path . length ( ) ] ; path . getChars ( 0 , chars . length , chars , 0 ) ; numSegments = 0 ; for ( int src = 0 ; src < chars . length ; ) { // skip empty segments while ( src < chars . length && chars [ src ] == ' ' ) { src ++ ; } if ( src < chars . length ) { // note the segment start segments [ numSegments ++ ] = src ; // seek to the end of the segment while ( src < chars . length && chars [ src ] != ' ' ) { src ++ ; } } } // assert (numSegments == segments.length); // 3. scan segments and remove all "." segments and "foo",".." segment pairs final int DELETED = - 1 ; for ( int segment = 0 ; segment < numSegments ; segment ++ ) { int src = segments [ segment ] ; if ( chars [ src ++ ] == ' ' ) { if ( src == chars . length || chars [ src ] == ' ' ) { // "." or"./" // delete the "." segment segments [ segment ] = DELETED ; wasNormalized = false ; } else { // ".something" if ( chars [ src ++ ] == ' ' && ( src == chars . length || chars [ src ] == ' ' ) ) { // ".." or "../" // we have the ".." segment scan backwards for segment to delete together with ".." for ( int toDelete = segment - 1 ; toDelete >= 0 ; toDelete -- ) { if ( segments [ toDelete ] != DELETED ) { if ( chars [ segments [ toDelete ] ] != ' ' ) { // delete the two segments segments [ toDelete ] = DELETED ; segments [ segment ] = DELETED ; wasNormalized = false ; } break ; } } } } } } // 4. join the result, if necessary if ( wasNormalized ) { // already normalized? nothing to do... return path ; } else { // join the resulting normalized path, retain the leading and ending slash int dst = ( chars [ 0 ] == ' ' ) ? 1 : 0 ; for ( int segment = 0 ; segment < numSegments ; segment ++ ) { int segmentStart = segments [ segment ] ; if ( segmentStart != DELETED ) { // if we remembered segment lengths in step 2, we could use // System.arraycopy method now but we had to allocate one // more array for ( int src = segmentStart ; src < chars . length ; src ++ ) { char ch = chars [ src ] ; chars [ dst ++ ] = ch ; if ( ch == ' ' ) { break ; } } } } return new String ( chars , 0 , dst ) ; } }
Returns normalized <code>path</code> (or simply the <code>path</code> if
it is already in normalized form). Normalized path does not contain any
empty or "." segments or ".." segments preceded by other segment than
"..".

@param path path to normalize
@return normalize path
Returns normalized <code > path< / code > ( or simply the <code > path< / code > if it is already in normalized form ) . Normalized path does not contain any empty or . segments or .. segments preceded by other segment than .. .