de.awtools.basic.file.AWToolsFileUtils.java Source code

Java tutorial

Introduction

Here is the source code for de.awtools.basic.file.AWToolsFileUtils.java

Source

/*
 * $Id: AWToolsFileUtils.java 3725 2013-05-24 18:34:57Z andrewinkler $
 * ============================================================================
 * Project awtools-basic
 * Copyright (c) 2000-2011 by Andre Winkler. All rights reserved.
 * ============================================================================
 *          GNU LESSER GENERAL PUBLIC LICENSE
 *  TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package de.awtools.basic.file;

import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;

import de.awtools.basic.LoggerFactory;

/**
 * Ein paar Utility Methoden rund um Verzeichnisse und Dateien.
 * 
 * @version $LastChangedRevision: 3725 $ $LastChangedDate: 2013-05-24 20:34:57 +0200 (Fr, 24. Mai 2013) $
 * @author by Andre Winkler, $LastChangedBy: andrewinkler $
 */
public class AWToolsFileUtils {

    /** Der private Logger der Klasse. */
    private static final Logger log = LoggerFactory.make();

    /** Ein Verzeichnis rauf. */
    private static final String UP = "../";

    /** Das Trennerzeichen fr Verzeichnisse. */
    public static final String FILESEPARATOR = File.separator;

    /**
     * Mein eigener File-Seperator. Wird fr das normalisieren verwendet!
     */
    public static final char WINFILE_SEPEATOR_CHAR = '/';

    /**
     * Mein eigener File-Seperator. Wird fr das normalisieren verwendet!
     */
    public static final String WINFILE_SEPERATOR = "" + WINFILE_SEPEATOR_CHAR;

    /** Pattern zur Erkennung von absoluten Windows Root Pfaden. */
    public static final Pattern WINDOWS_ROOT_PATTERN = Pattern.compile("^[a-zA-Z]:");

    /** Utility Klassen werden nicht instanziert. */
    private AWToolsFileUtils() {
    }

    /**
     * Ldt einen InputStream aus dem Klassenpfad.
     *
     * @param resource Die zu ladende Resource.
     * @return Der InputStream.
     */
    public static InputStream classpathLoader(final String resource) {
        return AWToolsFileUtils.class.getClassLoader().getResourceAsStream(resource);
    }

    /**
     * Prft ob bergebener Dateibezeichner auf ein Verzeichnis verweist.
     *
     * @param dir Die zu prfende File-Id.
     * @return true, ist ein Verzeichnis; false, es ist kein Verzeichnis.
     */
    public static boolean isDirectory(final String dir) {
        if (!StringUtils.isBlank(dir)) {
            File outDir = new File(dir);
            return outDir.isDirectory();
        } else {
            return false;
        }
    }

    /**
     * Erzeugt eine URL auf eine Datei.
     *
     * @param dirName Das Verzeichnis.
     * @param fileName Die Datei relativ zu dem Verzeichnis.
     * @return Die generierte URL oder 'null' falls keine URL generiert werden
     *  konnte.
     */
    public static URL createURL(final String dirName, final String fileName) {
        StringBuilder file = new StringBuilder();
        file.append("file:");
        file.append(dirName);
        file.append(System.getProperty("file.separator"));
        file.append(fileName);

        return createURL(file.toString());
    }

    /**
     * Erzeugt eine URL auf eine Datei.
     *
     * @param fileName Ein Dateiname.
     * @return Die generierte URL oder 'null' falls keine URL generiert werden
     *  konnte.
     */
    public static URL createURL(final String fileName) {
        URL url;
        try {
            url = new URL(fileName);
        } catch (MalformedURLException ex) {
            log.debug("Catched an MalformedURLException", ex);
            url = null;
        }
        return url;
    }

    /**
     * Erzeugt die ntigen Verzeichnisse unterhalb von <code>basePath</code>
     * um eine Datei <code>relativeFileName</code> anlegen zu knnen. Die Datei
     * selbst wird nicht angelegt, es wird aber dafr gesorgt, dass alle
     * notwendigen Verzeichnisse fr diese Datei angelegt werden.<br>
     * Im Gegensatz zu der Methode <code>FileUtils#forceMkdir(File)</code> in
     * <code>IOUtils</code> aus jakarta-commons wird der letzte Pfadeintrag,
     * z.B. C:/tmp/www/web.xml nicht als Verzeichnis angelegt. Im Beispiel
     * wre <code>web.xml</code> tatschlich als Verzeichnis angelegt worden.
     *
     * @param basePath Ein Basisverzeichnis.
     * @param relativeFileName Ein relativer Filename (ab Basis).
     */
    public static void createFilePath(final String basePath, final String relativeFileName) {

        if (StringUtils.contains(relativeFileName, '\\')) {
            createFilePath(basePath, relativeFileName, '\\');
        } else if (StringUtils.contains(relativeFileName, '/')) {
            createFilePath(basePath, relativeFileName, '/');
        } else {
            log.debug("Directory creation not necessary!");
        }
    }

