org.codehaus.mojo.rpm.FileHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.codehaus.mojo.rpm.FileHelper.java

Source

package org.codehaus.mojo.rpm;

/*
 * 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.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.archiver.ArchiveEntry;
import org.codehaus.plexus.archiver.util.DefaultFileSet;
import org.codehaus.plexus.util.StringUtils;

/**
 * Utility to interact with files (source, dependencies, artifacts, etc.).
 *
 * @author Brett Okken
 * @since 2.0
 */
final class FileHelper {
    private static final List<String> EMPTY_STRING_LIST = new ArrayList<String>();

    /**
     * Message for exception indicating that a {@link Source} has a {@link Source#getDestination() destination}, but
     * refers to a {@link File#isDirectory() directory}.
     */
    private static final String DESTINATION_DIRECTORY_ERROR_MSG = "Source has a destination [{0}], but the location [{1}] does not refer to a file.";

    /**
     * {@code Pattern} to identify macros.
     *
     * @since 2.1-alpha-1
     */
    private static final Pattern MACRO_PATTERN = Pattern.compile("%\\{([^}]*)\\}");

    /**
     * A Plexus component to copy files and directories. Using our own custom version of the DirectoryArchiver to allow
     * filtering of files.
     */
    private final FilteringDirectoryArchiver copier;

    private final AbstractRPMMojo mojo;

    /**
     * @param mojo
     * @param copier
     */
    public FileHelper(AbstractRPMMojo mojo, FilteringDirectoryArchiver copier) {
        super();
        this.mojo = mojo;
        this.copier = copier;
    }

    /**
     * Copy the files from the various mapping sources into the build root.
     *
     * @throws MojoExecutionException if a problem occurs
     * @throws MojoFailureException
     */
    public void installFiles() throws MojoExecutionException, MojoFailureException {
        final File workarea = mojo.getWorkarea();
        final File buildroot = mojo.getBuildroot();

        final File icon = mojo.getIcon();
        // Copy icon, if specified
        if (icon != null) {
            File icondest = new File(workarea, "SOURCES");
            copySource(icon, null, icondest, null, null, false, false);
        }

        final Log log = mojo.getLog();

        // Process each mapping
        for (Mapping map : mojo.getMappings()) {
            final String destinationString = map.getDestination();
            final String macroEvaluatedDestination = evaluateMacros(destinationString);

            File dest = new File(buildroot, macroEvaluatedDestination);
            map.setAbsoluteDestination(dest);

            if (map.isDirOnly()) {
                // Build the output directory if it doesn't exist
                if (!dest.exists()) {
                    log.info("Creating empty directory " + dest.getAbsolutePath());
                    if (!dest.mkdirs()) {
                        throw new MojoExecutionException("Unable to create " + dest.getAbsolutePath());
                    }
                }
            } else {
                processSources(map, dest);

                ArtifactMap art = map.getArtifact();
                if (art != null) {
                    List<Artifact> artlist = selectArtifacts(art);
                    for (Artifact artifactInstance : artlist) {
                        copyArtifact(artifactInstance, dest, false);
                        map.addCopiedFileNameRelativeToDestination(artifactInstance.getFile().getName());
                    }
                }

                Dependency dep = map.getDependency();
                if (dep != null) {
                    List<Artifact> deplist = selectDependencies(dep);
                    for (Artifact artifactInstance : deplist) {
                        // pass in dependency stripVersion parameter
                        String outputFileName = copyArtifact(artifactInstance, dest, dep.getStripVersion());
                        map.addCopiedFileNameRelativeToDestination(outputFileName);
                    }
                }

                if (map.getCopiedFileNamesRelativeToDestination().isEmpty()) {
                    log.info("Mapping empty with destination: " + dest.getName());
                    // Build the output directory if it doesn't exist
                    if (!dest.exists()) {
                        log.info("Creating empty directory " + dest.getAbsolutePath());
                        if (!dest.mkdirs()) {
                            throw new MojoExecutionException("Unable to create " + dest.getAbsolutePath());
                        }
                    }
                }
            }
        }
    }

