Convert a path to a cananonical form
//
// Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
/**
* URI Holder. This class assists with the decoding and encoding or HTTP URI's.
* It differs from the java.net.URL class as it does not provide communications
* ability, but it does assist with query string formatting.
* <P>
* UTF-8 encoding is used by default for % encoded characters. This may be
* overridden with the org.mortbay.util.URI.charset system property.
*
* @see UrlEncoded
* @author Greg Wilkins (gregw)
*/
public class Utils {
/**
* Convert a path to a cananonical form. All instances of "." and ".." are
* factored out. Null is returned if the path tries to .. above its root.
*
* @param path
* @return path or null.
*/
public static String canonicalPath(String path) {
if (path == null || path.length() == 0)
return path;
int end = path.length();
int queryIdx = path.indexOf('?');
int start = path.lastIndexOf('/', (queryIdx > 0 ? queryIdx : end));
search: while (end > 0) {
switch (end - start) {
case 2: // possible single dot
if (path.charAt(start + 1) != '.')
break;
break search;
case 3: // possible double dot
if (path.charAt(start + 1) != '.' || path.charAt(start + 2) != '.')
break;
break search;
}
end = start;
start = path.lastIndexOf('/', end - 1);
}
// If we have checked the entire string
if (start >= end)
return path;
StringBuffer buf = new StringBuffer(path);
int delStart = -1;
int delEnd = -1;
int skip = 0;
while (end > 0) {
switch (end - start) {
case 2: // possible single dot
if (buf.charAt(start + 1) != '.') {
if (skip > 0 && --skip == 0) {
delStart = start >= 0 ? start : 0;
if (delStart > 0 && delEnd == buf.length() && buf.charAt(delEnd - 1) == '.')
delStart++;
}
break;
}
if (start < 0 && buf.length() > 2 && buf.charAt(1) == '/' && buf.charAt(2) == '/')
break;
if (delEnd < 0)
delEnd = end;
delStart = start;
if (delStart < 0 || delStart == 0 && buf.charAt(delStart) == '/') {
delStart++;
if (delEnd < buf.length() && buf.charAt(delEnd) == '/')
delEnd++;
break;
}
if (end == buf.length())
delStart++;
end = start--;
while (start >= 0 && buf.charAt(start) != '/')
start--;
continue;
case 3: // possible double dot
if (buf.charAt(start + 1) != '.' || buf.charAt(start + 2) != '.') {
if (skip > 0 && --skip == 0) {
delStart = start >= 0 ? start : 0;
if (delStart > 0 && delEnd == buf.length() && buf.charAt(delEnd - 1) == '.')
delStart++;
}
break;
}
delStart = start;
if (delEnd < 0)
delEnd = end;
skip++;
end = start--;
while (start >= 0 && buf.charAt(start) != '/')
start--;
continue;
default:
if (skip > 0 && --skip == 0) {
delStart = start >= 0 ? start : 0;
if (delEnd == buf.length() && buf.charAt(delEnd - 1) == '.')
delStart++;
}
}
// Do the delete
if (skip <= 0 && delStart >= 0 && delStart >= 0) {
buf.delete(delStart, delEnd);
delStart = delEnd = -1;
if (skip > 0)
delEnd = end;
}
end = start--;
while (start >= 0 && buf.charAt(start) != '/')
start--;
}
// Too many ..
if (skip > 0)
return null;
// Do the delete
if (delEnd >= 0)
buf.delete(delStart, delEnd);
return buf.toString();
}
}
Related examples in the same category