    /**
     * Untersttzt die {@link #createFilePath(String, String)} bei ihrer
     * Arbeit.
     *
     * @param basePath Ein Basisverzeichnis.
     * @param relativeFileName Ein relativer Filename (ab Basis).
     * @param separator Der Separator fr Verzeichnisse.
     */
    private static void createFilePath(final String basePath, final String relativeFileName, final char separator) {

        StringBuilder path = new StringBuilder(basePath);

        // Alle mglichen Verzeichnisse/Unterverzeichnisse ermitteln ...
        String[] split = StringUtils.split(relativeFileName, separator);

        // ... und anlegen. Der letzte Eintrag bezeichnet die eigentliche Datei.
        for (int i = 0, max = split.length - 1; i < max; i++) {
            if ((!path.toString().endsWith("/")) && (!path.toString().endsWith("\\"))) {
                path.append(AWToolsFileUtils.FILESEPARATOR);
            }
            path.append(split[i]);

            File newDirFile = new File(path.toString());
            if (!newDirFile.exists()) {
                if (log.isDebugEnabled()) {
                    log.info("mkdir " + path);
                }
                newDirFile.mkdirs();
            }
        }
    }

    /**
     * Berechnet die Anzahl der Verzeichnisstufen bis
     * zum gesuchten Dateinamen. Die Angabe kann fiktiv sein, d.h.
     * mussen nicht notwendiger Weise im Dateisystem vorliegen.
     * Z.B. Der Parameter "./www/dir/dir1/index.html" liefert 3 zurck.
     *
     * @param fileName Die gesuchte Datei.
     * @return Anzahl der Verzeichnistiefen.
     */
    public static int countDirLevel(final String fileName) {
        // Normalisieren und ...
        String newFileName = AWToolsFileUtils.normalizePath(fileName);

        /*
        // ... nur noch die '/' zhlen und fertig (ohne Root!)
        int correction = 0;
            
        if (fileName.startsWith(WINFILE_SEPERATOR)) {
        correction = 1;
        } else if (WINDOWS_ROOT_PATTERN.matcher(newFileName).find()) {
        correction = 1;
        }
        */
        //return (StringUtils.countMatches(newFileName, "/") - correction);
        return (StringUtils.countMatches(newFileName, "/") - 1);
    }

    /**
     * Erstellt einen relativen Pfad anhand der bergebenen Level-Nummer.
     *
     * @param level Die Level-Nummer.
     * @return Der generierte relative Pfad.
     */
    public static String pathToRoot(final int level) {
        StringBuilder buf = new StringBuilder("./");
        for (int i = 0; i < level; i++) {
            buf.append(UP);
        }

        return buf.toString();
    }

    /**
     * Erstellt einen relativen Pfad, der zur Wurzel fhrt. Z.B.
     * <code>test/winkler/andre.txt</code> generiert den Pfad
     * <code>./../../</code>.
     *
     * @param fileName Die gesuchte Datei.
     * @return Ein relativer Pfad zur Wurzel.
     */
    public static String pathToRoot(final String fileName) {
        return AWToolsFileUtils.pathToRoot(AWToolsFileUtils.countDirLevel(fileName));
    }

    /**
     * Siehe die Beschreibung in Methode
     * {@link #findFiles(java.io.File, java.lang.String, java.lang.String)}.
     *
     * @param basePath Basisverzeichnis.
     * @param fileName Die gesuchte Datei.
     * @return Eine Liste der gefundenen Dateien.
     *
     * @see #findFiles(java.io.File, java.lang.String, java.lang.String)
     */
    public static List<File> findFiles(final File basePath, final String fileName) {

        String relativePath = "";
        String realFileName = fileName;

        if ((StringUtils.contains(fileName, "/"))) {
            relativePath = StringUtils.substringBeforeLast(fileName, "/");
            realFileName = StringUtils.substringAfterLast(fileName, "/");
        }

        if (log.isDebugEnabled()) {
            log.debug("basePath ......: " + basePath);
            log.debug("relativePath ..: " + relativePath);
            log.debug("realFileName ..: " + realFileName);
        }

        return AWToolsFileUtils.findFiles(basePath, relativePath, realFileName);
    }