    /**
     * Copy a set of files.
     *
     * @param src The file or directory to start copying from
     * @param srcName The src file name to be used in the copy, only used if the src is not a directory.
     * @param dest The destination directory
     * @param incl The list of inclusions
     * @param excl The list of exclusions
     * @param filter Indicates if the file(s) being copied should be filtered.
     * @return List of file names, relative to <i>dest</i>, copied to <i>dest</i>.
     * @throws MojoExecutionException if a problem occurs
     */
    private List<String> copySource(File src, String srcName, File dest, List<String> incl, List<String> excl,
            boolean filter, boolean dontUseDefaultExcludes) throws MojoExecutionException {
        try {
            // Set the destination
            copier.setDestFile(dest);

            // Set the source
            if (src.isDirectory()) {
                String[] ia = null;
                if (incl != null) {
                    ia = incl.toArray(new String[0]);
                }

                String[] ea = null;
                if (excl != null) {
                    ea = excl.toArray(new String[0]);
                }

                // same as copier.addDirectory( src, "", ia, ea ) with dontUseDefaultExcludes
                DefaultFileSet fileset = new DefaultFileSet(src);
                fileset.setPrefix("");
                fileset.setIncludes(ia);
                fileset.setExcludes(ea);
                fileset.setIncludingEmptyDirectories(copier.getIncludeEmptyDirs());
                fileset.setUsingDefaultExcludes(!dontUseDefaultExcludes);

                copier.addFileSet(fileset);
            } else {
                // set srcName to default if null
                srcName = srcName != null ? srcName : src.getName();
                copier.addFile(src, srcName);
            }

            copier.setFilter(filter);
            copier.setFilterWrappers(mojo.getFilterWrappers());

            Map<String, ArchiveEntry> copiedFilesMap = copier.getFiles();

            // Perform the copy
            copier.createArchive();

            List<String> copiedFiles = new ArrayList<String>(copiedFilesMap.size());
            for (String key : copiedFilesMap.keySet()) {
                if (key != null && key.length() > 0) {
                    copiedFiles.add(key);
                }
            }

            // Clear the list for the next mapping
            copier.resetArchiver();

            return copiedFiles;
        } catch (Throwable t) {
            throw new MojoExecutionException("Unable to copy files for packaging: " + t.getMessage(), t);
        }
    }

    public static DefaultFileSet fileSet(File directory) {
        final DefaultFileSet defaultFileSet = new DefaultFileSet(directory);
        return defaultFileSet;
    }

    /**
     * Copy an artifact.
     *
     * @param art The artifact to copy
     * @param dest The destination directory
     * @param stripVersion Whether or not to strip the artifact version from the filename
     * @return Artifact file name
     * @throws MojoExecutionException if a problem occurs
     */
    private String copyArtifact(Artifact art, File dest, boolean stripVersion) throws MojoExecutionException {
        if (art.getFile() == null) {
            final Log log = mojo.getLog();
            log.warn("Artifact " + art + " requested in configuration.");
            log.warn("Plugin must be run in Maven's build lifecycle for this to work.");
            throw new MojoExecutionException("Unable to resolve artifact.");
        }

        String outputFileName;
        if (stripVersion) {
            final String classifier = art.getClassifier();
            // strip the version from the file name
            outputFileName = art.getArtifactId();
            if (classifier != null) {
                outputFileName += '-';
                outputFileName += classifier;
            }
            outputFileName += '.';
            outputFileName += art.getType();
        } else {
            outputFileName = art.getFile().getName();
        }

        copySource(art.getFile(), outputFileName, dest, null, null, false, false);
        return outputFileName;
    }

    /**
     * Make a list of the artifacts to package in this mapping.
     *
     * @param am The artifact mapping information
     * @return The list of artifacts to package
     */
    private List<Artifact> selectArtifacts(ArtifactMap am) {
        final List<Artifact> retval = new ArrayList<Artifact>();
        final List<String> clist = am.getClassifiers();

        final Artifact artifact = mojo.getArtifact();
        final List<Artifact> attachedArtifacts = mojo.getAttachedArtifacts();

        if (clist == null) {
            retval.add(artifact);
            retval.addAll(attachedArtifacts);
        } else {
            if (clist.contains(null)) {
                retval.add(artifact);
            }
            for (Artifact aa : attachedArtifacts) {
                if ((aa.hasClassifier()) && (clist.contains(aa.getClassifier()))) {
                    retval.add(aa);
                }
            }
        }

        return retval;
    }

