org.codehaus.mojo.truezip.internal.TrueZipFileSetManager.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.mojo.truezip.internal.TrueZipFileSetManager.java

Source

package org.codehaus.mojo.truezip.internal;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.maven.shared.model.fileset.FileSet;
import org.apache.maven.shared.model.fileset.mappers.FileNameMapper;
import org.apache.maven.shared.model.fileset.mappers.MapperException;
import org.apache.maven.shared.model.fileset.mappers.MapperUtil;
import org.codehaus.mojo.truezip.TrueZipFileSet;

import de.schlichtherle.truezip.file.TFile;

/**
 * Provides operations for use with FileSet instances, such as retrieving the included/excluded files, deleting all
 * matching entries, etc. This is a fork of maven's shared FileSetManager with the following changes - Use
 * TrueZipDirectoryScanner instead DirectoryScanner - use java.io.File in isSymLink(); Note: symbolic support is
 * unsupported
 *
 * @author jdcasey
 * @version $Id$
 */
public class TrueZipFileSetManager {
    private static final String[] EMPTY_STRING_ARRAY = new String[0];

    // ----------------------------------------------------------------------
    // Constructors
    // ----------------------------------------------------------------------

    public TrueZipFileSetManager() {
    }

    // ----------------------------------------------------------------------
    // Public methods
    // ----------------------------------------------------------------------

    /**
     * @param fileSet
     * @return the included files as map
     * @throws MapperException if any
     * @see #getIncludedFiles(FileSet)
     */
    public Map mapIncludedFiles(TrueZipFileSet fileSet) throws MapperException {
        String[] sourcePaths = getIncludedFiles(fileSet);
        Map mappedPaths = new LinkedHashMap();

        FileNameMapper fileMapper = MapperUtil.getFileNameMapper(fileSet.getMapper());

        for (int i = 0; i < sourcePaths.length; i++) {
            String sourcePath = sourcePaths[i];

            String destPath;
            if (fileMapper != null) {
                destPath = fileMapper.mapFileName(sourcePath);
            } else {
                destPath = sourcePath;
            }

            mappedPaths.put(sourcePath, destPath);
        }

        return mappedPaths;
    }

    /**
     * Get all the filenames which have been included by the rules in this fileset.
     *
     * @param fileSet The fileset defining rules for inclusion/exclusion, and base directory.
     * @return the array of matching filenames, relative to the basedir of the file-set.
     */
    public String[] getIncludedFiles(TrueZipFileSet fileSet) {
        TrueZipDirectoryScanner scanner = scan(fileSet);

        if (scanner != null) {
            return scanner.getIncludedFiles();
        }

        return EMPTY_STRING_ARRAY;
    }

    /**
     * Get all the directory names which have been included by the rules in this fileset.
     *
     * @param fileSet The fileset defining rules for inclusion/exclusion, and base directory.
     * @return the array of matching dirnames, relative to the basedir of the file-set.
     */
    public String[] getIncludedDirectories(TrueZipFileSet fileSet) {
        TrueZipDirectoryScanner scanner = scan(fileSet);

        if (scanner != null) {
            return scanner.getIncludedDirectories();
        }

        return EMPTY_STRING_ARRAY;
    }

    /**
     * Get all the filenames which have been excluded by the rules in this fileset.
     *
     * @param fileSet The fileset defining rules for inclusion/exclusion, and base directory.
     * @return the array of non-matching filenames, relative to the basedir of the file-set.
     */
    public String[] getExcludedFiles(TrueZipFileSet fileSet) {
        TrueZipDirectoryScanner scanner = scan(fileSet);

        if (scanner != null) {
            return scanner.getExcludedFiles();
        }

        return EMPTY_STRING_ARRAY;
    }

    /**
     * Get all the directory names which have been excluded by the rules in this fileset.
     *
     * @param fileSet The fileset defining rules for inclusion/exclusion, and base directory.
     * @return the array of non-matching dirnames, relative to the basedir of the file-set.
     */
    public String[] getExcludedDirectories(TrueZipFileSet fileSet) {
        TrueZipDirectoryScanner scanner = scan(fileSet);

        if (scanner != null) {
            return scanner.getExcludedDirectories();
        }

        return EMPTY_STRING_ARRAY;
    }