    /**
     * Sucht nach allen Dateien, die auf einem Verzeichnispfad liegen
     * und die die Bezeichnung 'fileName' besitzen. Der vorgegebene
     * Verzeichnistrenner ist der '/'. Beispiel:<br/>
     * <pre>
     *  BASE = /temp/readme.txt
     *         /temp/dir1/readme.txt
     *         /temp/dir11/
     *         /temp/dir11/dir121/readme.txt.
     * </pre>
     * Gesucht wird der <code>fileName = readme.txt</code>, der zu
     * untersuchende Pfadabschnitt lautet <code>dir11/dir121/</code> und
     * <code>basePath = /temp</code>, dann wird folgende Liste zurck
     * geliefert: <code>/temp/readme.txt</code>,
     * <code>/temp/dir1/readme.txt</code>,
     * <code>/temp/dir1/dir121/readme.txt</code>.  
     *
     * @param basePath Basisverzeichnis.
     * @param relativePath Der relative Pfad - zum Basisverzeichnis - zu der
     *  gesuchten Datei.
     * @param fileName Die gesuchte Datei.
     * @return Eine Liste der Dateien, die den selben Dateibezeichner haben
     *  wie im <code>fileName</code> und auf dem Pfad zwischen Basisverzeichnis
     *  und der gesuchten Datei liegen.
     */
    public static List<File> findFiles(final File basePath, final String relativePath, final String fileName) {

        List<File> filesWithSameName = new ArrayList<>();

        File fileInBase = new File(basePath, fileName);
        if (fileInBase.exists()) {
            filesWithSameName.add(fileInBase);
        }

        String newRelativePath = AWToolsFileUtils.normalizePath(relativePath);
        String[] split = StringUtils.split(newRelativePath, "/");

        StringBuilder intermediateDir = new StringBuilder();
        for (int i = 0; i < split.length; i++) {
            intermediateDir.append(split[i]).append(File.separatorChar);

            File currentDir = new File(basePath, intermediateDir.toString());
            File checkMyExistence = new File(currentDir, fileName);

            if (checkMyExistence.exists()) {
                filesWithSameName.add(checkMyExistence);
            }
        }

        return filesWithSameName;
    }

    /**
     * Normalisiert einen Pfadausdruck. D.h. aus den Windows-Trenner werden
     * Unix-Trenner, aus /./ wird /. Falls / vorne fehlt, wird dieser
     * vorangestellt. Doppelte // werden durch / ersetzt. Windows
     * Laufwerksbezeichner werden eliminiert.<br/>
     *
     * <b>ACHTUNG:</b> URL wie file://c:/temp werden nicht korrekt verarbeitet,
     * da das // ebenfalls durch / ersetzt wird. 
     *
     * @param fileName Der zu normalisierende Dateiname.
     * @return Der normalisierte Dateiname.
     */
    public static String normalizePath(final String fileName) {
        // Alle Windows Trenner durch Unix Trenner ersetzen.
        String newFileName = StringUtils.replace(fileName, "\\", WINFILE_SEPERATOR);

        // Alle // durch / ersetzen.
        newFileName = StringUtils.replace(newFileName, "//", WINFILE_SEPERATOR);
        newFileName = StringUtils.replace(newFileName, "///", WINFILE_SEPERATOR);

        // Alle /./ durch / ersetzen. 
        newFileName = StringUtils.replace(newFileName, "/./", WINFILE_SEPERATOR);

        // Startsymbole normalisieren.
        if (newFileName.startsWith("./")) {
            newFileName = StringUtils.replace(newFileName, ".", "", 1);
        }

        if (WINDOWS_ROOT_PATTERN.matcher(newFileName).find()) {
            newFileName = newFileName.substring(2);
        }

        if (!newFileName.startsWith(WINFILE_SEPERATOR)) {
            newFileName = WINFILE_SEPERATOR + newFileName;
        }

        // / Endsymbol bei Verzeichnissen entfernen
        if (newFileName.endsWith(WINFILE_SEPERATOR)) {
            newFileName = StringUtils.removeEnd(newFileName, WINFILE_SEPERATOR);
        }

        return newFileName;
    }

    /**
     * Extrahiert aus einem Dateibezeichner den Verzeichnispfad. Beispiel:<br/>
     * Der Parameter <code>temp/ab/db/text.txt</code> fhrt zu der Rckgabe
     * <code>/temp/ab/db</code>. hnliches versucht die Methode
     * <code>File.getParent()</code>, kann aber z.B. die zwei Pfade
     * 'temp/test.txt' und '/temp/test.txt' nicht auf Gleichheit prfen.
     *
     * @param fileName Der Dateibezeichner dessen Verzeichnispfad ermittelt
     *  werden soll.
     * @return Der Verzeichnispfad.
     */
    public static String getParent(final String fileName) {
        String tmp = AWToolsFileUtils.normalizePath(fileName);
        if (StringUtils.contains(tmp, "/")) {
            return StringUtils.substringBeforeLast(tmp, "/");
        } else {
            return "";
        }
    }

    /**
     * Entfernt aus einem Dateibezeichner alle Pfadangaben.
     * 
     * @param fileName Der zu untersuchende Dateibezeichner. 
     * @return Liefert den Dateibezeichner ohne Pfadangaben.
     */
    public static String getFileName(final String fileName) {
        String tmp = AWToolsFileUtils.normalizePath(fileName);
        if (StringUtils.contains(tmp, "/")) {
            return StringUtils.substringAfterLast(tmp, "/");
        } else {
            return tmp;
        }
    }

}