    /**
     * Make a list of the dependencies to package in this mapping.
     *
     * @param d The artifact mapping information
     * @return The list of artifacts to package
     */
    private List<Artifact> selectDependencies(Dependency d) {
        List<Artifact> retval = new ArrayList<Artifact>();
        List<Artifact> inc = d.getIncludes();
        List<Artifact> exc = d.getExcludes();

        @SuppressWarnings("unchecked")
        Set<Artifact> deps = mojo.project.getArtifacts();
        if (deps == null || deps.isEmpty()) {
            return retval;
        }

        final Log log = mojo.getLog();

        for (Artifact pdep : deps) {
            log.debug("Dependency is " + pdep + " at " + pdep.getFile());
            if (!depMatcher(pdep, exc)) {
                log.debug("--> not excluded");
                if ((inc == null) || (depMatcher(pdep, inc))) {
                    log.debug("--> included");
                    retval.add(pdep);
                }
            }
        }

        return retval;
    }

    /**
     * Installs the {@link Mapping#getSources() sources} to <i>dest</i>
     *
     * @param map The <tt>Mapping</tt> to process the {@link Source sources} for.
     * @param dest The destination directory for the sources.
     * @throws MojoExecutionException
     * @throws MojoFailureException
     */
    private void processSources(Mapping map, File dest) throws MojoExecutionException, MojoFailureException {
        if (!dest.exists()) {
            if (!dest.mkdirs()) {
                throw new MojoExecutionException("unable to create directory: " + dest.getAbsolutePath());
            }
        }

        String relativeDestination = map.getDestination();
        if (!relativeDestination.endsWith(File.separator)) {
            relativeDestination += File.separatorChar;
        }

        List<Source> srcs = map.getSources();
        if (srcs != null) {
            // for passivity, we will always use lowercase representation of architecture
            // for comparison purposes.
            final String targetArchComparison = mojo.getTargetArch().toLowerCase(Locale.ENGLISH);

            final String targetOS = mojo.getTargetOS();

            final Map<String, List<SoftlinkSource>> linkTargetToSources = mojo.getLinkTargetToSources();

            // it is important that for each Source we set the files that are "installed".
            for (Source src : srcs) {
                if (!src.matchesArchitecture(targetArchComparison)) {
                    mojo.getLog().debug("Source does not match target architecture: " + src.toString());
                    continue;
                }

                if (!src.matchesOSName(targetOS)) {
                    mojo.getLog().debug("Source does not match target os name: " + src.toString());
                    continue;
                }

                final File macroEvaluatedLocation = new File(evaluateMacros(src.getLocation()));
                src.setMacroEvaluatedLocation(macroEvaluatedLocation.getPath());

                final File locationFile = macroEvaluatedLocation.isAbsolute() ? macroEvaluatedLocation
                        : new File(mojo.project.getBasedir(), macroEvaluatedLocation.getPath());
                // better with just macroEvaluatedLocation.getAbsoluteFile(), but not tested yet

                // it is important that we check if softlink source first as the "location" may
                // exist in the filesystem of the build machine
                if (src instanceof SoftlinkSource) {
                    SoftlinkSource softlinkSource = (SoftlinkSource) src;
                    List<SoftlinkSource> sources = linkTargetToSources.get(relativeDestination);
                    if (sources == null) {
                        sources = new LinkedList<SoftlinkSource>();
                        linkTargetToSources.put(relativeDestination, sources);
                    }

                    sources.add(softlinkSource);

                    softlinkSource.setSourceMapping(map);
                    map.setHasSoftLinks(true);
                } else if (locationFile.exists()) {
                    final String destination = src.getDestination();
                    if (destination == null) {
                        map.addCopiedFileNamesRelativeToDestination(copySource(locationFile, null, dest,
                                src.getIncludes(), src.getExcludes(), src.isFilter(), src.getNoDefaultExcludes()));
                    } else {
                        if (!locationFile.isFile()) {
                            throw new MojoExecutionException(MessageFormat.format(DESTINATION_DIRECTORY_ERROR_MSG,
                                    new Object[] { destination, macroEvaluatedLocation }));
                        }

                        copySource(locationFile, destination, dest, EMPTY_STRING_LIST, EMPTY_STRING_LIST,
                                src.isFilter(), src.getNoDefaultExcludes());

                        map.addCopiedFileNameRelativeToDestination(destination);
                    }
                } else if (!src.getFailIfLocationNotExists()) {
                    mojo.getLog().warn("Source location " + macroEvaluatedLocation
                            + " does not exist, but failIfLocationNotExist was set to false");
                } else {
                    throw new MojoExecutionException(
                            "Source location " + macroEvaluatedLocation + " does not exist");
                }
            }
        }
    }

