1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.shared.filtering;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.io.RandomAccessFile;
24 import java.io.Reader;
25 import java.io.Writer;
26 import java.nio.Buffer;
27 import java.nio.ByteBuffer;
28 import java.nio.CharBuffer;
29 import java.nio.charset.Charset;
30 import java.nio.charset.CharsetEncoder;
31 import java.nio.charset.CoderResult;
32 import java.nio.file.Files;
33 import java.nio.file.LinkOption;
34 import java.nio.file.NoSuchFileException;
35 import java.nio.file.StandardCopyOption;
36 import java.util.StringTokenizer;
37 import java.util.regex.Pattern;
38
39 import org.apache.commons.io.IOUtils;
40 import org.codehaus.plexus.util.Os;
41
42
43
44
45
46 public final class FilteringUtils {
47
48
49
50 private static final int ONE_KB = 1024;
51
52
53
54
55 private static final int ONE_MB = ONE_KB * ONE_KB;
56
57
58
59
60 private static final int FILE_COPY_BUFFER_SIZE = ONE_MB * 30;
61
62 private static final String WINDOWS_PATH_PATTERN = "^(.*)[a-zA-Z]:\\\\(.*)";
63
64 private static final Pattern PATTERN = Pattern.compile(WINDOWS_PATH_PATTERN);
65
66
67
68
69 private FilteringUtils() {
70
71 }
72
73
74
75
76
77
78
79 public static String escapeWindowsPath(String val) {
80 if (!isEmpty(val) && PATTERN.matcher(val).matches()) {
81
82 StringBuilder buf = new StringBuilder(val.length());
83 int start = 0, end = 0;
84 while ((end = val.indexOf('\\', start)) != -1) {
85 buf.append(val, start, end).append("\\\\");
86 start = end + 1;
87
88 if (val.indexOf('\\', end + 1) == end + 1) {
89 start++;
90 }
91 }
92
93 buf.append(val.substring(start));
94
95 return buf.toString();
96 }
97 return val;
98 }
99
100
101
102
103
104
105
106
107
108
109 public static File resolveFile(final File baseFile, String filename) {
110 String filenm = filename;
111 if ('/' != File.separatorChar) {
112 filenm = filename.replace('/', File.separatorChar);
113 }
114
115 if ('\\' != File.separatorChar) {
116 filenm = filename.replace('\\', File.separatorChar);
117 }
118
119
120 if (filenm.startsWith(File.separator) || (Os.isFamily(Os.FAMILY_WINDOWS) && filenm.indexOf(":") > 0)) {
121 File file = new File(filenm);
122
123 try {
124 file = file.getCanonicalFile();
125 } catch (final IOException ioe) {
126
127 }
128
129 return file;
130 }
131
132
133 final char[] chars = filename.toCharArray();
134 final StringBuilder sb = new StringBuilder();
135
136
137
138
139 int start = 0;
140 if ('\\' == File.separatorChar) {
141 sb.append(filenm.charAt(0));
142 start++;
143 }
144
145 for (int i = start; i < chars.length; i++) {
146 final boolean doubleSeparator = File.separatorChar == chars[i] && File.separatorChar == chars[i - 1];
147
148 if (!doubleSeparator) {
149 sb.append(chars[i]);
150 }
151 }
152
153 filenm = sb.toString();
154
155
156 File file = (new File(baseFile, filenm)).getAbsoluteFile();
157
158 try {
159 file = file.getCanonicalFile();
160 } catch (final IOException ioe) {
161
162 }
163
164 return file;
165 }
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187 public static String getRelativeFilePath(final String oldPath, final String newPath) {
188 if (isEmpty(oldPath) || isEmpty(newPath)) {
189 return "";
190 }
191
192
193 String fromPath = new File(oldPath).getPath();
194 String toPath = new File(newPath).getPath();
195
196
197 if (toPath.matches("^\\[a-zA-Z]:")) {
198 toPath = toPath.substring(1);
199 }
200 if (fromPath.matches("^\\[a-zA-Z]:")) {
201 fromPath = fromPath.substring(1);
202 }
203
204
205 if (fromPath.startsWith(":", 1)) {
206 fromPath = Character.toLowerCase(fromPath.charAt(0)) + fromPath.substring(1);
207 }
208 if (toPath.startsWith(":", 1)) {
209 toPath = Character.toLowerCase(toPath.charAt(0)) + toPath.substring(1);
210 }
211
212
213
214 if ((toPath.startsWith(":", 1) && fromPath.startsWith(":", 1))
215 && (!toPath.substring(0, 1).equals(fromPath.substring(0, 1)))) {
216
217
218 return null;
219 }
220
221 if ((toPath.startsWith(":", 1) && !fromPath.startsWith(":", 1))
222 || (!toPath.startsWith(":", 1) && fromPath.startsWith(":", 1))) {
223
224
225 return null;
226 }
227
228 String resultPath = buildRelativePath(toPath, fromPath, File.separatorChar);
229
230 if (newPath.endsWith(File.separator) && !resultPath.endsWith(File.separator)) {
231 return resultPath + File.separator;
232 }
233
234 return resultPath;
235 }
236
237 private static String buildRelativePath(String toPath, String fromPath, final char separatorChar) {
238
239 StringTokenizer toTokeniser = new StringTokenizer(toPath, String.valueOf(separatorChar));
240 StringTokenizer fromTokeniser = new StringTokenizer(fromPath, String.valueOf(separatorChar));
241
242 int count = 0;
243
244
245 while (toTokeniser.hasMoreTokens() && fromTokeniser.hasMoreTokens()) {
246 if (separatorChar == '\\') {
247 if (!fromTokeniser.nextToken().equalsIgnoreCase(toTokeniser.nextToken())) {
248 break;
249 }
250 } else {
251 if (!fromTokeniser.nextToken().equals(toTokeniser.nextToken())) {
252 break;
253 }
254 }
255
256 count++;
257 }
258
259
260
261
262 toTokeniser = new StringTokenizer(toPath, String.valueOf(separatorChar));
263 fromTokeniser = new StringTokenizer(fromPath, String.valueOf(separatorChar));
264
265 while (count-- > 0) {
266 fromTokeniser.nextToken();
267 toTokeniser.nextToken();
268 }
269
270 StringBuilder relativePath = new StringBuilder();
271
272
273 while (fromTokeniser.hasMoreTokens()) {
274 fromTokeniser.nextToken();
275
276 relativePath.append("..");
277
278 if (fromTokeniser.hasMoreTokens()) {
279 relativePath.append(separatorChar);
280 }
281 }
282
283 if (relativePath.length() != 0 && toTokeniser.hasMoreTokens()) {
284 relativePath.append(separatorChar);
285 }
286
287
288 while (toTokeniser.hasMoreTokens()) {
289 relativePath.append(toTokeniser.nextToken());
290
291 if (toTokeniser.hasMoreTokens()) {
292 relativePath.append(separatorChar);
293 }
294 }
295 return relativePath.toString();
296 }
297
298 static boolean isEmpty(final String string) {
299 return string == null || string.trim().isEmpty();
300 }
301
302
303
304
305
306
307
308
309
310
311
312
313
314 public static void copyFile(File from, File to, String encoding, FilterWrapper[] wrappers, boolean overwrite)
315 throws IOException {
316 if (wrappers == null || wrappers.length == 0) {
317 if (overwrite || to.lastModified() < from.lastModified()) {
318 Files.copy(from.toPath(), to.toPath(), LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING);
319 }
320 } else {
321 Charset charset = charset(encoding);
322
323
324 try (Reader fileReader = Files.newBufferedReader(from.toPath(), charset)) {
325 Reader wrapped = fileReader;
326 for (FilterWrapper wrapper : wrappers) {
327 wrapped = wrapper.getReader(wrapped);
328 }
329
330 if (overwrite || !to.exists()) {
331 try (Writer fileWriter = Files.newBufferedWriter(to.toPath(), charset)) {
332 IOUtils.copy(wrapped, fileWriter);
333 }
334 } else {
335 CharsetEncoder encoder = charset.newEncoder();
336
337 int charBufferSize = (int) Math.floor(FILE_COPY_BUFFER_SIZE / (2 + 2 * encoder.maxBytesPerChar()));
338 int byteBufferSize = (int) Math.ceil(charBufferSize * encoder.maxBytesPerChar());
339
340 CharBuffer newChars = CharBuffer.allocate(charBufferSize);
341 ByteBuffer newBytes = ByteBuffer.allocate(byteBufferSize);
342 ByteBuffer existingBytes = ByteBuffer.allocate(byteBufferSize);
343
344 CoderResult coderResult;
345 int existingRead;
346 boolean writing = false;
347
348 try (RandomAccessFile existing = new RandomAccessFile(to, "rw")) {
349 int n;
350 while (-1 != (n = wrapped.read(newChars))) {
351 ((Buffer) newChars).flip();
352
353 coderResult = encoder.encode(newChars, newBytes, n != 0);
354 if (coderResult.isError()) {
355 coderResult.throwException();
356 }
357
358 ((Buffer) newBytes).flip();
359
360 if (!writing) {
361 existingRead = existing.read(existingBytes.array(), 0, newBytes.remaining());
362 if (existingRead == -1) {
363 writing = true;
364 } else {
365 ((Buffer) existingBytes).position(existingRead);
366 ((Buffer) existingBytes).flip();
367
368 if (newBytes.compareTo(existingBytes) != 0) {
369 writing = true;
370 if (existingRead > 0) {
371 existing.seek(existing.getFilePointer() - existingRead);
372 }
373 }
374 }
375 }
376
377 if (writing) {
378 existing.write(newBytes.array(), 0, newBytes.remaining());
379 }
380
381 ((Buffer) newChars).clear();
382 ((Buffer) newBytes).clear();
383 ((Buffer) existingBytes).clear();
384 }
385
386 if (existing.length() > existing.getFilePointer()) {
387 existing.setLength(existing.getFilePointer());
388 }
389 }
390 }
391 }
392 }
393
394 copyFilePermissions(from, to);
395 }
396
397
398
399
400
401
402
403
404
405
406
407 private static void copyFilePermissions(File source, File destination) throws IOException {
408 try {
409
410 Files.setPosixFilePermissions(destination.toPath(), Files.getPosixFilePermissions(source.toPath()));
411 } catch (NoSuchFileException nsfe) {
412
413 } catch (UnsupportedOperationException e) {
414
415 destination.setExecutable(source.canExecute());
416 destination.setReadable(source.canRead());
417 destination.setWritable(source.canWrite());
418 }
419 }
420
421 private static Charset charset(String encoding) {
422 if (encoding == null || encoding.isEmpty()) {
423 return Charset.defaultCharset();
424 } else {
425 return Charset.forName(encoding);
426 }
427 }
428 }