All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.mojo.jaxb2.shared.FileSystemUtilities Maven / Gradle / Ivy

Go to download

Mojo's JAXB-2 Maven plugin is used to create an object graph from XSDs based on the JAXB 2.x implementation and to generate XSDs from JAXB annotated Java classes.

There is a newer version: 3.2.0
Show newest version
package org.codehaus.mojo.jaxb2.shared;

/*
 * 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 org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.mojo.jaxb2.AbstractJaxbMojo;
import org.codehaus.mojo.jaxb2.shared.filters.Filter;
import org.codehaus.mojo.jaxb2.shared.filters.Filters;
import org.codehaus.plexus.util.FileUtils;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.StringUtils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * The Jaxb2 Maven Plugin needs to fiddle with the filesystem a great deal, to create and optionally prune
 * directories or detect/create various files. This utility class contains all such algorithms, and serves as
 * an entry point to any Plexus Utils methods.
 *
 * @author Lennart Jörelid
 * @since 2.0
 */
public final class FileSystemUtilities {

    /*
     * Hide the constructor for utility classes.
     */
    private FileSystemUtilities() {
        // Do nothing
    }

    /**
     * FileFilter which accepts Files that exist and for which {@code File.isFile() } is {@code true}.
     */
    public static final FileFilter EXISTING_FILE = new FileFilter() {
        @Override
        public boolean accept(final File candidate) {
            return candidate != null && candidate.exists() && candidate.isFile();
        }
    };

    /**
     * FileFilter which accepts Files that exist and for which {@code File.isDirectory() } is {@code true}.
     */
    public static final FileFilter EXISTING_DIRECTORY = new FileFilter() {
        @Override
        public boolean accept(final File candidate) {
            return candidate != null && candidate.exists() && candidate.isDirectory();
        }
    };

    /**
     * Acquires the canonical path for the supplied file.
     *
     * @param file A non-null File for which the canonical path should be retrieved.
     * @return The canonical path of the supplied file.
     */
    public static String getCanonicalPath(final File file) {
        return getCanonicalFile(file).getPath();
    }

    /**
     * Non-valid Characters for naming files, folders under Windows: ":", "*", "?", "\"", "<", ">", "|"
     *
     * @see 
     * http://support.microsoft.com/?scid=kb%3Ben-us%3B177506&x=12&y=13
     * @see org.codehaus.plexus.util.FileUtils
     */
    private static final String[] INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME = {":", "*", "?", "\"", "<", ">", "|"};

    /**
     * Acquires the canonical File for the supplied file.
     *
     * @param file A non-null File for which the canonical File should be retrieved.
     * @return The canonical File of the supplied file.
     */
    public static File getCanonicalFile(final File file) {

        // Check sanity
        Validate.notNull(file, "file");

        // All done
        try {
            return file.getCanonicalFile();
        } catch (IOException e) {
            throw new IllegalArgumentException("Could not acquire the canonical file for ["
                    + file.getAbsolutePath() + "]", e);
        }
    }