    /**
     * Determine if there are any macros in the <i>value</i> and replace any/all occurrences with the
     * {@link AbstractRPMMojo#evaluateMacro(String) evaluated} value.
     *
     * @param value String to replace macros in.
     * @return Result of evaluating all macros in <i>value</i>.
     * @throws MojoExecutionException
     * @since 2.1-alpha-1
     */
    private String evaluateMacros(String value) throws MojoExecutionException {
        final Matcher matcher = MACRO_PATTERN.matcher(value);

        final StringBuffer newValue = new StringBuffer(value.length());
        while (matcher.find()) {
            final String macro = matcher.group(1);
            final String evaluatedValue = mojo.evaluateMacro(macro);
            matcher.appendReplacement(newValue, evaluatedValue.replaceAll("\\\\", "\\\\\\\\"));
        }

        matcher.appendTail(newValue);
        return newValue.toString();
    }

    /**
     * Determine if the dependency matches an include or exclude list.
     *
     * @param dep The dependency to check
     * @param list The list to check against
     * @return <code>true</code> if the dependency was found on the list
     */
    private boolean depMatcher(Artifact dep, List<Artifact> list) {
        if (list == null) {
            // No list, not possible to match
            return false;
        }

        final Log log = mojo.getLog();

        for (Artifact item : list) {
            log.debug("Compare " + dep + " to " + item);
            final String groupId = item.getGroupId();
            if (StringUtils.isEmpty(groupId) || "*".equals(groupId) || groupId.equals(dep.getGroupId())) {
                log.debug("... Group matches");
                final String artifactId = item.getArtifactId();
                if (StringUtils.isEmpty(artifactId) || "*".equals(artifactId)
                        || artifactId.equals(dep.getArtifactId())) {
                    log.debug("... Artifact matches");
                    // ArtifactVersion av = item.getVersionRange().matchVersion( dep.getAvailableVersions() );
                    try {
                        if (item.getVersionRange().containsVersion(dep.getSelectedVersion())) {
                            log.debug("... Version matches");
                            if (item.getType().isEmpty()) {
                                log.debug("... Type is empty");
                                return true;
                            } else if (item.getType().equals(dep.getType())) {
                                log.debug("... Type matches");
                                if (item.getClassifier().isEmpty()) {
                                    log.debug("... Classifier is empty");
                                    return true;
                                } else if (item.getClassifier().equals(dep.getClassifier())) {
                                    log.debug("... Classifier matches");
                                    return true;
                                }
                            }
                        }
                    } catch (OverConstrainedVersionException ocve) {
                        log.debug("... caught OverConstrainedVersionException");
                    }
                }
            }
        }

        // Not found
        return false;
    }

    //
    // Convenient utils to convert to cygwin if needed
    //

    public static final String UNIX_FILE_SEPARATOR = "/";

    public static String toUnixPath(File path) {
        return toUnixPath(path.getAbsolutePath());
    }

    public static String toUnixPath(String path) {
        path = StringUtils.replace(path, "\\", "/");

        String[] tokens = StringUtils.split(path, ":");

        if (tokens.length == 2 && tokens[0].length() == 1) {
            StringBuilder buffer = new StringBuilder();
            buffer.append("/cygdrive/").append(tokens[0]).append(tokens[1]);
            path = buffer.toString();
        }

        return path;
    }
}