160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair/* 260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Copyright 2008 the original author or authors. 360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Licensed under the Apache License, Version 2.0 (the "License"); 560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * you may not use this file except in compliance with the License. 660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * You may obtain a copy of the License at 760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * http://www.apache.org/licenses/LICENSE-2.0 960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 1060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Unless required by applicable law or agreed to in writing, software 1160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * distributed under the License is distributed on an "AS IS" BASIS, 1260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * See the License for the specific language governing permissions and 1460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * limitations under the License. 1560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 1660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairpackage org.mockftpserver.fake.filesystem; 1760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 1860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport org.apache.log4j.Logger; 1960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport org.mockftpserver.core.util.Assert; 2060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport org.mockftpserver.core.util.PatternUtil; 2160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport org.mockftpserver.core.util.StringUtil; 2260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 2360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport java.util.ArrayList; 2460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport java.util.Collections; 2560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport java.util.Date; 2660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport java.util.HashMap; 2760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport java.util.Iterator; 2860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport java.util.List; 2960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairimport java.util.Map; 3060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 3160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair/** 3260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Abstract superclass for implementation of the FileSystem interface that manage the files 3360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * and directories in memory, simulating a real file system. 3460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * <p/> 3560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * If the <code>createParentDirectoriesAutomatically</code> property is set to <code>true</code>, 3660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * then creating a directory or file will automatically create any parent directories (recursively) 3760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * that do not already exist. If <code>false</code>, then creating a directory or file throws an 3860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * exception if its parent directory does not exist. This value defaults to <code>true</code>. 3960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * <p/> 4060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * The <code>directoryListingFormatter</code> property holds an instance of {@link DirectoryListingFormatter} , 4160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * used by the <code>formatDirectoryListing</code> method to format directory listings in a 4260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * filesystem-specific manner. This property must be initialized by concrete subclasses. 4360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 4460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @author Chris Mair 4560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @version $Revision$ - $Date$ 4660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 4760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismairpublic abstract class AbstractFakeFileSystem implements FileSystem { 4860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 4960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private static final Logger LOG = Logger.getLogger(AbstractFakeFileSystem.class); 5060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 5160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 5260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * If <code>true</code>, creating a directory or file will automatically create 5360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * any parent directories (recursively) that do not already exist. If <code>false</code>, 5460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * then creating a directory or file throws an exception if its parent directory 5560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * does not exist. This value defaults to <code>true</code>. 5660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 5760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private boolean createParentDirectoriesAutomatically = true; 5860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 5960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 6060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * The {@link DirectoryListingFormatter} used by the {@link #formatDirectoryListing(FileSystemEntry)} 6160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * method. This must be initialized by concrete subclasses. 6260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 6360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private DirectoryListingFormatter directoryListingFormatter; 6460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 6560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private Map entries = new HashMap(); 6660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 6760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair //------------------------------------------------------------------------- 6860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // Public API 6960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair //------------------------------------------------------------------------- 7060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 7160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public boolean isCreateParentDirectoriesAutomatically() { 7260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return createParentDirectoriesAutomatically; 7360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 7460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 7560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public void setCreateParentDirectoriesAutomatically(boolean createParentDirectoriesAutomatically) { 7660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair this.createParentDirectoriesAutomatically = createParentDirectoriesAutomatically; 7760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 7860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 7960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public DirectoryListingFormatter getDirectoryListingFormatter() { 8060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return directoryListingFormatter; 8160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 8260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 8360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public void setDirectoryListingFormatter(DirectoryListingFormatter directoryListingFormatter) { 8460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair this.directoryListingFormatter = directoryListingFormatter; 8560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 8660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 8760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 8860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Add each of the entries in the specified List to this filesystem. Note that this does not affect 8960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * entries already existing within this filesystem. 9060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 9160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param entriesToAdd - the List of FileSystemEntry entries to add 9260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 9360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public void setEntries(List entriesToAdd) { 9460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair for (Iterator iter = entriesToAdd.iterator(); iter.hasNext();) { 9560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry entry = (FileSystemEntry) iter.next(); 9660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair add(entry); 9760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 9860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 9960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 10060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 10160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Add the specified file system entry (file or directory) to this file system 10260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 10360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param entry - the FileSystemEntry to add 10460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 10560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public void add(FileSystemEntry entry) { 10660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String path = entry.getPath(); 10760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair checkForInvalidFilename(path); 10860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (getEntry(path) != null) { 10960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair throw new FileSystemException(path, "filesystem.pathAlreadyExists"); 11060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 11160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 11260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (!parentDirectoryExists(path)) { 11360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String parent = getParent(path); 11460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (createParentDirectoriesAutomatically) { 11560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair add(new DirectoryEntry(parent)); 11660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } else { 11760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair throw new FileSystemException(parent, "filesystem.parentDirectoryDoesNotExist"); 11860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 11960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 12060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 12160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // Set lastModified, if not already set 12260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (entry.getLastModified() == null) { 12360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair entry.setLastModified(new Date()); 12460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 12560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 12660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair entries.put(getFileSystemEntryKey(path), entry); 12760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair entry.lockPath(); 12860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 12960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 13060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 13160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Delete the file or directory specified by the path. Return true if the file is successfully 13260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * deleted, false otherwise. If the path refers to a directory, it must be empty. Return false 13360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * if the path does not refer to a valid file or directory or if it is a non-empty directory. 13460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 13560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path of the file or directory to delete 13660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the file or directory is successfully deleted 13760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws org.mockftpserver.core.util.AssertFailedException 13860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * - if path is null 13960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#delete(java.lang.String) 14060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 14160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public boolean delete(String path) { 14260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(path, "path"); 14360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 14460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (getEntry(path) != null && !hasChildren(path)) { 14560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair removeEntry(path); 14660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return true; 14760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 14860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return false; 14960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 15060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 15160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 15260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return true if there exists a file or directory at the specified path 15360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 15460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 15560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the file/directory exists 15660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if path is null 15760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#exists(java.lang.String) 15860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 15960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public boolean exists(String path) { 16060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(path, "path"); 16160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return getEntry(path) != null; 16260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 16360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 16460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 16560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return true if the specified path designates an existing directory, false otherwise 16660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 16760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 16860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if path is a directory, false otherwise 16960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if path is null 17060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#isDirectory(java.lang.String) 17160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 17260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public boolean isDirectory(String path) { 17360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(path, "path"); 17460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry entry = getEntry(path); 17560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return entry != null && entry.isDirectory(); 17660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 17760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 17860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 17960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return true if the specified path designates an existing file, false otherwise 18060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 18160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 18260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if path is a file, false otherwise 18360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if path is null 18460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#isFile(java.lang.String) 18560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 18660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public boolean isFile(String path) { 18760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(path, "path"); 18860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry entry = getEntry(path); 18960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return entry != null && !entry.isDirectory(); 19060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 19160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 19260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 19360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the List of FileSystemEntry objects for the files in the specified directory or group of 19460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * files. If the path specifies a single file, then return a list with a single FileSystemEntry 19560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * object representing that file. If the path does not refer to an existing directory or 19660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * group of files, then an empty List is returned. 19760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 19860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path specifying a directory or group of files; may contain wildcards (? or *) 19960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the List of FileSystemEntry objects for the specified directory or file; may be empty 20060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#listFiles(java.lang.String) 20160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 20260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public List listFiles(String path) { 20360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (isFile(path)) { 20460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return Collections.singletonList(getEntry(path)); 20560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 20660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 20760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List entryList = new ArrayList(); 20860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List children = children(path); 20960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Iterator iter = children.iterator(); 21060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair while (iter.hasNext()) { 21160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String childPath = (String) iter.next(); 21260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry fileSystemEntry = getEntry(childPath); 21360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair entryList.add(fileSystemEntry); 21460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 21560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return entryList; 21660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 21760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 21860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 21960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the List of filenames in the specified directory path or file path. If the path specifies 22060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * a single file, then return that single filename. The returned filenames do not 22160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * include a path. If the path does not refer to a valid directory or file path, then an empty List 22260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * is returned. 22360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 22460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path specifying a directory or group of files; may contain wildcards (? or *) 22560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the List of filenames (not including paths) for all files in the specified directory 22660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * or file path; may be empty 22760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if path is null 22860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#listNames(java.lang.String) 22960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 23060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public List listNames(String path) { 23160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (isFile(path)) { 23260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return Collections.singletonList(getName(path)); 23360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 23460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 23560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List filenames = new ArrayList(); 23660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List children = children(path); 23760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Iterator iter = children.iterator(); 23860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair while (iter.hasNext()) { 23960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String childPath = (String) iter.next(); 24060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry fileSystemEntry = getEntry(childPath); 24160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair filenames.add(fileSystemEntry.getName()); 24260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 24360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return filenames; 24460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 24560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 24660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 24760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Rename the file or directory. Specify the FROM path and the TO path. Throw an exception if the FROM path or 24860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * the parent directory of the TO path do not exist; or if the rename fails for another reason. 24960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 25060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param fromPath - the source (old) path + filename 25160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param toPath - the target (new) path + filename 25260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if fromPath or toPath is null 25360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws FileSystemException - if the rename fails. 25460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 25560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public void rename(String fromPath, String toPath) { 25660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(toPath, "toPath"); 25760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(fromPath, "fromPath"); 25860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 25960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry entry = getRequiredEntry(fromPath); 26060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 26160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedFromPath = normalize(fromPath); 26260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedToPath = normalize(toPath); 26360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 26460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (!entry.isDirectory()) { 26560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair renamePath(entry, normalizedToPath); 26660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return; 26760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 26860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 26960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // Create the TO directory entry first so that the destination path exists when you 27060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // move the children. Remove the FROM path after all children have been moved 27160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair add(new DirectoryEntry(normalizedToPath)); 27260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 27360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List children = descendents(fromPath); 27460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Iterator iter = children.iterator(); 27560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair while (iter.hasNext()) { 27660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String childPath = (String) iter.next(); 27760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry child = getRequiredEntry(childPath); 27860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedChildPath = normalize(child.getPath()); 27960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.isTrue(normalizedChildPath.startsWith(normalizedFromPath), "Starts with FROM path"); 28060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String childToPath = normalizedToPath + normalizedChildPath.substring(normalizedFromPath.length()); 28160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair renamePath(child, childToPath); 28260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 28360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.isTrue(children(normalizedFromPath).isEmpty(), "Must have no children: " + normalizedFromPath); 28460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair removeEntry(normalizedFromPath); 28560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 28660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 28760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 28860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see java.lang.Object#toString() 28960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 29060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public String toString() { 29160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return this.getClass().getName() + entries; 29260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 29360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 29460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 29560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the formatted directory listing entry for the file represented by the specified FileSystemEntry 29660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 29760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param fileSystemEntry - the FileSystemEntry representing the file or directory entry to be formatted 29860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the the formatted directory listing entry 29960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 30060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public String formatDirectoryListing(FileSystemEntry fileSystemEntry) { 30160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(directoryListingFormatter, "directoryListingFormatter"); 30260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(fileSystemEntry, "fileSystemEntry"); 30360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return directoryListingFormatter.format(fileSystemEntry); 30460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 30560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 30660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 30760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Build a path from the two path components. Concatenate path1 and path2. Insert the path 30860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * separator character in between if necessary (i.e., if both are non-empty and path1 does not already 30960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * end with a separator character AND path2 does not begin with one). 31060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 31160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path1 - the first path component may be null or empty 31260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path2 - the second path component may be null or empty 31360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the path resulting from concatenating path1 to path2 31460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 31560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public String path(String path1, String path2) { 31660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair StringBuffer buf = new StringBuffer(); 31760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (path1 != null && path1.length() > 0) { 31860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair buf.append(path1); 31960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 32060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (path2 != null && path2.length() > 0) { 32160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if ((path1 != null && path1.length() > 0) 32260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair && (!isSeparator(path1.charAt(path1.length() - 1))) 32360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair && (!isSeparator(path2.charAt(0)))) { 32460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair buf.append(this.getSeparator()); 32560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 32660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair buf.append(path2); 32760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 32860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return buf.toString(); 32960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 33060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 33160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 33260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the parent path of the specified path. If <code>path</code> specifies a filename, 33360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * then this method returns the path of the directory containing that file. If <code>path</code> 33460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * specifies a directory, the this method returns its parent directory. If <code>path</code> is 33560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * empty or does not have a parent component, then return an empty string. 33660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * <p/> 33760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * All path separators in the returned path are converted to the system-dependent separator character. 33860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 33960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 34060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the parent of the specified path, or null if <code>path</code> has no parent 34160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if path is null 34260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 34360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public String getParent(String path) { 34460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List parts = normalizedComponents(path); 34560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (parts.size() < 2) { 34660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return null; 34760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 34860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair parts.remove(parts.size() - 1); 34960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return componentsToPath(parts); 35060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 35160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 35260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 35360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Returns the name of the file or directory denoted by this abstract 35460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * pathname. This is just the last name in the pathname's name 35560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * sequence. If the pathname's name sequence is empty, then the empty string is returned. 35660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 35760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 35860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return The name of the file or directory denoted by this abstract pathname, or the 35960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * empty string if this pathname's name sequence is empty 36060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 36160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public String getName(String path) { 36260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(path, "path"); 36360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalized = normalize(path); 36460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair int separatorIndex = normalized.lastIndexOf(this.getSeparator()); 36560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return (separatorIndex == -1) ? normalized : normalized.substring(separatorIndex + 1); 36660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 36760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 36860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 36960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Returns the FileSystemEntry object representing the file system entry at the specified path, or null 37060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * if the path does not specify an existing file or directory within this file system. 37160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 37260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path of the file or directory within this file system 37360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the FileSystemEntry containing the information for the file or directory, or else null 37460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @see FileSystem#getEntry(String) 37560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 37660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public FileSystemEntry getEntry(String path) { 37760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return (FileSystemEntry) entries.get(getFileSystemEntryKey(path)); 37860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 37960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 38060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair //------------------------------------------------------------------------- 38160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // Abstract Methods 38260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair //------------------------------------------------------------------------- 38360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 38460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 38560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 38660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the specified dir/file path name is valid according to the current filesystem. 38760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 38860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected abstract boolean isValidName(String path); 38960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 39060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 39160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the file system-specific file separator as a char 39260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 39360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected abstract char getSeparatorChar(); 39460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 39560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 39660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param pathComponent - the component (piece) of the path to check 39760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the specified path component is a root for this filesystem 39860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 39960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected abstract boolean isRoot(String pathComponent); 40060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 40160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 40260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return true if the specified char is a separator character for this filesystem 40360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 40460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param c - the character to test 40560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the specified char is a separator character 40660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 40760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected abstract boolean isSeparator(char c); 40860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 40960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair //------------------------------------------------------------------------- 41060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // Internal Helper Methods 41160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair //------------------------------------------------------------------------- 41260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 41360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 41460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the file system-specific file separator as a String 41560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 41660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected String getSeparator() { 41760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return Character.toString(getSeparatorChar()); 41860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 41960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 42060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 42160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the normalized and unique key used to access the file system entry 42260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 42360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 42460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the corresponding normalized key 42560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 42660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected String getFileSystemEntryKey(String path) { 42760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return normalize(path); 42860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 42960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 43060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 43160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the standard, normalized form of the path. 43260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 43360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 43460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the path in a standard, unique, canonical form 43560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if path is null 43660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 43760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected String normalize(String path) { 43860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return componentsToPath(normalizedComponents(path)); 43960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 44060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 44160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 44260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Throw an InvalidFilenameException if the specified path is not valid. 44360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 44460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 44560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 44660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected void checkForInvalidFilename(String path) { 44760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (!isValidName(path)) { 44860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair throw new InvalidFilenameException(path); 44960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 45060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 45160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 45260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 45360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Rename the file system entry to the specified path name 45460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 45560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param entry - the file system entry 45660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param toPath - the TO path (normalized) 45760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 45860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected void renamePath(FileSystemEntry entry, String toPath) { 45960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedFrom = normalize(entry.getPath()); 46060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedTo = normalize(toPath); 46160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair LOG.info("renaming from [" + normalizedFrom + "] to [" + normalizedTo + "]"); 46260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry newEntry = entry.cloneWithNewPath(normalizedTo); 46360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair add(newEntry); 46460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // Do this at the end, in case the addEntry() failed 46560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair removeEntry(normalizedFrom); 46660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 46760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 46860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 46960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the FileSystemEntry for the specified path. Throw FileSystemException if the 47060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * specified path does not exist. 47160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 47260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 47360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the FileSystemEntry 47460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws FileSystemException - if the specified path does not exist 47560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 47660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected FileSystemEntry getRequiredEntry(String path) { 47760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair FileSystemEntry entry = getEntry(path); 47860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (entry == null) { 47960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair LOG.error("Path does not exist: " + path); 48060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair throw new FileSystemException(normalize(path), "filesystem.pathDoesNotExist"); 48160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 48260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return entry; 48360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 48460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 48560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 48660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the components of the specified path as a List. The components are normalized, and 48760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * the returned List does not include path separator characters. 48860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 48960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 49060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the List of normalized components 49160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 49260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected List normalizedComponents(String path) { 49360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Assert.notNull(path, "path"); 49460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair char otherSeparator = this.getSeparatorChar() == '/' ? '\\' : '/'; 49560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String p = path.replace(otherSeparator, this.getSeparatorChar()); 49660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 49760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair // TODO better way to do this 49860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (p.equals(this.getSeparator())) { 49960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return Collections.singletonList(""); 50060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 50160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 50260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String[] parts = p.split("\\" + this.getSeparator()); 50360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List result = new ArrayList(); 50460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair for (int i = 0; i < parts.length; i++) { 50560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String part = parts[i]; 50660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (part.equals("..")) { 50760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair result.remove(result.size() - 1); 50860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } else if (!part.equals(".")) { 50960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair result.add(part); 51060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 51160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 51260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return result; 51360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 51460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 51560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 51660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Build a path from the specified list of path components 51760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 51860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param components - the list of path components 51960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the resulting path 52060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 52160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair protected String componentsToPath(List components) { 52260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (components.size() == 1) { 52360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String first = (String) components.get(0); 52460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (first.length() == 0 || isRoot(first)) { 52560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return first + this.getSeparator(); 52660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 52760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 52860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return StringUtil.join(components, this.getSeparator()); 52960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 53060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 53160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 53260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return true if the specified path designates an absolute file path. 53360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 53460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 53560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if path is absolute, false otherwise 53660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @throws AssertionError - if path is null 53760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 53860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair public boolean isAbsolute(String path) { 53960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return isValidName(path); 54060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 54160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 54260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 54360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return true if the specified path exists 54460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 54560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 54660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the path exists 54760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 54860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private boolean pathExists(String path) { 54960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return getEntry(path) != null; 55060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 55160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 55260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 55360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * If the specified path has a parent, then verify that the parent exists 55460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 55560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 55660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the parent of the specified path exists 55760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 55860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private boolean parentDirectoryExists(String path) { 55960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String parent = getParent(path); 56060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return parent == null || pathExists(parent); 56160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 56260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 56360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 56460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return true if the specified path represents a directory that contains one or more files or subdirectories 56560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 56660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 56760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return true if the path has child entries 56860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 56960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private boolean hasChildren(String path) { 57060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (!isDirectory(path)) { 57160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return false; 57260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 57360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String key = getFileSystemEntryKey(path); 57460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Iterator iter = entries.keySet().iterator(); 57560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair while (iter.hasNext()) { 57660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String p = (String) iter.next(); 57760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (p.startsWith(key) && !key.equals(p)) { 57860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return true; 57960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 58060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 58160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return false; 58260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 58360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 58460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 58560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the List of files or subdirectory paths that are descendents of the specified path 58660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 58760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 58860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the List of the paths for the files and subdirectories that are children, grandchildren, etc. 58960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 59060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private List descendents(String path) { 59160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (isDirectory(path)) { 59260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedPath = getFileSystemEntryKey(path); 59360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String separator = (normalizedPath.endsWith(getSeparator())) ? "" : getSeparator(); 59460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedDirPrefix = normalizedPath + separator; 59560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List descendents = new ArrayList(); 59660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Iterator iter = entries.keySet().iterator(); 59760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair while (iter.hasNext()) { 59860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String p = (String) iter.next(); 59960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (p.startsWith(normalizedDirPrefix) && !normalizedPath.equals(p)) { 60060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair descendents.add(p); 60160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 60260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 60360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return descendents; 60460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 60560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return Collections.EMPTY_LIST; 60660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 60760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 60860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair /** 60960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * Return the List of files or subdirectory paths that are children of the specified path 61060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * 61160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @param path - the path 61260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair * @return the List of the paths for the files and subdirectories that are children 61360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair */ 61460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private List children(String path) { 61560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String lastComponent = getName(path); 61660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair boolean containsWildcards = PatternUtil.containsWildcards(lastComponent); 61760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String dir = containsWildcards ? getParent(path) : path; 61860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String pattern = containsWildcards ? PatternUtil.convertStringWithWildcardsToRegex(getName(path)) : null; 61960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair LOG.debug("path=" + path + " lastComponent=" + lastComponent + " containsWildcards=" + containsWildcards + " dir=" + dir + " pattern=" + pattern); 62060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 62160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List descendents = descendents(dir); 62260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair List children = new ArrayList(); 62360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String normalizedDir = normalize(dir); 62460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair Iterator iter = descendents.iterator(); 62560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair while (iter.hasNext()) { 62660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair String descendentPath = (String) iter.next(); 62760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 62860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair boolean patternEmpty = pattern == null || pattern.length() == 0; 62960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair if (normalizedDir.equals(getParent(descendentPath)) && 63060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair (patternEmpty || (getName(descendentPath).matches(pattern)))) { 63160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair children.add(descendentPath); 63260b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 63360b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 63460b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair return children; 63560b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 63660b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 63760b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair private void removeEntry(String path) { 63860b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair entries.remove(getFileSystemEntryKey(path)); 63960b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair } 64060b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair 64160b81e2faf8511148f0d1e8f296e0b40ce9c7971chrismair}