1 /*
2 * $Id: GHFileUtils.java 2993 2011-11-24 19:51:48Z andrewinkler $
3 * ============================================================================
4 * Project gluehloch-homepage-core
5 * Copyright (c) 2004-2007 by Andre Winkler. All rights reserved.
6 * ============================================================================
7 * GNU LESSER GENERAL PUBLIC LICENSE
8 * TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
9 *
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
14 *
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
19 *
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 *
24 */
25
26 package de.awtools.homegen.directory.utils;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.Collections;
33 import java.util.Comparator;
34 import java.util.List;
35 import java.util.Map;
36
37 import org.apache.commons.io.FileUtils;
38 import org.apache.commons.io.filefilter.SuffixFileFilter;
39 import org.apache.commons.lang.StringUtils;
40
41 import de.awtools.basic.file.AWToolsFileUtils;
42 import de.awtools.homegen.TransformerException;
43 import de.awtools.homegen.directory.GHBuilder;
44 import de.awtools.homegen.directory.GHDirectory;
45 import de.awtools.homegen.directory.GHFile;
46
47 /**
48 * Verschiedene Utilities für das Suchen von Dateien in Verzeichnissen etc.
49 *
50 * @version $LastChangedRevision: 2993 $ $LastChangedDate: 2011-11-24 20:51:48 +0100 (Thu, 24 Nov 2011) $
51 * @author by Andre Winkler, $LastChangedBy: andrewinkler $
52 */
53 public class GHFileUtils {
54
55 /**
56 * Prüft, ob der übergebene File-Parameter ein Verzeichnis und lesbar ist.
57 *
58 * @param dir Das zu prüfende <code>File</code> Objekt.
59 */
60 public static void checkDirectory(final File dir) {
61 if (!dir.canRead()) {
62 throw new TransformerException(dir.getPath() + " is not readable!");
63 }
64 if (!dir.isDirectory()) {
65 throw new TransformerException(dir.getPath()
66 + " is not a directory!");
67 }
68 }
69
70 /**
71 * Prüft, ob der übergebene File-Parameter eine Datei und lesbar ist.
72 *
73 * @param file Das zu prüfende <code>File</code> Objekt.
74 */
75 public static void checkFile(final File file) {
76 if (!file.canRead()) {
77 throw new TransformerException(file.getPath() + " is not readable!");
78 }
79 if (file.isDirectory()) {
80 throw new TransformerException(file.getPath() + " is a directory!");
81 }
82 }
83
84 /**
85 * Sucht nach einem <code>file</code> in dem Verzeichnis
86 * <code>directory</code>. Das übergebene und das eventuell gefundenen
87 * {@link GHFile} Objekt müssen nicht im Sinne von <code>==</code> gleich
88 * sein. Diese Methode ist sinnvoll bei Verwendung von zwei
89 * unterschiedlichen Root-Verzeichnissen, die eine ähnliche Unterstruktur
90 * besitzen.
91 *
92 * @param file Die zu suchende Datei.
93 * @param directory Das Verzeichnis und deren Unterverzeichnisse die
94 * untersucht werden.
95 * @return Die gefundene Datei oder <code>null</code> wenn nichts gefunden.
96 */
97 public static GHFile findFile(final GHFile file, final GHDirectory directory) {
98 GHFile foundedFile = null;
99 List<GHFile> files = directory.getAllFiles();
100 int index = files.indexOf(file);
101 if (index != -1) {
102 foundedFile = files.get(index);
103 }
104 return foundedFile;
105 }
106
107 /**
108 * Siehe dazu in der Methode {@link #findFile(GHFile, GHDirectory)}. Die
109 * Datei-Extension wird bei der Suche ignoriert. Die Dateien müssen nur
110 * gleich im Namen sein.
111 *
112 * @param file Die zu suchende Datei.
113 * @param directory Das Verzeichnis und deren Unterverzeichnisse die
114 * untersucht werden.
115 * @param expectedExtension Die erwartete Erweiterung.
116 * @return Die gefundene Datei oder <code>null</code> wenn nichts gefunden.
117 */
118 public static GHFile findFileIgnoreExtension(final GHFile file,
119 final GHDirectory directory, final String expectedExtension) {
120
121 GHFile foundedFile = null;
122 List<GHFile> files = directory.getAllFiles();
123 for (GHFile currFile : files) {
124 if (currFile.getNameWithoutExtension().equals(
125 file.getNameWithoutExtension())
126 && currFile.getExtension().equals(expectedExtension)) {
127
128 foundedFile = currFile;
129 break;
130 }
131 }
132 return foundedFile;
133 }
134
135 /**
136 * Kopiert eine Datei <code>file</code> in das Verzeichnis
137 * <code>directory</code>. Den relativen Pfad den die Datei
138 * <code>file</code> im alten {@link GHDirectory} besaß, wird sie im
139 * neuen <code>director</code> beibehalten.
140 *
141 * @param file Die zu kopierende Datei.
142 * @param target Das Verzeichnis im dem die Datei kopiert werden soll.
143 * @param force Wenn <code>false</code> wird die Kopieraktion nur dann
144 * ausgeführt, wenn im Target-Verzeichnis die Datei noch nicht vorhanden
145 * ist, oder die zu kopierende Datei ein jüngeres Datum als die
146 * Datei im Target-Verzeichnis besitzt.
147 * @param builder Erstellt das passende {@link GHDirectory} Objekt.
148 * @return Liefert <code>true</code> zurück, wenn die Datei kopiert wurde.
149 * In allen anderen Fällen wird <code>false</code> zurück geliefert.
150 */
151 public static boolean copyFile(final GHFile file, final GHDirectory target,
152 final boolean force, final GHBuilder builder) {
153
154 boolean result = false;
155 List<GHDirectory> path = file.getDirectoryPath();
156 GHDirectory targetDirectory = GHFileUtils.createPath(path, target,
157 builder);
158
159 GHFile targetFile = targetDirectory.getFile(file.getName());
160 if (targetFile == null) {
161 try {
162 FileUtils.copyFileToDirectory(file.getFile(),
163 targetDirectory.getFile());
164 } catch (IOException ex) {
165 throw new TransformerException(ex);
166 }
167 GHFile newFile = builder.buildFile(
168 new File(targetDirectory.getFile(), file.getName()),
169 targetDirectory, file.getName());
170 targetDirectory.addFile(newFile);
171 result = true;
172 } else if (file.isNewer(targetFile) || force) {
173 try {
174 FileUtils.copyFile(file.getFile(), targetFile.getFile());
175 } catch (IOException ex) {
176 throw new TransformerException(ex);
177 }
178 result = true;
179 }
180
181 return result;
182 }
183
184 /**
185 * Erstellt den übergebenen Verzeichnispfad im Target-Verzeichnis. Die
186 * etwaigen Dateien die im übergebenen Verzeichnispfad hängen, werden nicht
187 * angelegt.
188 *
189 * @param path Der anzulegende Pfad.
190 * @param target Das Target-Root-Verzeichnis. In diesem werden ggf. die
191 * fehlenden Pfade nachgetragen.
192 * @param builder Erstellt das passende {@link GHDirectory} Objekt.
193 * @return Das letzte angelegte Verzeichnis.
194 */
195 public static GHDirectory createPath(final List<GHDirectory> path,
196 final GHDirectory target, final GHBuilder builder) {
197
198 // Das erste Verzeichnis in der Liste muss das Wurzelverzeichnis sein.
199 if (!path.get(0).isRootDir()) {
200 throw new IllegalArgumentException(
201 "path must start with a root dir!");
202 }
203
204 // Wenn die Liste nur aus einem Element besteht, dann können wir
205 // bereits jetzt aufhören. Das Root-Verzeichnis existiert auch
206 // im Target-Verzeichnis immer!
207 if (path.size() < 2) {
208 return target;
209 }
210
211 GHDirectory currentDir = target;
212 for (int index = 1; index < path.size(); index++) {
213 String dirName = path.get(index).getName();
214 GHDirectory dir = currentDir.getDirectory(dirName);
215 if (dir == null) {
216 File newDir = new File(currentDir.getFile(), dirName);
217 newDir.mkdir();
218 dir = builder.buildDirectory(currentDir, newDir, dirName);
219 }
220 currentDir = dir;
221 }
222
223 return currentDir;
224 }
225
226 // -- Helper Methods ------------------------------------------------------
227
228 /**
229 * Entfernt aus einem Dateibezeichner den Basispfad. Zum Beispiel:
230 * Basis=<code>C:\temp\test</code> und
231 * Dateiname=<code>C:\temp\test\dir\subdir\test.txt</code>. Dann
232 * liefert die Methode <code>dir/subdir/test.txt</code> zurück.
233 *
234 * @param baseDir Das Basisverzeichnis.
235 * @param fileName Die Datei.
236 * @return Ein relative Pfad zum Basisverzeichnis.
237 *
238 * @todo Eventuell nach winutils in die Klasse FileUtils auslagern.
239 */
240 public static String removeBaseDir(final File baseDir, final File fileName) {
241 try {
242 return (removeBaseDir(baseDir.getCanonicalPath(),
243 fileName.getCanonicalPath()));
244 } catch (IOException ex) {
245 throw new TransformerException(ex);
246 }
247 }
248
249 /**
250 * Entfernt aus einem Dateibezeichner den Basispfad. Zum Beispiel:
251 * Basis=<code>C:\temp\test</code> und
252 * Dateiname=<code>C:\temp\test\dir\subdir\test.txt</code>. Dann
253 * liefert die Methode <code>dir/subdir/test.txt</code> zurück.<br>
254 * <strong>HINWEIS:</strong> Die Pfad- und Dateiangaben müssen in
255 * kanonischer Form vorliegen. Das Endergebnis der Operation wird
256 * normalisiert! Siehe dazu
257 * {@link WinFileUtils#normalizePath(java.lang.String)}.
258 *
259 * @param baseDir Das Basisverzeichnis.
260 * @param fileName Die Datei.
261 * @return Ein relativer Pfad zum Basisverzeichnis.
262 *
263 * @todo Eventuell nach winutils in die Klasse FileUtils auslagern.
264 */
265 public static String removeBaseDir(final String baseDir,
266 final String fileName) {
267
268 String right = StringUtils.substringAfterLast(fileName, baseDir);
269 return (AWToolsFileUtils.normalizePath(right));
270 }
271
272 /**
273 * Liefert eine Liste von Files aus dem übergebenen Verzeichnis.
274 *
275 * @param directory Das zu untersuchende Verzeichnis.
276 * @param suffixFileFilter Ein Filter. Nur diese Dateien landen in der
277 * Ergebnisliste.
278 * @return Die Files in diesem Verzeichnis.
279 *
280 * @see #findFiles(File)
281 */
282 @SuppressWarnings("unchecked")
283 public static List<File> findFiles(final File directory,
284 final SuffixFileFilter suffixFileFilter) {
285
286 List<File> results = new ArrayList<File>();
287 Collection<File> files = FileUtils.listFiles(directory,
288 suffixFileFilter, null);
289
290 for (File file : files) {
291 if (file.isFile()) {
292 results.add(file);
293 }
294 }
295
296 sortFiles(results);
297 return results;
298 }
299
300 /**
301 * Liefert eine Liste von Files aus dem übergebenen Verzeichnis.
302 *
303 * @param directory Das Ausgangsverzeichnis.
304 * @return Die gefundenen Dateien.
305 *
306 * @see #findFiles(File, SuffixFileFilter)
307 *
308 * @todo Eventuell nach winutils in die Klasse FileUtils auslagern.
309 */
310 public static List<File> findFiles(final File directory) {
311 List<File> results = new ArrayList<File>();
312 File[] files = directory.listFiles();
313 if (files != null) {
314 for (int index = 0; index < files.length; index++) {
315 File file = files[index];
316 if (file.isFile()) {
317 results.add(file);
318 }
319 }
320 }
321
322 sortFiles(results);
323 return results;
324 }
325
326 /**
327 * Wird von der Methode {@link #findDeepDirectories(File)} verwendet und
328 * ermittelt alle Verzeichnisse unterhalb des gesuchten Verzeichnisses.
329 *
330 * @param directory Das Ausgangsverzeichnis.
331 * @return Die gefundenen Unterverzeichnisse.
332 *
333 * @todo Eventuell nach winutils in die Klasse FileUtils auslagern.
334 */
335 public static List<File> findDirectories(final File directory) {
336 List<File> results = new ArrayList<File>();
337 File[] files = directory.listFiles();
338 if (files != null) {
339 for (int index = 0; index < files.length; index++) {
340 File file = files[index];
341 if (file.isDirectory()) {
342 results.add(file);
343 }
344 }
345 }
346
347 sortFiles(results);
348 return results;
349 }
350
351 private static void sortFiles(List<File> files) {
352 Collections.sort(files, new Comparator<File>() {
353 @Override
354 public int compare(File file1, File file2) {
355 return file1.getName().compareTo(file2.getName());
356 }
357 });
358 }
359
360 /**
361 * Fischt aus einem Verzeichnis alle Unterverzeichnisse heraus.
362 *
363 * @param directory Das zu untersuchende Verzeichnis.
364 * @return Die gefundenen Unterverzeichnisse.
365 *
366 * @todo Eventuell nach winutils in die Klasse FileUtils auslagern.
367 */
368 public static List<File> findDeepDirectories(final File directory) {
369 List<File> directoryFiles = new ArrayList<File>();
370 findDeepDirectories(directoryFiles, directory);
371 return directoryFiles;
372 }
373
374 /**
375 * Wird von der Methode {@link #findDeepDirectories(File)} verwendet und
376 * verwendet einen rekursiven Abstieg zur Ermittlung aller Verzeichnisse.
377 *
378 * @param results Die ermittelten Unterverzeichnisse.
379 * @param directory Das Ausgangsverzeichnis.
380 *
381 * @todo Eventuell nach winutils in die Klasse FileUtils auslagern.
382 */
383 private static void findDeepDirectories(final List<File> results,
384 final File directory) {
385
386 File[] files = directory.listFiles();
387 for (int index = 0; index < files.length; index++) {
388 File file = files[index];
389 if (file.isDirectory()) {
390 results.add(file);
391 findDeepDirectories(results, file);
392 }
393 }
394 }
395
396 /**
397 * Ersetzt die Tokens '@...@' in der übergebenen Datei. Das Datum
398 * 'lastModified' wird dabei nicht verändert. Nach der Filter-Operations
399 * wird dieses wieder auf den alten Wert gesetzt.
400 *
401 * @param source Die zu untersuchende Datei.
402 * @param tokens Die Tokens.
403 * @param encoding Sollte i.d.R. UTF-8 oder ISO-8859-1 sein.
404 */
405 public static void replaceInFile(final File source,
406 final Map<String, String> tokens, final String encoding) {
407
408 try {
409 long lastModified = source.lastModified();
410 String text = FileUtils.readFileToString(source, encoding);
411 String result = GHFileUtils.replace(text, tokens, encoding);
412 FileUtils.writeStringToFile(source, result, encoding);
413 source.setLastModified(lastModified);
414 } catch (IOException ex) {
415 throw new TransformerException(ex);
416 }
417 }
418
419 /**
420 * Ersetzt alle @...@ Ausdrücke durch die entsprechenden Werte.
421 *
422 * @param text Der Text
423 * @param tokens Die Tokens mit der Ersetzung.
424 * @param encoding Sollte i.d.R. UTF-8 oder ISO-8859-1 sein.
425 * @return Das Endergebnis.
426 */
427 public static String replace(final String text,
428 final Map<String, String> tokens, final String encoding) {
429
430 String result = text;
431 for (String key : tokens.keySet()) {
432 String value = tokens.get(key);
433 StringBuilder replaceToken = new StringBuilder("@");
434 replaceToken.append(key).append("@");
435 result = StringUtils
436 .replace(result, replaceToken.toString(), value);
437 }
438 return result;
439 }
440
441 }