    /**
     * Delete the matching files and directories for the given file-set definition.
     *
     * @param fileSet The file-set matching rules, along with search base directory
     * @throws IOException If a matching file cannot be deleted
     */
    public void delete(TrueZipFileSet fileSet) throws IOException {
        delete(fileSet, true);
    }

    /**
     * Delete the matching files and directories for the given file-set definition.
     *
     * @param fileSet The file-set matching rules, along with search base directory.
     * @param throwsError Throw IOException when errors have occurred by deleting files or directories.
     * @throws IOException If a matching file cannot be deleted and <code>throwsError=true</code>, otherwise print
     *             warning messages.
     */
    public void delete(TrueZipFileSet fileSet, boolean throwsError) throws IOException {
        Set deletablePaths = findDeletablePaths(fileSet);

        List warnMessages = new LinkedList();

        for (Iterator it = deletablePaths.iterator(); it.hasNext();) {
            String path = (String) it.next();

            TFile file = new TFile(fileSet.getDirectory(), path);

            if (file.exists()) {
                if (file.isDirectory()) {
                    if (fileSet.isFollowSymlinks() || !isSymlink(file)) {
                        removeDir(file, fileSet.isFollowSymlinks(), throwsError, warnMessages);
                    } else {

                        if (!file.delete()) {
                            String message = "Unable to delete symlink " + file.getAbsolutePath();
                            if (throwsError) {
                                throw new IOException(message);
                            }
                        }
                    }
                } else {
                    if (!delete(file)) {
                        String message = "Failed to delete file " + file.getAbsolutePath() + ". Reason is unknown.";
                        if (throwsError) {
                            throw new IOException(message);
                        }
                    }
                }
            }
        }

    }

    // ----------------------------------------------------------------------
    // Private methods
    // ----------------------------------------------------------------------

    private boolean isSymlink(TFile file) throws IOException {
        TFile fileInCanonicalParent = null;
        java.io.File parentDir = file.getParentFile(); // truezip-plugin specific change
        if (parentDir == null) {
            fileInCanonicalParent = file;
        } else {
            fileInCanonicalParent = new TFile(parentDir.getCanonicalPath(), file.getName());
        }
        return !fileInCanonicalParent.getCanonicalFile().equals(fileInCanonicalParent.getAbsoluteFile());

    }

    private Set findDeletablePaths(TrueZipFileSet fileSet) {
        Set includes = findDeletableDirectories(fileSet);
        includes.addAll(findDeletableFiles(fileSet, includes));

        return includes;
    }

    private Set findDeletableDirectories(TrueZipFileSet fileSet) {
        TrueZipDirectoryScanner scanner = scan(fileSet);

        if (scanner == null) {
            return Collections.EMPTY_SET;
        }

        Set includes = new HashSet(Arrays.asList(scanner.getIncludedDirectories()));
        Collection excludes = new ArrayList(Arrays.asList(scanner.getExcludedDirectories()));
        Collection linksForDeletion = new ArrayList();

        if (!fileSet.isFollowSymlinks()) {

            // we need to see which entries were only excluded because they're symlinks...
            scanner.setFollowSymlinks(true);
            scanner.scan();

            List includedDirsAndSymlinks = Arrays.asList(scanner.getIncludedDirectories());

            linksForDeletion.addAll(excludes);
            linksForDeletion.retainAll(includedDirsAndSymlinks);

            excludes.removeAll(includedDirsAndSymlinks);
        }

        excludeParentDirectoriesOfExcludedPaths(excludes, includes);

        includes.addAll(linksForDeletion);

        return includes;
    }

