1 /*
2 * #%L
3 * Nuiton Utils
4 * %%
5 * Copyright (C) 2004 - 2010 CodeLutin
6 * %%
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
11 *
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 Lesser Public License for more details.
16 *
17 * You should have received a copy of the GNU General Lesser Public
18 * License along with this program. If not, see
19 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
20 * #L%
21 */
22
23 package org.nuiton.util;
24
25 import org.apache.commons.io.FileUtils;
26 import org.apache.commons.io.IOUtils;
27 import org.apache.commons.lang3.StringUtils;
28 import org.apache.commons.lang3.SystemUtils;
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31
32 import java.io.BufferedInputStream;
33 import java.io.BufferedReader;
34 import java.io.BufferedWriter;
35 import java.io.ByteArrayOutputStream;
36 import java.io.File;
37 import java.io.FileFilter;
38 import java.io.FileInputStream;
39 import java.io.FileOutputStream;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.InputStreamReader;
43 import java.io.OutputStreamWriter;
44 import java.io.PrintStream;
45 import java.nio.CharBuffer;
46 import java.nio.MappedByteBuffer;
47 import java.nio.channels.FileChannel;
48 import java.nio.charset.Charset;
49 import java.nio.charset.CharsetDecoder;
50 import java.nio.charset.StandardCharsets;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.HashMap;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.regex.Matcher;
58 import java.util.regex.Pattern;
59
60 /**
61 * Opérations sur des fichiers. Copie, suppression, renommage,
62 * recherche, fichiers d'un répertoire, sous-répertoires d'un répertoire,
63 * récupération du basename ou de l'extension, création d'un fichier
64 * temporaire, comparaison de dates de création, récupération d'une chaîne,
65 * d'un Reader ou d'un Writer à partir d'un fichier, récupération du fichier
66 * saisi dans une boîte de dialogue, conversions en byte[], en Stream...
67 * <p>
68 * Created: 22 novembre 2004
69 *
70 * @author Benjamin Poussin - poussin@codelutin.com
71 * @author Tony Chemit - chemit@codelutin.com
72 */
73 public class FileUtil { // FileUtil
74
75 private static final Log log = LogFactory.getLog(FileUtil.class);
76
77 /**
78 * Encoding utilisé (peut être redéfini)
79 */
80 // TODO fdesbois 2011-04-16 : Perhaps change ISO encoding by UTF-8
81 public static final String ENCODING = StandardCharsets.ISO_8859_1.name();
82
83 /**
84 * Permet de convertir des bytes en fichier, le fichier sera automatiquement
85 * supprimé a la fin de la JVM.
86 *
87 * @param bytes the array of bytes to copy in dstination file
88 * @return le fichier temporaire contenant les bytes
89 * @throws IOException if any io pb
90 */
91 public static File byteToFile(byte[] bytes) throws IOException {
92 File file = File.createTempFile("FileUtil-byteToFile", ".tmp");
93 FileUtils.writeByteArrayToFile(file, bytes);
94 return file;
95 }
96
97 /**
98 * Retourne un Reader utilisant l'encoding par defaut {@link #ENCODING}.
99 *
100 * @param file the given reader
101 * @return the reader on the given file
102 * @throws IOException if any io pb
103 */
104 public static BufferedReader getReader(File file) throws IOException {
105 return getReader(file, ENCODING);
106 }
107
108 /**
109 * Retourne un reader utilisant l'encoding choisi et placé dans un
110 * BufferedReader
111 *
112 * @param file the given file
113 * @param encoding (ISO-8859-1, UTF-8, ...)
114 * @return the buffered reader in the given encoding
115 * @throws IOException if any io pb
116 */
117 public static BufferedReader getReader(File file,
118 String encoding) throws IOException {
119 FileInputStream inf = new FileInputStream(file);
120 InputStreamReader in = new InputStreamReader(inf, encoding);
121 BufferedReader result = new BufferedReader(in);
122 return result;
123 }
124
125 /**
126 * Retourne un Writer utilisant l'encoding par defaut {@link #ENCODING}.
127 *
128 * @param file the given file
129 * @return the writer on the given file
130 * @throws IOException if any io pb
131 */
132 public static BufferedWriter getWriter(File file) throws IOException {
133 return getWriter(file, ENCODING);
134 }
135
136 /**
137 * Retourne un writer utilisant l'encoding choisi et placé dans un
138 * BufferedWriter
139 *
140 * @param file the given file
141 * @param encoding (ISO-8859-1, UTF-8, ...)
142 * @return the buffered writer on the given file with given encoding
143 * @throws IOException if any io pb
144 */
145 public static BufferedWriter getWriter(File file,
146 String encoding) throws IOException {
147 FileOutputStream outf = new FileOutputStream(file);
148 OutputStreamWriter out = new OutputStreamWriter(outf, encoding);
149 BufferedWriter result = new BufferedWriter(out);
150 return result;
151 }
152
153
154 /**
155 * Permet de creer un nouveu repertoire temporaire, l'effacement du
156 * répertoire est a la charge de l'appelant
157 *
158 * @param prefix le prefix du fichier
159 * @param suffix le suffix du fichier
160 * @param tmpdir le répertoire temporaire ou il faut creer le repertoire
161 * si null on utilise java.io.tmpdir
162 * @return le fichier pointant sur le nouveau repertoire
163 * @throws IOException if any io pb
164 */
165 public static File createTempDirectory(String prefix,
166 String suffix,
167 File tmpdir) throws IOException {
168 if (tmpdir == null) {
169 tmpdir = new File(System.getProperty("java.io.tmpdir"));
170 }
171 File result = new File(tmpdir, prefix + System.currentTimeMillis() + suffix);
172 while (result.exists()) {
173 result = new File(tmpdir, prefix + System.currentTimeMillis() + suffix);
174 }
175 if (!result.mkdirs()) {
176 throw new IOException("Can't create temporary directory: " + result);
177 }
178 return result;
179 }
180
181 /**
182 * Permet de creer un nouveu repertoire temporaire, l'effacement du
183 * répertoire est a la charge de l'appelant
184 *
185 * @param prefix le prefix du repertoire a creer
186 * @param suffix le suffix du repertoire a creer.
187 * @return the temprary created file
188 * @throws IOException if any io pb
189 */
190 public static File createTempDirectory(String prefix,
191 String suffix) throws IOException {
192 return createTempDirectory(prefix, suffix, null);
193 }
194
195 /**
196 * Permet de donner une representation fichier pour une chaine de caractere.
197 * Le fichier sera automatiquement effacé à la fin de la JVM.
198 *
199 * @param content le contenu du fichier temporaire
200 * @return le fichier qui contient content
201 * @throws IOException if any io pb
202 */
203 public static File getTempFile(String content) throws IOException {
204 return getTempFile(content, "");
205 }
206
207 /**
208 * Permet de donner une representation fichier pour une chaine de caractere.
209 * Le fichier sera automatiquement effacé à la fin de la JVM.
210 *
211 * @param content le contenu du fichier temporaire
212 * @param fileSuffix l'extension du fichier créé
213 * @return le fichier qui contient content
214 * @throws IOException if any io pb
215 */
216 public static File getTempFile(String content,
217 String fileSuffix) throws IOException {
218 File result = File.createTempFile("tmp-" + FileUtil.class.getName(),
219 fileSuffix);
220 result.deleteOnExit();
221 FileUtils.write(result, content, Charset.defaultCharset());
222 return result;
223 }
224
225 /**
226 * Equivalent de la methode basename unix.
227 * basename("/tmp/toto.xml", ".xml") → "toto"
228 *
229 * @param file le fichier dont on souhaite le nom sans le chemin
230 * @param suffixes si present represente le suffixe a eliminer du fichier
231 * s'il est trouvé
232 * @return le nom du fichier sans le suffixe si trouvé.
233 */
234 public static String basename(File file, String... suffixes) {
235 String result = basename(file.getName(), suffixes);
236 return result;
237 }
238
239 /**
240 * Equivalent de la methode basename unix.
241 * basename("/tmp/toto.xml", ".xml") → "toto"
242 *
243 * @param name le nom du fichier dont on souhaite le nom sans le chemin
244 * @param suffixes si present represente le suffixe a eliminer du fichier
245 * s'il est trouvé
246 * @return le nom du fichier sans le suffixe si trouvé.
247 * @since 1.4.2
248 */
249 public static String basename(String name, String... suffixes) {
250 String result = name;
251 for (String suffixe : suffixes) {
252 if (result.endsWith(suffixe)) {
253 result = result.substring(0,
254 result.length() - suffixe.length());
255 break;
256 }
257 }
258 return result;
259 }
260
261 /**
262 * Permet de récupérer l'extension d'un fichier
263 *
264 * @param file le fichier dont on souhaite l'extension
265 * @param extchars la liste des caracteres pouvant former l'extension
266 * dans l'ordre de preference. Si vide on utilise ".".
267 * @return l'extension ou la chaine vide si le fichier n'a pas d'extension
268 * l'extension ne contient pas le chaine de delimitation
269 */
270 public static String extension(File file, String... extchars) {
271 String name = file.getName();
272 String result = extension(name, extchars);
273 return result;
274 }
275
276 /**
277 * Permet de récupérer l'extension d'un nom de fichier
278 *
279 * @param name le nom du fichier dont on souhaite l'extension
280 * @param extchars la liste des caracteres pouvant former l'extension
281 * dans l'ordre de preference. Si vide on utilise ".".
282 * @return l'extension ou la chaine vide si le fichier n'a pas d'extension
283 * l'extension ne contient pas le chaine de delimitation
284 * @since 1.4.2
285 */
286 public static String extension(String name, String... extchars) {
287 String result = "";
288
289 if (extchars.length == 0) {
290 extchars = new String[]{"."};
291 }
292 for (String extchar : extchars) {
293 int pos = name.lastIndexOf(extchar);
294 if (pos != -1) {
295 result = name.substring(pos + extchar.length());
296 break;
297 }
298 }
299 return result;
300 }
301
302 /**
303 * Recupère le fichier dans le même répertoire que le fichier donné et dont
304 * on a changé l'extension.
305 *
306 * @param file le fichier d'origine
307 * @param newExtension la nouvelle extension à utiliser
308 * @param extchars la liste des extensions possibles
309 * @return le fichier dont on a changé l'extension
310 * @throws IOException si aucune extension trouvé dans le fichier d'origine
311 * @since 1.4.2
312 */
313 public static File changeExtension(File file,
314 String newExtension,
315 String... extchars) throws IOException {
316 String name = file.getName();
317 String newName = changeExtension(name, newExtension, extchars);
318 File newFile = new File(file.getParentFile(), newName);
319 return newFile;
320 }
321
322 /**
323 * Change l'extension du fichier en entrée avec la nouvelle extension
324 *
325 * @param name le nom de fichier à transformer
326 * @param newExtension la nouvelle extension à utiliser
327 * @param extchars la liste des extensions possibles
328 * @return le nouveau nom de fichier
329 * @throws IOException si aucune extension trouvé dans le fichier d'origine
330 * @since 1.4.2
331 */
332 public static String changeExtension(String name,
333 String newExtension,
334 String... extchars) throws IOException {
335 String extension = extension(name, extchars);
336 if (extension == null) {
337 throw new IOException("Could not find extension for name " +
338 name + " within " + Arrays.toString(extchars));
339 }
340 String nameWithoutExtension = name.substring(
341 0, name.length() - extension.length());
342 String newName = nameWithoutExtension + newExtension;
343 return newName;
344 }
345
346 /**
347 * Recupère le fichier mirroir du fichier {@code file} donnée qui est dans
348 * l'arborescence de {@code inputDirectory} dans le répertoire
349 * {@code ouputDirectory}.
350 *
351 * @param inputDirectory le répertoire de départ
352 * @param outputDirectory le répertoire cible
353 * @param file le fichier
354 * @return le fichier mirroir dans le répertoire cible
355 * @since 1.4.2
356 */
357 public static File getRelativeFile(File inputDirectory,
358 File outputDirectory,
359 File file) {
360 String inputPath = inputDirectory.getAbsolutePath();
361 String s = file.getAbsolutePath();
362 int index = s.indexOf(inputPath);
363 if (index == -1) {
364 throw new IllegalArgumentException(
365 "File " + file + " is not in " + inputDirectory);
366 }
367 String relativePath = s.substring(inputPath.length());
368 File result = new File(outputDirectory, relativePath);
369 return result;
370 }
371
372 public interface FileAction {
373 boolean doAction(File f);
374 }
375
376 /**
377 * Retourne tous les sous répertoires du répertoire passé en argument.
378 *
379 * @param directory un répertoire
380 * @return une liste d'objet {@link File} de répertoires et ceci
381 * recursivement à partir de directory, si directory
382 * n'est pas un répertoire la liste est vide.
383 */
384 public static List<File> getSubDirectories(File directory) {
385 class DirectoryFilter implements FileFilter {
386 @Override
387 public boolean accept(File f) {
388 return f.isDirectory();
389 }
390 }
391 return getFilteredElements(directory, new DirectoryFilter(), true);
392 }
393
394 /**
395 * Retourne tous les fichiers du répertoire passé en argument.
396 *
397 * @param directory un répertoire
398 * @return une liste d'objet {@link File} des fichiers et ceci
399 * recursivement à partir de directory, si directory n'est pas un
400 * répertoire la liste est vide
401 */
402 public static List<File> getFiles(File directory) {
403 class NormalFileFilter implements FileFilter {
404 @Override
405 public boolean accept(File f) {
406 return f.isFile();
407 }
408 }
409 return getFilteredElements(directory, new NormalFileFilter(), true);
410 }
411
412 /**
413 * Retourne les fichiers d'un répertoire qui satisfont un certain pattern.
414 * La recherche est faite récursivement dans les sous répertoires
415 *
416 * @param directory le répertoire à partir duquel il faut faire la recherche
417 * @param pattern le pattern que doit respecter le fichier pour être dans la
418 * liste résultante
419 * @param recursively flag pour indiquer si on doit descendre dans les sous répertoires
420 * @return une liste d'objet {@link File} qui ont s'attisfait le
421 * pattern.
422 */
423 public static List<File> find(File directory,
424 final String pattern,
425 boolean recursively) {
426 final String root = directory.getAbsolutePath();
427 final int rootLength = root.length();
428
429 return getFilteredElements(directory, new FileFilter() {
430 @Override
431 public boolean accept(File f) {
432 String longFilename = f.getAbsolutePath();
433 // + 1 to remove the first / or \
434 String filename = longFilename.substring(rootLength + 1);
435 return filename.matches(pattern);
436 }
437 }, recursively);
438 }
439
440 /**
441 * Retourne la liste de toutes les fichiers ou répertoire qui s'attisfont
442 * le filtre
443 *
444 * @param directory repertoire à partir duquel il faut faire la recherche
445 * @param ff le filtre à appliquer pour savoir si le fichier parcouru doit
446 * être conservé dans les résultats, ou null pour tous les fichiers
447 * @param recursively un flag pour indiquer si on doit descendre dans les répertoires
448 * @return une liste d'objet {@link File}, qui s'attisfont le filtre
449 */
450 public static List<File> getFilteredElements(File directory,
451 FileFilter ff,
452 boolean recursively) {
453 ArrayList<File> result = new ArrayList<File>();
454 LinkedList<File> todo = new LinkedList<File>();
455 if (directory.isDirectory()) {
456 todo.addAll(Arrays.asList(directory.listFiles()));
457 }
458 while (todo.size() > 0) {
459 File file = todo.removeFirst();
460 if (recursively && file.isDirectory()) {
461 File[] childs = file.listFiles();
462 if (childs != null) {
463 // null if we don't have access to directory
464 todo.addAll(Arrays.asList(childs));
465 }
466 }
467 if (ff == null || ff.accept(file)) {
468 result.add(file);
469 }
470 }
471 return result;
472 }
473
474 /**
475 * Permet de faire une action avant le parcours des fichiers, c-a-d que
476 * l'on fera l'action sur les fichiers contenu dans un répertoire
477 * après l'action sur le répertoire lui même.
478 *
479 * @param f le fichier ou répertoire à partir duquel il faut commencer
480 * @param fileAction l'action à effectuer sur chaque fichier
481 * @return le résultat des fileAction executé sur les fichiers, chaque
482 * résultat de FileAction sont assemblé par un ET logique pour donner
483 * le résultat final
484 */
485 public static boolean walkAfter(File f, FileAction fileAction) {
486 boolean result = fileAction.doAction(f);
487 if (f.isDirectory()) {
488 File list[] = f.listFiles();
489 for (File aList : list) {
490 result = result && walkAfter(aList, fileAction);
491 }
492 }
493 return result;
494 }
495
496 /**
497 * Permet de faire une action apès le parcours des fichiers, c-a-d que
498 * l'on fera l'action sur les fichiers contenu dans un répertoire
499 * avant l'action sur le répertoire lui même.
500 *
501 * @param f le fichier ou répertoire à partir duquel il faut commencer
502 * @param fileAction l'action à effectuer sur chaque fichier
503 * @return le résultat des fileAction executé sur les fichiers, chaque
504 * résultat de FileAction sont assemblé par un ET logique pour donner
505 * le résultat final
506 */
507 public static boolean walkBefore(File f, FileAction fileAction) {
508 boolean result = true;
509 if (f.isDirectory()) {
510 File list[] = f.listFiles();
511 for (File aList : list) {
512 result = result && walkBefore(aList, fileAction);
513 }
514 }
515 return result && fileAction.doAction(f);
516 }
517
518 /**
519 * Copie recursivement le repertoire source dans le repertoire destination
520 * <p>
521 * copyRecursively("/truc/titi", "/var/tmp") donnera le repertoire
522 * "/var/tmp/titi"
523 *
524 * @param srcDir le répertoire source à copier
525 * @param destDir le répertoire destination où copier
526 * @param includePatterns les patterns que doivent resperter les
527 * fichiers/repertoires pour etre copié. Si vide alors tout est copié
528 * @throws IOException if any io pb
529 */
530 public static void copyRecursively(File srcDir,
531 File destDir,
532 String... includePatterns) throws IOException {
533 copyAndRenameRecursively(srcDir, destDir, null, null, includePatterns);
534 }
535
536 /**
537 * Copie recursivement le repertoire source dans le repertoire destination
538 * <p>
539 * copyRecursively("/truc/titi", "/var/tmp", "bidulle") donnera le repertoire
540 * "/var/tmp/bidulle", 'bidulle' remplacant 'titi'
541 *
542 * @param srcDir le répertoire source à copier
543 * @param destDir le répertoire destination où copier
544 * @param renameFrom pattern to permit rename file before uncompress it
545 * @param renameTo new name for file if renameFrom is applicable to it
546 * you can use $1, $2, ... if you have '(' ')' in renameFrom
547 * @param includePatterns les patterns que doivent resperter les
548 * fichiers/repertoires pour etre copié. Si vide alors tout est copié
549 * @throws IOException if any io pb
550 */
551 public static void copyAndRenameRecursively(File srcDir, File destDir,
552 String renameFrom,
553 String renameTo,
554 String... includePatterns) throws IOException {
555 copyAndRenameRecursively(srcDir,
556 destDir,
557 true,
558 renameFrom,
559 renameTo,
560 false,
561 includePatterns
562 );
563 }
564
565 /**
566 * Copie recursivement le repertoire source dans le repertoire destination
567 * <p>
568 * copyRecursively("/truc/titi", "/var/tmp", "bidulle") donnera le repertoire
569 * "/var/tmp/bidulle", 'bidulle' remplacant 'titi'
570 *
571 * @param srcDir le répertoire source à copier
572 * @param destDir le répertoire destination où copier
573 * @param includeSrcDir si vrai alors le repertoire source est copie dans le
574 * repertoire destination et non pas seulement les fichiers qu'il contient
575 * @param renameFrom pattern to permit rename file before uncompress it
576 * @param renameTo new name for file if renameFrom is applicable to it
577 * you can use $1, $2, ... if you have '(' ')' in renameFrom
578 * @param exclude inverse include pattern interpretation
579 * @param includePatterns les patterns que doivent resperter les
580 * fichiers/repertoires pour etre copié. Si vide alors tout est copié
581 * @throws IOException if any io pb
582 */
583 public static void copyAndRenameRecursively(File srcDir,
584 File destDir,
585 boolean includeSrcDir,
586 String renameFrom,
587 String renameTo,
588 boolean exclude,
589 String... includePatterns) throws IOException {
590 String rootSrc;
591 if (includeSrcDir) {
592 rootSrc = srcDir.getParent();
593 } else {
594 rootSrc = srcDir.getPath();
595 }
596 List<File> files = getFilteredElements(srcDir, null, true);
597 log.debug("copyRecursively: " + files);
598 for (File file : files) {
599 boolean doCopy = copyRecursivelyAccept(file, includePatterns);
600 if (exclude ^ doCopy) {
601 String path = file.getPath().substring(rootSrc.length());
602 if (renameFrom != null && renameTo != null) {
603 String tmp = path.replaceAll(renameFrom, renameTo);
604 if (log.isDebugEnabled()) {
605 log.debug("rename " + path + " → " + tmp);
606 }
607 path = tmp;
608 }
609
610 File destFile = new File(destDir, path);
611 if (file.isDirectory()) {
612 log.debug("create directory: " + destFile);
613 //fixme on doit tester le retour de la méthode, car il se peut que le répertoire
614 // ne puisse être copié.
615 createDirectoryIfNecessary(destFile);
616 } else {
617 log.debug("copy " + path + " to " + destFile);
618 FileUtils.copyFile(file, destFile);
619 }
620 }
621 }
622 }
623
624 /**
625 * Get a ByteArrayOutputStream containing all data that could be read from the given InputStream
626 *
627 * @param inputStream the stream to read
628 * @param defaultBufferSize the buffer size
629 * @return the input stream read for input
630 * @throws IOException if any pb while reading or writing
631 */
632 public static ByteArrayOutputStream readBytesFrom(InputStream inputStream,
633 int defaultBufferSize) throws IOException {
634
635 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(
636 defaultBufferSize);
637 byte[] buffer = new byte[defaultBufferSize];
638
639 int readBytes = inputStream.read(buffer);
640 while (readBytes > 0) {
641 outputStream.write(buffer, 0, readBytes);
642 readBytes = inputStream.read(buffer);
643 }
644
645 return outputStream;
646 }
647
648 /**
649 * @param file le fichier à tester.
650 * @param includePatterns les patterns pour accepeter le fichier depuis son nom
651 * @return {@code true} si le fichier est accepté, <code>false> autrement.
652 */
653 private static boolean copyRecursivelyAccept(File file,
654 String[] includePatterns) {
655 boolean result = includePatterns.length == 0;
656 String filename = file.getAbsolutePath();
657 for (String pattern : includePatterns) {
658 result = filename.matches(pattern);
659 if (result) {
660 break;
661 }
662 }
663 return result;
664 }
665
666 /**
667 * Use the linePattern to break the given CharBuffer into lines, applying
668 * the input pattern to each line to see if we have a match
669 * <p>
670 * Code taken from :
671 * <p>
672 * http://java.sun.com/javase/6/docs/technotes/guides/io/example/Grep.java
673 *
674 * @param regex regex to search into file
675 * @param cb nio buffer
676 * @return matching lines (or {code null} if no matching lines)
677 * @since 1.1.2
678 */
679 protected static List<CharSequence> grep(String regex,
680 CharBuffer cb) {
681
682 List<CharSequence> linesList = null;
683
684 Pattern pattern = Pattern.compile(regex);
685
686 Pattern linePattern = Pattern.compile(".*\r?\n");
687
688 Matcher lm = linePattern.matcher(cb); // Line matcher
689 Matcher pm = null; // Pattern matcher
690 //int lines = 0;
691 while (lm.find()) {
692 //lines++;
693 CharSequence cs = lm.group(); // The current line
694 if (pm == null) {
695 pm = pattern.matcher(cs);
696 } else {
697 pm.reset(cs);
698 }
699 if (pm.find()) {
700 // init
701 if (linesList == null) {
702 linesList = new ArrayList<CharSequence>();
703 }
704 linesList.add(cs);
705 }
706 if (lm.end() == cb.limit()) {
707 break;
708 }
709 }
710
711 return linesList;
712 }
713
714 /**
715 * Java implementation for the unix grep command.
716 * <p>
717 * Code taken from :
718 * <p>
719 * http://java.sun.com/javase/6/docs/technotes/guides/io/example/Grep.java
720 * <p>
721 * May fail on windows with error :
722 * The requested operation cannot be performed on a file with a user-mapped section open
723 *
724 * @param searchRegex regex to search into file
725 * @param f file to search into
726 * @param encoding encoding to use
727 * @return matching lines (or {code null} if no matching lines)
728 * @throws IOException FIXME
729 * @since 1.1.2
730 */
731 public static List<CharSequence> grep(String searchRegex,
732 File f,
733 String encoding) throws IOException {
734
735 List<CharSequence> lines = null;
736
737 FileInputStream fis = null;
738 FileChannel fc = null;
739
740 try {
741 // Open the file and then get a channel from the stream
742 fis = new FileInputStream(f);
743 fc = fis.getChannel();
744
745 // Get the file's size and then map it into memory
746 int sz = (int) fc.size();
747 MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
748
749 // Decode the file into a char buffer
750 Charset charset = Charset.forName(encoding);
751 CharsetDecoder decoder = charset.newDecoder();
752 CharBuffer cb = decoder.decode(bb);
753
754 // Perform the search
755 lines = grep(searchRegex, cb);
756 } finally {
757 // Close the channel and the stream
758 if (fc != null) {
759 fc.close();
760 }
761 if (fis != null) {
762 fis.close();
763 }
764 }
765
766 return lines;
767 }
768
769 /**
770 * Java implementation for the unix grep command.
771 * <p>
772 * May fail on windows with error :
773 * The requested operation cannot be performed on a file with a user-mapped section open
774 *
775 * @param searchRegex regex to search into file
776 * @param rootDirectory directory to seacrh into
777 * @param fileRegex regex for file to find in {@code rootDirectory}
778 * @param encoding encoding to use
779 * @return all matching lines for each files
780 * @throws IOException FIXME
781 * @since 1.1.2
782 */
783 public static Map<File, List<CharSequence>> grep(String searchRegex,
784 File rootDirectory,
785 String fileRegex,
786 String encoding) throws IOException {
787 Map<File, List<CharSequence>> results =
788 new HashMap<File, List<CharSequence>>();
789 List<File> files = find(rootDirectory, fileRegex, true);
790 for (File file : files) {
791 List<CharSequence> lines = grep(searchRegex, file, encoding);
792 if (lines != null) {
793 results.put(file, lines);
794 }
795 }
796 return results;
797 }
798
799 /**
800 * Search for files matching regex in current directory.
801 * <p>
802 * May fail on windows with error :
803 * The requested operation cannot be performed on a file with a user-mapped section open
804 *
805 * @param searchRegex regex to search into file
806 * @param fileRegex regex for file to find in current dir
807 * @param encoding encoding to use
808 * @return all matching lines for each files
809 * @throws IOException FIXME
810 * @since 1.1.2
811 */
812 public static Map<File, List<CharSequence>> grep(String searchRegex,
813 String fileRegex,
814 String encoding) throws IOException {
815 Map<File, List<CharSequence>> results = grep(searchRegex,
816 new File("."),
817 fileRegex,
818 encoding);
819 return results;
820 }
821
822 /**
823 * Sed implementation for a single file.
824 * <p>
825 * Oginal source code from http://kickjava.com/src/org/apache/lenya/util/SED.java.htm.
826 * <p>
827 * May fail on windows with error :
828 * The requested operation cannot be performed on a file with a user-mapped section open
829 *
830 * @param searchRegex Prefix which shall be replaced
831 * @param replace Prefix which is going to replace the original
832 * @param file File which sed shall be applied
833 * @param encoding charset encoding
834 * @throws IOException FIXME
835 * @since 1.1.2
836 */
837 public static void sed(String searchRegex,
838 String replace,
839 File file,
840 String encoding) throws IOException {
841
842 Pattern pattern = Pattern.compile(searchRegex);
843
844 String outString = null;
845 FileInputStream fis = new FileInputStream(file);
846 try {
847 // Open the file and then get a channel from the stream
848 FileChannel fc = fis.getChannel();
849
850 // Get the file's size and then map it into memory
851 int sz = (int) fc.size();
852 MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, sz);
853
854 // Decode the file into a char buffer
855 // Charset and decoder for encoding
856 Charset charset = Charset.forName(encoding);
857 CharsetDecoder decoder = charset.newDecoder();
858 CharBuffer cb = decoder.decode(bb);
859
860 Matcher matcher = pattern.matcher(cb);
861 outString = matcher.replaceAll(replace);
862 } finally {
863 fis.close();
864 }
865
866 if (outString != null) {
867 PrintStream ps = new PrintStream(new FileOutputStream(file));
868 try {
869 ps.print(outString);
870 } finally {
871 ps.close();
872 }
873 }
874 }
875
876 /**
877 * Java implementation for the unix sed command.
878 * <p>
879 * May fail on windows with error :
880 * The requested operation cannot be performed on a file with a user-mapped section open
881 *
882 * @param searchRegex regex to search into file
883 * @param replace string to replace matching patterns
884 * @param rootDirectory directory to search into
885 * @param fileRegex regex for file to find in {@code rootDirectory}
886 * @param encoding encoding to use
887 * @throws IOException FIXME
888 * @since 1.1.2
889 */
890 public static void sed(String searchRegex,
891 String replace,
892 File rootDirectory,
893 String fileRegex,
894 String encoding) throws IOException {
895 List<File> files = find(rootDirectory, fileRegex, true);
896 for (File file : files) {
897 sed(searchRegex, replace, file, encoding);
898 }
899 }
900
901 /**
902 * Java implementation for the unix sed command.
903 * <p>
904 * May fail on windows with error :
905 * The requested operation cannot be performed on a file with a user-mapped section open
906 *
907 * @param searchRegex regex to search into file
908 * @param replace string to replace matching patterns
909 * @param fileRegex regex for file to find in current dir
910 * @param encoding encoding to use
911 * @throws IOException FIXME
912 * @since 1.1.2
913 */
914 public static void sed(String searchRegex,
915 String replace,
916 String fileRegex,
917 String encoding) throws IOException {
918 sed(searchRegex, replace, new File("."), fileRegex, encoding);
919 }
920
921 /**
922 * Create the directory (and his parents) if necessary.
923 *
924 * @param dir the directory to create if not exisiting
925 * @return {@code true} if directory was created, {@code false} if was no
926 * need to create it
927 * @since 1.3.2
928 */
929 public static boolean createDirectoryIfNecessary(File dir) {
930 if (!dir.exists()) {
931 // do not throw exception if directory was created by another thread
932 return dir.mkdirs();
933 }
934 return false;
935 }
936
937 /**
938 * Obtain a file from the given {@code rootDirectory}, applying given paths.
939 * <p>
940 * For example with paths = a, b and c, then result is :
941 * <pre>
942 * root/a/b/c
943 * </pre>
944 *
945 * @param rootDirectory the root directory
946 * @param paths paths to apply
947 * @return the final file
948 * @since 2.2
949 */
950 public static File getFileFromPaths(File rootDirectory, String... paths) {
951 File result = rootDirectory;
952 for (String path : paths) {
953 result = new File(result, path);
954 }
955 return result;
956 }
957
958 /**
959 * Obtain a file fro the given {@code rootDirectory}, applying the fqn.
960 * <p>
961 * For example with fqn = a.b.c, the result is :
962 * <pre>
963 * root/a/b/c
964 * </pre>
965 *
966 * @param rootDirectory the root directory
967 * @param fqn fqn of searched file
968 * @return the final file
969 * @since 2.2
970 */
971 public static File getFileFromFQN(File rootDirectory, String fqn) {
972 String[] paths = fqn.split("\\.");
973
974 File result = getFileFromPaths(rootDirectory, paths);
975 return result;
976 }
977
978 /**
979 * Obtain a directory and creates it if required to place some test data.
980 * <p>
981 * The directory will be :
982 * <pre>
983 * java.io.tmpdir/testclassName.fqn/methodName[/classifier]/timestamp
984 * </pre>
985 *
986 * @param testClassName test class name
987 * @param methodName method name
988 * @param classifier optional classifier
989 * @param timestamp timestamp
990 * @return the computed and created if required directory.
991 * @since 2.6.10
992 */
993 public static File getTestSpecificDirectory(Class<?> testClassName,
994 String methodName,
995 String classifier,
996 long timestamp) {
997 File tempDirFile = SystemUtils.getJavaIoTmpDir();
998
999 // create the directory to store database data
1000 String dataBasePath = testClassName.getName()
1001 + File.separator // a directory with the test class name
1002 + methodName; // a sub-directory with the method name
1003
1004 if (StringUtils.isNotBlank(classifier)) {
1005 dataBasePath += classifier;
1006 }
1007 dataBasePath += '_'
1008 + timestamp; // and a timestamp
1009 File databaseFile = new File(tempDirFile, dataBasePath);
1010 return databaseFile;
1011 }
1012
1013 /**
1014 * Tests if a file is GZipped.
1015 *
1016 * @param file file to test
1017 * @return {@code true} if file is gzipped, {@code false} otherwise
1018 * @throws IOException if any io errors while reading file
1019 * @since 3.0
1020 */
1021 public static boolean isGzipFile(File file) throws IOException {
1022
1023 InputStream in = new BufferedInputStream(new FileInputStream(file));
1024
1025 try {
1026 boolean gzip = GZUtil.isGzipStream(in);
1027 in.close();
1028 return gzip;
1029 } finally {
1030 IOUtils.closeQuietly(in);
1031 }
1032
1033 }
1034
1035 } // FileUtil