Merge pull request #64 in ITERATE/cyberduck from feature/windows/9074 to master
[cyberduck.git] / source / ch / cyberduck / core / PathNormalizer.java
bloba81d3352dd141bacc8bdf68a9f16357649e36316
1 package ch.cyberduck.core;
3 /*
4 * Copyright (c) 2002-2013 David Kocher. All rights reserved.
5 * http://cyberduck.ch/
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * Bug fixes, suggestions and comments should be sent to feedback@cyberduck.ch
20 import ch.cyberduck.core.preferences.PreferencesFactory;
21 import ch.cyberduck.core.unicode.NFCNormalizer;
23 import org.apache.commons.io.FilenameUtils;
24 import org.apache.commons.lang3.StringUtils;
26 import java.util.List;
28 /**
29 * @version $Id$
31 public final class PathNormalizer {
33 private PathNormalizer() {
37 public static String name(final String path) {
38 final String normalized = normalize(path, true);
39 if(String.valueOf(Path.DELIMITER).equals(normalized)) {
40 return path;
42 return FilenameUtils.getName(normalized);
45 public static String parent(final String absolute, final char delimiter) {
46 if(absolute.equals(String.valueOf(delimiter))) {
47 return null;
49 int index = absolute.length() - 1;
50 if(absolute.charAt(index) == delimiter) {
51 if(index > 0) {
52 index--;
55 int cut = absolute.lastIndexOf(delimiter, index);
56 if(cut > 0) {
57 return absolute.substring(0, cut);
59 //if (index == 0) parent is root
60 return String.valueOf(delimiter);
63 public static String normalize(final String path) {
64 return normalize(path, true);
67 /**
68 * Return a context-relative path, beginning with a "/", that represents
69 * the canonical version of the specified path after ".." and "." elements
70 * are resolved out.
72 * @param path The path to parse
73 * @param absolute If the path is absolute
74 * @return the normalized path.
76 public static String normalize(final String path, final boolean absolute) {
77 if(null == path) {
78 return String.valueOf(Path.DELIMITER);
80 String normalized = path;
81 if(PreferencesFactory.get().getBoolean("path.normalize")) {
82 if(absolute) {
83 while(!normalized.startsWith(String.valueOf(Path.DELIMITER))) {
84 normalized = Path.DELIMITER + normalized;
87 while(!normalized.endsWith(String.valueOf(Path.DELIMITER))) {
88 normalized += Path.DELIMITER;
90 // Resolve occurrences of "/./" in the normalized path
91 while(true) {
92 int index = normalized.indexOf("/./");
93 if(index < 0) {
94 break;
96 normalized = normalized.substring(0, index) +
97 normalized.substring(index + 2);
99 // Resolve occurrences of "/../" in the normalized path
100 while(true) {
101 int index = normalized.indexOf("/../");
102 if(index < 0) {
103 break;
105 if(index == 0) {
106 // The only left path is the root.
107 return String.valueOf(Path.DELIMITER);
109 normalized = normalized.substring(0, normalized.lastIndexOf(Path.DELIMITER, index - 1)) +
110 normalized.substring(index + 3);
112 StringBuilder n = new StringBuilder();
113 if(normalized.startsWith("//")) {
114 // see #972. Omit leading delimiter
115 n.append(Path.DELIMITER);
116 n.append(Path.DELIMITER);
118 else if(absolute) {
119 // convert to absolute path
120 n.append(Path.DELIMITER);
122 else if(normalized.startsWith(String.valueOf(Path.DELIMITER))) {
123 // Keep absolute path
124 n.append(Path.DELIMITER);
126 // Remove duplicated Path.DELIMITERs
127 final String[] segments = normalized.split(String.valueOf(Path.DELIMITER));
128 for(String segment : segments) {
129 if(segment.equals(StringUtils.EMPTY)) {
130 continue;
132 n.append(segment);
133 n.append(Path.DELIMITER);
135 normalized = n.toString();
136 while(normalized.endsWith(String.valueOf(Path.DELIMITER)) && normalized.length() > 1) {
137 //Strip any redundant delimiter at the end of the path
138 normalized = normalized.substring(0, normalized.length() - 1);
141 if(PreferencesFactory.get().getBoolean("path.normalize.unicode")) {
142 return new NFCNormalizer().normalize(normalized);
144 // Return the normalized path that we have completed
145 return normalized;
149 * Prunes the list of selected files. Files which are a child of an already included directory
150 * are removed from the returned list.
152 * @param selected Selected files for transfer
153 * @return Normalized
155 public static List<Path> normalize(final List<Path> selected) {
156 final List<Path> normalized = new Collection<Path>();
157 for(Path f : selected) {
158 boolean duplicate = false;
159 for(Path n : normalized) {
160 if(f.isChild(n)) {
161 // The selected file is a child of a directory already included
162 duplicate = true;
163 break;
166 if(!duplicate) {
167 normalized.add(f);
170 return normalized;