    private Set findDeletableFiles(TrueZipFileSet fileSet, Set deletableDirectories) {
        TrueZipDirectoryScanner scanner = scan(fileSet);

        if (scanner == null) {
            return deletableDirectories;
        }

        Set includes = deletableDirectories;
        includes.addAll(Arrays.asList(scanner.getIncludedFiles()));
        Collection excludes = new ArrayList(Arrays.asList(scanner.getExcludedFiles()));
        Collection linksForDeletion = new ArrayList();

        if (!fileSet.isFollowSymlinks()) {
            // we need to see which entries were only excluded because they're symlinks...
            scanner.setFollowSymlinks(true);
            scanner.scan();

            List includedFilesAndSymlinks = Arrays.asList(scanner.getIncludedFiles());

            linksForDeletion.addAll(excludes);
            linksForDeletion.retainAll(includedFilesAndSymlinks);

            excludes.removeAll(includedFilesAndSymlinks);
        }

        excludeParentDirectoriesOfExcludedPaths(excludes, includes);

        includes.addAll(linksForDeletion);

        return includes;
    }

    /**
     * Removes all parent directories of the already excluded files/directories from the given set of deletable
     * directories. I.e. if "subdir/excluded.txt" should not be deleted, "subdir" should be excluded from deletion, too.
     *
     * @param excludedPaths The relative paths of the files/directories which are excluded from deletion, must not be
     *            <code>null</code>.
     * @param deletablePaths The relative paths to files/directories which are scheduled for deletion, must not be
     *            <code>null</code>.
     */
    private void excludeParentDirectoriesOfExcludedPaths(Collection excludedPaths, Set deletablePaths) {
        for (Iterator it = excludedPaths.iterator(); it.hasNext();) {
            String path = (String) it.next();

            String parentPath = new TFile(path).getParent();

            while (parentPath != null) {
                boolean removed = deletablePaths.remove(parentPath);

                parentPath = new TFile(parentPath).getParent();
            }
        }

        if (!excludedPaths.isEmpty()) {
            boolean removed = deletablePaths.remove("");
        }
    }

    /**
     * Delete a directory
     *
     * @param dir the directory to delete
     * @param followSymlinks whether to follow symbolic links, or simply delete the link
     * @param throwsError Throw IOException when errors have occurred by deleting files or directories.
     * @param warnMessages A list of warning messages used when <code>throwsError=false</code>.
     * @throws IOException If a matching file cannot be deleted and <code>throwsError=true</code>.
     */
    private void removeDir(TFile dir, boolean followSymlinks, boolean throwsError, List warnMessages)
            throws IOException {
        String[] list = dir.list();
        if (list == null) {
            list = new String[0];
        }

        for (int i = 0; i < list.length; i++) {
            String s = list[i];
            TFile f = new TFile(dir, s);
            if (f.isDirectory() && (followSymlinks || !isSymlink(f))) {
                removeDir(f, followSymlinks, throwsError, warnMessages);
            } else {
                if (!delete(f)) {
                    String message = "Unable to delete file " + f.getAbsolutePath();
                    if (throwsError) {
                        throw new IOException(message);
                    }

                    if (!warnMessages.contains(message)) {
                        warnMessages.add(message);
                    }
                }
            }
        }

        if (!delete(dir)) {
            String message = "Unable to delete directory " + dir.getAbsolutePath();
            if (throwsError) {
                throw new IOException(message);
            }

        }
    }

    /**
     * Delete a file
     *
     * @param f a file
     */
    private boolean delete(TFile f) {
        try {
            FileUtils.forceDelete(f);
        } catch (IOException e) {
            return false;
        }

        return true;
    }

    private TrueZipDirectoryScanner scan(TrueZipFileSet fileSet) {
        TFile basedir = new TFile(fileSet.getDirectory());
        if (!basedir.exists() || !basedir.isDirectory()) {
            return null;
        }

        TrueZipDirectoryScanner scanner = new TrueZipDirectoryScanner();

        String[] includesArray = fileSet.getIncludesArray();
        String[] excludesArray = fileSet.getExcludesArray();

        if (includesArray.length > 0) {
            scanner.setIncludes(includesArray);
        }

        if (excludesArray.length > 0) {
            scanner.setExcludes(excludesArray);
        }

        if (fileSet.isUseDefaultExcludes()) {
            scanner.addDefaultExcludes();
        }

        scanner.setBasedir(basedir);
        scanner.setFollowSymlinks(fileSet.isFollowSymlinks());
        scanner.setFollowArchive(fileSet.isFollowArchive());

        scanner.scan();

        return scanner;
    }

}