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