package org.springframework.roo.file.monitor.event; import java.io.File; import java.util.Date; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.builder.ToStringBuilder; import org.springframework.roo.file.monitor.FileMonitorService; import org.springframework.roo.support.util.FileUtils; /** * The details of a file that once existed on the disk. * <p> * Instances of this class are usually included within a {@link FileEvent} * object. * * @author Ben Alex * @since 1.0 */ public class FileDetails implements Comparable<FileDetails> { /** * Returns the canonical path of the given {@link File}. * * @param file the file for which to find the canonical path (required) * @return the canonical path * @deprecated use {@link FileUtils#getCanonicalPath(File)} instead */ @Deprecated public static String getCanonicalPath(final File file) { return FileUtils.getCanonicalPath(file); } /** * Indicates whether the given canonical path matches the given Ant-style * pattern * * @param antPattern the pattern to check against (can't be blank) * @param canonicalPath the path to check (can't be blank) * @return see above * @deprecated use {@link FileUtils#matchesAntPath(String, String)} instead */ @Deprecated public static boolean matchesAntPath(final String antPattern, final String canonicalPath) { return FileUtils.matchesAntPath(antPattern, canonicalPath); } private final File file; private final Long lastModified; /** * Constructor * * @param file the file for which these are the details (required) * @param lastModified the system clock in milliseconds when this file was * last modified (can be <code>null</code>) */ public FileDetails(final File file, final Long lastModified) { Validate.notNull(file, "File required"); this.file = file; this.lastModified = lastModified; } public int compareTo(final FileDetails o) { if (o == null) { throw new NullPointerException(); } // N.B. this is in reverse order to how we'd normally compare int result = o.getFile().compareTo(file); if (result == 0) { result = ObjectUtils.compare(o.getLastModified(), lastModified); } return result; } @Override public boolean equals(final Object obj) { return obj instanceof FileDetails && compareTo((FileDetails) obj) == 0; } /** * Each {@link FileDetails} is known by its canonical file name, which is * also the format used for Ant path matching etc. This method provides the * canonical file name without forcing the user to deal with the exceptions * that would arise from using {@link File} directly. * * @return the canonical path. */ public String getCanonicalPath() { return FileUtils.getCanonicalPath(file); } /** * @return the file that is subject of this status object (indicates the new * name in the case of a {@link FileOperation#RENAMED}). */ public File getFile() { return file; } /** * The {@link FileMonitorService} is required to advise of last modification * times. This method provides access to the modification time according to * {@link FileMonitorService}, which may be out of date due to the polling * mechanisms often used by implementations. Instead you should generally * use {@link #getFile()#lastModified} for the most accurate disk-derived * representation of the last modification time. * * @return the time the file was last modified, or in the case of a delete, * it is implementation-specific (may return null) */ public Long getLastModified() { return lastModified; } /** * Returns the portion of the child identifier that is relative to the * parent {@link FileDetails} instance. Note that this instance must be the * parent. * <p> * If an empty string is returned from this method, it denotes the child was * actually the same identifier as the parent. * * @param childCanonicalPath the confirmed child of this instance (required; * use canonical path) * @return the relative path within the parent instance (never null) */ public String getRelativeSegment(final String childCanonicalPath) { Validate.notNull(childCanonicalPath, "Child identifier is required"); Validate.isTrue(isParentOf(childCanonicalPath), "Identifier '%s' is not a child of '%s'", childCanonicalPath, this); return childCanonicalPath.substring(getCanonicalPath().length()); } @Override public int hashCode() { return 7 * file.hashCode() * ObjectUtils.hashCode(lastModified); } /** * Indicates whether the presented canonical path is a child of the current * {@link FileDetails} instance. Put differently, returning true indicates * the current instance is a parent directory of the presented * possibleChildCanonicalPath. * <p> * This method will return true if the presented child is a child of the * current instance, or if the presented child is identical to the current * instance. * * @param possibleChildCanonicalPath to evaluate (required) * @return true if the presented possible child is indeed a child of the * current instance */ public boolean isParentOf(final String possibleChildCanonicalPath) { Validate.notBlank(possibleChildCanonicalPath, "Possible child to evaluate is required"); return FileUtils.ensureTrailingSeparator(possibleChildCanonicalPath).startsWith( FileUtils.ensureTrailingSeparator(getCanonicalPath())); } /** * Indicates whether this file's canonical path matches the given Ant-style * pattern. * <p> * The presented path must be in Ant syntax. It should include a full prefix * that is consistent with the {@link #getCanonicalPath()} method. * * @param antPattern the pattern to check this file against (cannot be * blank) * @return whether the path matches or not */ public boolean matchesAntPath(final String antPattern) { return FileUtils.matchesAntPath(antPattern, getCanonicalPath()); } @Override public String toString() { final ToStringBuilder builder = new ToStringBuilder(this); builder.append("file", file); builder.append("exists", file.exists()); builder.append("lastModified", lastModified == null ? "Unavailable" : new Date(lastModified).toString()); return builder.toString(); } }