    /**
     * 

Retrieves the canonical File matching the supplied path in the following order or priority:

*
    *
  1. Absolute path: The path is used by itself (i.e. {@code new File(path);}). If an * existing file or directory matches the provided path argument, its canonical path will be returned.
  2. *
  3. Relative path: The path is appended to the baseDir (i.e. * {@code new File(baseDir, path);}). If an existing file or directory matches the provided path argument, * its canonical path will be returned. Only in this case will be baseDir argument be considered.
  4. *
*

If no file or directory could be derived from the supplied path and baseDir, {@code null} is returned.

* * @param path A non-null path which will be used to find an existing file or directory. * @param baseDir A directory to which the path will be appended to search for the existing file or directory in * case the file was nonexistent when interpreted as an absolute path. * @return either a canonical File for the path, or {@code null} if no file or directory matched * the supplied path and baseDir. */ public static File getExistingFile(final String path, final File baseDir) { // Check sanity Validate.notEmpty(path, "path"); final File theFile = new File(path); File toReturn = null; // Is 'path' absolute? if (theFile.isAbsolute() && (EXISTING_FILE.accept(theFile) || EXISTING_DIRECTORY.accept(theFile))) { toReturn = getCanonicalFile(theFile); } // Is 'path' relative? if (!theFile.isAbsolute()) { // In this case, baseDir cannot be null. Validate.notNull(baseDir, "baseDir"); final File relativeFile = new File(baseDir, path); if (EXISTING_FILE.accept(relativeFile) || EXISTING_DIRECTORY.accept(relativeFile)) { toReturn = getCanonicalFile(relativeFile); } } // The path provided did not point to an existing File or Directory. return toReturn; } /** * Retrieves the URL for the supplied File. Convenience method which hides exception handling * for the operation in question. * * @param aFile A File for which the URL should be retrieved. * @return The URL for the supplied aFile. * @throws java.lang.IllegalArgumentException if getting the URL yielded a MalformedURLException. */ public static URL getUrlFor(final File aFile) throws IllegalArgumentException { // Check sanity Validate.notNull(aFile, "aFile"); try { return aFile.toURI().normalize().toURL(); } catch (MalformedURLException e) { throw new IllegalArgumentException("Could not retrieve the URL from file [" + getCanonicalPath(aFile) + "]", e); } } /** * Acquires the file for a supplied URL, provided that its protocol is is either a file or a jar. * * @param anURL a non-null URL. * @param encoding The encoding to be used by the URLDecoder to decode the path found. * @return The File pointing to the supplied URL, for file or jar protocol URLs and null otherwise. */ public static File getFileFor(final URL anURL, final String encoding) { // Check sanity Validate.notNull(anURL, "anURL"); Validate.notNull(encoding, "encoding"); final String protocol = anURL.getProtocol(); File toReturn = null; if ("file".equalsIgnoreCase(protocol)) { try { final String decodedPath = URLDecoder.decode(anURL.getPath(), encoding); toReturn = new File(decodedPath); } catch (Exception e) { throw new IllegalArgumentException("Could not get the File for [" + anURL + "]", e); } } else if ("jar".equalsIgnoreCase(protocol)) { try { // Decode the JAR final String tmp = URLDecoder.decode(anURL.getFile(), encoding); // JAR URLs generally contain layered protocols, such as: // jar:file:/some/path/to/nazgul-tools-validation-aspect-4.0.2.jar!/the/package/ValidationAspect.class final URL innerURL = new URL(tmp); // We can handle File protocol URLs here. if ("file".equalsIgnoreCase(innerURL.getProtocol())) { // Peel off the inner protocol final String innerUrlPath = innerURL.getPath(); final String filePath = innerUrlPath.contains("!") ? innerUrlPath.substring(0, innerUrlPath.indexOf("!")) : innerUrlPath; toReturn = new File(URLDecoder.decode(filePath, encoding)); } } catch (Exception e) { throw new IllegalArgumentException("Could not get the File for [" + anURL + "]", e); } } // All done. return toReturn; } /** * Filters files found either in the sources paths (or in the standardDirectory if no explicit sources are given), * and retrieves a List holding those files that do not match any of the supplied Java Regular Expression * excludePatterns. * * @param baseDir The non-null basedir Directory. * @param sources The sources which should be either absolute or relative (to the given baseDir) * paths to files or to directories that should be searched recursively for files. * @param standardDirectories If no sources are given, revert to searching all files under these standard * directories. Each path is {@code relativize()}-d to the supplied baseDir to * reach a directory path. * @param log A non-null Maven Log for logging any operations performed. * @param fileTypeDescription A human-readable short description of what kind of files are searched for, such as * "xsdSources" or "xjbSources". * @param excludePatterns An optional List of patterns used to construct an ExclusionRegExpFileFilter used to * identify files which should be excluded from the result. * @return URLs to all Files under the supplied sources (or standardDirectories, if no explicit sources * are given) which do not match the supplied Java Regular excludePatterns. */ @SuppressWarnings("all") public static List filterFiles(final File baseDir, final List sources, final List standardDirectories, final Log log, final String fileTypeDescription, final List> excludePatterns) { final SortedMap pathToResolvedSourceMap = new TreeMap(); for (String current : standardDirectories) { for (File currentResolvedSource : FileSystemUtilities.filterFiles( baseDir, sources, FileSystemUtilities.relativize(current, baseDir, true), log, fileTypeDescription, excludePatterns)) { // Add the source pathToResolvedSourceMap.put( FileSystemUtilities.getCanonicalPath(currentResolvedSource), currentResolvedSource); } } final List toReturn = new ArrayList(); // Extract the URLs for all resolved Java sources. for (Map.Entry current : pathToResolvedSourceMap.entrySet()) { toReturn.add(FileSystemUtilities.getUrlFor(current.getValue())); } if (log.isDebugEnabled()) { final StringBuilder builder = new StringBuilder(); builder.append("\n+=================== [Filtered " + fileTypeDescription + "]\n"); builder.append("|\n"); builder.append("| " + excludePatterns.size() + " Exclude patterns:\n"); for (int i = 0; i < excludePatterns.size(); i++) { builder.append("| [" + (i + 1) + "/" + excludePatterns.size() + "]: " + excludePatterns.get(i) + "\n"); } builder.append("|\n"); builder.append("| " + standardDirectories.size() + " Standard Directories:\n"); for (int i = 0; i < standardDirectories.size(); i++) { builder.append("| [" + (i + 1) + "/" + standardDirectories.size() + "]: " + standardDirectories.get(i) + "\n"); } builder.append("|\n"); builder.append("| " + toReturn.size() + " Results:\n"); for (int i = 0; i < toReturn.size(); i++) { builder.append("| [" + (i + 1) + "/" + toReturn.size() + "]: " + toReturn.get(i) + "\n"); } builder.append("|\n"); builder.append("+=================== [End Filtered " + fileTypeDescription + "]\n\n"); // Log all. log.debug(builder.toString().replace("\n", AbstractJaxbMojo.NEWLINE)); } // All done. return toReturn; } /** * Filters files found either in the sources paths (or in the standardDirectory if no explicit sources are given), * and retrieves a List holding those files that do not match any of the supplied Java Regular Expression * excludePatterns. * * @param baseDir The non-null basedir Directory. * @param sources The sources which should be either absolute or relative (to the given baseDir) * paths to files or to directories that should be searched recursively for files. * @param standardDirectory If no sources are given, revert to searching all files under this standard directory. * This is the path appended to the baseDir to reach a directory. * @param log A non-null Maven Log for logging any operations performed. * @param fileTypeDescription A human-readable short description of what kind of files are searched for, such as * "xsdSources" or "xjbSources". * @param excludeFilters An optional List of Filters used to identify files which should be excluded from * the result. * @return All files under the supplied sources (or standardDirectory, if no explicit sources are given) which * do not match the supplied Java Regular excludePatterns. */ @SuppressWarnings("CheckStyle") public static List filterFiles(final File baseDir, final List sources, final String standardDirectory, final Log log, final String fileTypeDescription, final List> excludeFilters) { // Check sanity Validate.notNull(baseDir, "baseDir"); Validate.notNull(log, "log"); Validate.notEmpty(standardDirectory, "standardDirectory"); Validate.notEmpty(fileTypeDescription, "fileTypeDescription"); // No sources provided? Fallback to the standard (which should be a relative path). List effectiveSources = sources; if (sources == null || sources.isEmpty()) { effectiveSources = new ArrayList(); final File tmp = new File(standardDirectory); final File rootDirectory = tmp.isAbsolute() ? tmp : new File(baseDir, standardDirectory); effectiveSources.add(FileSystemUtilities.getCanonicalPath(rootDirectory)); } // First, remove the non-existent sources. List existingSources = new ArrayList(); for (String current : effectiveSources) { final File existingFile = FileSystemUtilities.getExistingFile(current, baseDir); if (existingFile != null) { existingSources.add(existingFile); if (log.isDebugEnabled()) { log.debug("Accepted configured " + fileTypeDescription + " [" + FileSystemUtilities.getCanonicalFile(existingFile) + "]"); } } else { if (log.isInfoEnabled()) { log.info("Ignored given or default " + fileTypeDescription + " [" + current + "], since it is not an existent file or directory."); } } } if (log.isDebugEnabled() && existingSources.size() > 0) { final int size = existingSources.size(); log.debug(" [" + size + " existing " + fileTypeDescription + "] ..."); for (int i = 0; i < size; i++) { log.debug(" " + (i + 1) + "/" + size + ": " + existingSources.get(i)); } log.debug(" ... End [" + size + " existing " + fileTypeDescription + "]"); } // All Done. return FileSystemUtilities.resolveRecursively(existingSources, excludeFilters, log); } /** * Filters all supplied files using the provided {@code acceptFilter}. * * @param files The list of files to resolve, filter and return. If the {@code files} List * contains directories, they are searched for Files recursively. Any found Files in such * a search are included in the resulting File List if they match the acceptFilter supplied. * @param acceptFilter A filter matched to all files in the given List. If the acceptFilter matches a file, it is * included in the result. * @param log The active Maven Log. * @return All files in (or files in subdirectories of directories provided in) the files List, provided that each * file is accepted by an ExclusionRegExpFileFilter. */ public static List filterFiles(final List files, final Filter acceptFilter, final Log log) { // Check sanity Validate.notNull(files, "files"); final List toReturn = new ArrayList(); if (files.size() > 0) { for (File current : files) { final boolean isAcceptedFile = EXISTING_FILE.accept(current) && acceptFilter.accept(current); final boolean isAcceptedDirectory = EXISTING_DIRECTORY.accept(current) && acceptFilter.accept(current); if (isAcceptedFile) { toReturn.add(current); } else if (isAcceptedDirectory) { recurseAndPopulate(toReturn, Collections.singletonList(acceptFilter), current, false, log); } } } // All done return toReturn; } /** * Retrieves a List of Files containing all the existing files within the supplied files List, including all * files found in directories recursive to any directories provided in the files list. Each file included in the * result must pass an ExclusionRegExpFileFilter synthesized from the supplied exclusions pattern(s). * * @param files The list of files to resolve, filter and return. If the {@code files} List * contains directories, they are searched for Files recursively. Any found Files in such * a search are included in the resulting File List if they do not match any of the * exclusionFilters supplied. * @param exclusionFilters A List of Filters which identify files to remove from the result - implying that any * File matched by any of these exclusionFilters will not be included in the result. * @param log The active Maven Log. * @return All files in (or files in subdirectories of directories provided in) the files List, provided that each * file is accepted by an ExclusionRegExpFileFilter. */ public static List resolveRecursively(final List files, final List> exclusionFilters, final Log log) { // Check sanity Validate.notNull(files, "files"); final List> effectiveExclusions = exclusionFilters == null ? new ArrayList>() : exclusionFilters; final List toReturn = new ArrayList(); if (files.size() > 0) { for (File current : files) { final boolean isAcceptedFile = EXISTING_FILE.accept(current) && Filters.noFilterMatches(current, effectiveExclusions); final boolean isAcceptedDirectory = EXISTING_DIRECTORY.accept(current) && Filters.noFilterMatches(current, effectiveExclusions); if (isAcceptedFile) { toReturn.add(current); } else if (isAcceptedDirectory) { recurseAndPopulate(toReturn, effectiveExclusions, current, true, log); } } } // All done return toReturn; } /** * Convenience method to successfully create a directory - or throw an exception if failing to create it. * * @param aDirectory The directory to create. * @param cleanBeforeCreate if {@code true}, the directory and all its content will be deleted before being * re-created. This will ensure that the created directory is really clean. * @throws MojoExecutionException if the aDirectory could not be created (and/or cleaned). */ public static void createDirectory(final File aDirectory, final boolean cleanBeforeCreate) throws MojoExecutionException { // Check sanity Validate.notNull(aDirectory, "aDirectory"); validateFileOrDirectoryName(aDirectory); // Clean an existing directory? if (cleanBeforeCreate) { try { FileUtils.deleteDirectory(aDirectory); } catch (IOException e) { throw new MojoExecutionException("Could not clean directory [" + getCanonicalPath(aDirectory) + "]", e); } } // Now, make the required directory, if it does not already exist as a directory. final boolean existsAsFile = aDirectory.exists() && aDirectory.isFile(); if (existsAsFile) { throw new MojoExecutionException("[" + getCanonicalPath(aDirectory) + "] exists and is a file. " + "Cannot make directory"); } else if (!aDirectory.exists() && !aDirectory.mkdirs()) { throw new MojoExecutionException("Could not create directory [" + getCanonicalPath(aDirectory) + "]"); } } /** * If the supplied path refers to a file or directory below the supplied basedir, the returned * path is identical to the part below the basedir. * * @param path The path to strip off basedir path from, and return. * @param parentDir The maven project basedir. * @param removeInitialFileSep If true, an initial {@code File#separator} is removed before returning. * @return The path relative to basedir, if it is situated below the basedir. Otherwise the supplied path. */ public static String relativize(final String path, final File parentDir, final boolean removeInitialFileSep) { // Check sanity Validate.notNull(path, "path"); Validate.notNull(parentDir, "parentDir"); final Path p = Paths.get(path); final Path pd = parentDir.toPath(); String platformSpecificPath; if (p.normalize().startsWith(pd.normalize().toString())) { platformSpecificPath = pd.relativize(p).toString(); } else { platformSpecificPath = p.toString(); } if (removeInitialFileSep && platformSpecificPath.startsWith(File.separator)) { platformSpecificPath = platformSpecificPath.substring(File.separator.length()); } // NOTE: it appears this function is meant to preserve the file separator that was passed in the path if (path.indexOf('\\') == -1) { platformSpecificPath = platformSpecificPath.replace('\\', '/'); } return platformSpecificPath; } /** * If the supplied fileOrDir is a File, it is added to the returned List if any of the filters Match. * If the supplied fileOrDir is a Directory, it is listed and any of the files immediately within the fileOrDir * directory are returned within the resulting List provided that they match any of the supplied filters. * * @param fileOrDir A File or Directory. * @param fileFilters A List of filter of which at least one must match to add the File * (or child Files, in case of a directory) to the resulting List. * @param log The active Maven Log * @return A List holding the supplied File (or child Files, in case fileOrDir is a Directory) given that at * least one Filter accepts them. */ @SuppressWarnings("all") public static List listFiles(final File fileOrDir, final List> fileFilters, final Log log) { return listFiles(fileOrDir, fileFilters, false, log); } /** * If the supplied fileOrDir is a File, it is added to the returned List if any of the filters Match. * If the supplied fileOrDir is a Directory, it is listed and any of the files immediately within the fileOrDir * directory are returned within the resulting List provided that they match any of the supplied filters. * * @param fileOrDir A File or Directory. * @param fileFilters A List of filter of which at least one must match to add the File (or child Files, in case * of a directory) to the resulting List. * @param excludeFilterOperation if {@code true}, all fileFilters are considered exclude filter, i.e. if * any of the Filters match the fileOrDir, that fileOrDir will be excluded from the * result. * @param log The active Maven Log * @return A List holding the supplied File (or child Files, in case fileOrDir is a Directory) given that at * least one Filter accepts them. */ @SuppressWarnings("all") public static List listFiles(final File fileOrDir, final List> fileFilters, final boolean excludeFilterOperation, final Log log) { // Check sanity Validate.notNull(log, "log"); Validate.notNull(fileFilters, "fileFilters"); final List toReturn = new ArrayList(); if (EXISTING_FILE.accept(fileOrDir)) { checkAndAdd(toReturn, fileOrDir, fileFilters, excludeFilterOperation, log); } else if (EXISTING_DIRECTORY.accept(fileOrDir)) { final File[] listedFiles = fileOrDir.listFiles(); if (listedFiles != null) { for (File current : listedFiles) { // Typically, hidden directories start with '.' // Except them from automagic filtering. if (current.getName().charAt(0) != '.') { checkAndAdd(toReturn, current, fileFilters, excludeFilterOperation, log); } } } } // All done. return toReturn; } // // Private helpers // private static void checkAndAdd(final List toPopulate, final File current, final List> fileFilters, final boolean excludeFilterOperation, final Log log) { // // When no filters are supplied... // [Include Operation]: all files will be rejected // [Exclude Operation]: all files will be included // final boolean noFilters = fileFilters == null || fileFilters.isEmpty(); final boolean addFile = excludeFilterOperation ? noFilters || Filters.rejectAtLeastOnce(current, fileFilters) : noFilters || Filters.matchAtLeastOnce(current, fileFilters); final String logPrefix = (addFile ? "Accepted " : "Rejected ") + (current.isDirectory() ? "directory" : "file") + " ["; if (addFile) { toPopulate.add(current); } if (log.isDebugEnabled()) { log.debug(logPrefix + getCanonicalPath(current) + "]"); } } private static void validateFileOrDirectoryName(final File fileOrDir) { if (Os.isFamily(Os.FAMILY_WINDOWS) && !FileUtils.isValidWindowsFileName(fileOrDir)) { throw new IllegalArgumentException( "The file (" + fileOrDir + ") cannot contain any of the following characters: \n" + StringUtils.join(INVALID_CHARACTERS_FOR_WINDOWS_FILE_NAME, " ")); } } private static void recurseAndPopulate(final List toPopulate, final List> fileFilters, final File aDirectory, final boolean excludeOperation, final Log log) { final List files = listFiles(aDirectory, fileFilters, excludeOperation, log); for (File current : files) { if (EXISTING_FILE.accept(current)) { toPopulate.add(current); } if (EXISTING_DIRECTORY.accept(current)) { recurseAndPopulate(toPopulate, fileFilters, current, excludeOperation, log); } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy