1a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair/* 2a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Copyright 2008 the original author or authors. 3a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 4a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Licensed under the Apache License, Version 2.0 (the "License"); 5a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * you may not use this file except in compliance with the License. 6a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * You may obtain a copy of the License at 7a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 8a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * http://www.apache.org/licenses/LICENSE-2.0 9a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 10a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Unless required by applicable law or agreed to in writing, software 11a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * distributed under the License is distributed on an "AS IS" BASIS, 12a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * See the License for the specific language governing permissions and 14a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * limitations under the License. 15a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 16a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairpackage org.mockftpserver.fake.filesystem; 17a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 18a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.apache.log4j.Logger; 19a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.util.Assert; 20a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.util.PatternUtil; 21a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport org.mockftpserver.core.util.StringUtil; 22a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 23a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.ArrayList; 24a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.Collections; 25a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.Date; 26a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.HashMap; 27a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.Iterator; 28a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.List; 29a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairimport java.util.Map; 30a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 31a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair/** 32a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Abstract superclass for implementation of the FileSystem interface that manage the files 33a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * and directories in memory, simulating a real file system. 34a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * <p/> 35a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * If the <code>createParentDirectoriesAutomatically</code> property is set to <code>true</code>, 36a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * then creating a directory or file will automatically create any parent directories (recursively) 37a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * that do not already exist. If <code>false</code>, then creating a directory or file throws an 38a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * exception if its parent directory does not exist. This value defaults to <code>true</code>. 39a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * <p/> 40a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * The <code>directoryListingFormatter</code> property holds an instance of {@link DirectoryListingFormatter} , 41a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * used by the <code>formatDirectoryListing</code> method to format directory listings in a 42a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * filesystem-specific manner. This property must be initialized by concrete subclasses. 43a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 44a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @author Chris Mair 45a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @version $Revision$ - $Date$ 46a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 47a0ad464efff5f5e2d2523a3522cce6823ce05858chrismairpublic abstract class AbstractFakeFileSystem implements FileSystem { 48a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 49a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private static final Logger LOG = Logger.getLogger(AbstractFakeFileSystem.class); 50a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 51a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 52a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * If <code>true</code>, creating a directory or file will automatically create 53a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * any parent directories (recursively) that do not already exist. If <code>false</code>, 54a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * then creating a directory or file throws an exception if its parent directory 55a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * does not exist. This value defaults to <code>true</code>. 56a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 57a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private boolean createParentDirectoriesAutomatically = true; 58a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 59a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 60a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * The {@link DirectoryListingFormatter} used by the {@link #formatDirectoryListing(FileSystemEntry)} 61a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * method. This must be initialized by concrete subclasses. 62a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 63a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private DirectoryListingFormatter directoryListingFormatter; 64a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 65a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private Map entries = new HashMap(); 66a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 67a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair //------------------------------------------------------------------------- 68a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Public API 69a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair //------------------------------------------------------------------------- 70a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 71a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public boolean isCreateParentDirectoriesAutomatically() { 72a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return createParentDirectoriesAutomatically; 73a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 74a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 75a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public void setCreateParentDirectoriesAutomatically(boolean createParentDirectoriesAutomatically) { 76a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair this.createParentDirectoriesAutomatically = createParentDirectoriesAutomatically; 77a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 78a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 79a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public DirectoryListingFormatter getDirectoryListingFormatter() { 80a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return directoryListingFormatter; 81a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 82a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 83a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public void setDirectoryListingFormatter(DirectoryListingFormatter directoryListingFormatter) { 84a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair this.directoryListingFormatter = directoryListingFormatter; 85a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 86a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 87a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 88a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Add each of the entries in the specified List to this filesystem. Note that this does not affect 89a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * entries already existing within this filesystem. 90a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 91a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param entriesToAdd - the List of FileSystemEntry entries to add 92a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 93a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public void setEntries(List entriesToAdd) { 94a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair for (Iterator iter = entriesToAdd.iterator(); iter.hasNext();) { 95a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = (FileSystemEntry) iter.next(); 96a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair add(entry); 97a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 98a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 99a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 100a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 101a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Add the specified file system entry (file or directory) to this file system 102a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 103a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param entry - the FileSystemEntry to add 104a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 105a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public void add(FileSystemEntry entry) { 106a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String path = entry.getPath(); 107a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair checkForInvalidFilename(path); 108a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (getEntry(path) != null) { 109a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair throw new FileSystemException(path, "filesystem.pathAlreadyExists"); 110a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 111a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 112a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (!parentDirectoryExists(path)) { 113a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String parent = getParent(path); 114a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (createParentDirectoriesAutomatically) { 115a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair add(new DirectoryEntry(parent)); 116a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } else { 117a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair throw new FileSystemException(parent, "filesystem.parentDirectoryDoesNotExist"); 118a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 119a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 120a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 121a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Set lastModified, if not already set 122a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (entry.getLastModified() == null) { 123a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair entry.setLastModified(new Date()); 124a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 125a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 126a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair entries.put(getFileSystemEntryKey(path), entry); 127a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair entry.lockPath(); 128a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 129a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 130a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 131a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Delete the file or directory specified by the path. Return true if the file is successfully 132a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * deleted, false otherwise. If the path refers to a directory, it must be empty. Return false 133a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * if the path does not refer to a valid file or directory or if it is a non-empty directory. 134a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 135a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path of the file or directory to delete 136a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the file or directory is successfully deleted 137a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws org.mockftpserver.core.util.AssertFailedException 138a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * - if path is null 139a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#delete(java.lang.String) 140a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 141a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public boolean delete(String path) { 142a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(path, "path"); 143a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 144a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (getEntry(path) != null && !hasChildren(path)) { 145a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair removeEntry(path); 146a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return true; 147a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 148a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return false; 149a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 150a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 151a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 152a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if there exists a file or directory at the specified path 153a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 154a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 155a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the file/directory exists 156a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if path is null 157a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#exists(java.lang.String) 158a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 159a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public boolean exists(String path) { 160a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(path, "path"); 161a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return getEntry(path) != null; 162a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 163a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 164a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 165a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if the specified path designates an existing directory, false otherwise 166a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 167a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 168a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if path is a directory, false otherwise 169a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if path is null 170a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#isDirectory(java.lang.String) 171a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 172a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public boolean isDirectory(String path) { 173a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(path, "path"); 174a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = getEntry(path); 175a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return entry != null && entry.isDirectory(); 176a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 177a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 178a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 179a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if the specified path designates an existing file, false otherwise 180a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 181a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 182a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if path is a file, false otherwise 183a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if path is null 184a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#isFile(java.lang.String) 185a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 186a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public boolean isFile(String path) { 187a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(path, "path"); 188a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = getEntry(path); 189a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return entry != null && !entry.isDirectory(); 190a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 191a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 192a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 193a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the List of FileSystemEntry objects for the files in the specified directory or group of 194a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * files. If the path specifies a single file, then return a list with a single FileSystemEntry 195a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * object representing that file. If the path does not refer to an existing directory or 196a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * group of files, then an empty List is returned. 197a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 198a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path specifying a directory or group of files; may contain wildcards (? or *) 199a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the List of FileSystemEntry objects for the specified directory or file; may be empty 200a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#listFiles(java.lang.String) 201a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 202a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public List listFiles(String path) { 203a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (isFile(path)) { 204a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return Collections.singletonList(getEntry(path)); 205a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 206a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 207a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List entryList = new ArrayList(); 208a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List children = children(path); 209a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Iterator iter = children.iterator(); 210a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair while (iter.hasNext()) { 211a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String childPath = (String) iter.next(); 212a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry fileSystemEntry = getEntry(childPath); 213a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair entryList.add(fileSystemEntry); 214a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 215a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return entryList; 216a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 217a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 218a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 219a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the List of filenames in the specified directory path or file path. If the path specifies 220a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * a single file, then return that single filename. The returned filenames do not 221a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * include a path. If the path does not refer to a valid directory or file path, then an empty List 222a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * is returned. 223a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 224a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path specifying a directory or group of files; may contain wildcards (? or *) 225a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the List of filenames (not including paths) for all files in the specified directory 226a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * or file path; may be empty 227a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if path is null 228a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see org.mockftpserver.fake.filesystem.FileSystem#listNames(java.lang.String) 229a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 230a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public List listNames(String path) { 231a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (isFile(path)) { 232a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return Collections.singletonList(getName(path)); 233a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 234a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 235a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List filenames = new ArrayList(); 236a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List children = children(path); 237a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Iterator iter = children.iterator(); 238a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair while (iter.hasNext()) { 239a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String childPath = (String) iter.next(); 240a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry fileSystemEntry = getEntry(childPath); 241a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair filenames.add(fileSystemEntry.getName()); 242a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 243a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return filenames; 244a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 245a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 246a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 247a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Rename the file or directory. Specify the FROM path and the TO path. Throw an exception if the FROM path or 248a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * the parent directory of the TO path do not exist; or if the rename fails for another reason. 249a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 250a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param fromPath - the source (old) path + filename 251a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param toPath - the target (new) path + filename 252a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if fromPath or toPath is null 253a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws FileSystemException - if the rename fails. 254a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 255a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public void rename(String fromPath, String toPath) { 256a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(toPath, "toPath"); 257a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(fromPath, "fromPath"); 258a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 259a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = getRequiredEntry(fromPath); 260a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 261a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedFromPath = normalize(fromPath); 262a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedToPath = normalize(toPath); 263a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 264a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (!entry.isDirectory()) { 265a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair renamePath(entry, normalizedToPath); 266a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return; 267a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 268a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 269a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Create the TO directory entry first so that the destination path exists when you 270a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // move the children. Remove the FROM path after all children have been moved 271a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair add(new DirectoryEntry(normalizedToPath)); 272a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 273a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List children = descendents(fromPath); 274a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Iterator iter = children.iterator(); 275a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair while (iter.hasNext()) { 276a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String childPath = (String) iter.next(); 277a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry child = getRequiredEntry(childPath); 278a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedChildPath = normalize(child.getPath()); 279a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.isTrue(normalizedChildPath.startsWith(normalizedFromPath), "Starts with FROM path"); 280a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String childToPath = normalizedToPath + normalizedChildPath.substring(normalizedFromPath.length()); 281a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair renamePath(child, childToPath); 282a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 283a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.isTrue(children(normalizedFromPath).isEmpty(), "Must have no children: " + normalizedFromPath); 284a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair removeEntry(normalizedFromPath); 285a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 286a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 287a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 288a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see java.lang.Object#toString() 289a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 290a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public String toString() { 291a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return this.getClass().getName() + entries; 292a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 293a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 294a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 295a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the formatted directory listing entry for the file represented by the specified FileSystemEntry 296a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 297a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param fileSystemEntry - the FileSystemEntry representing the file or directory entry to be formatted 298a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the the formatted directory listing entry 299a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 300a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public String formatDirectoryListing(FileSystemEntry fileSystemEntry) { 301a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(directoryListingFormatter, "directoryListingFormatter"); 302a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(fileSystemEntry, "fileSystemEntry"); 303a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return directoryListingFormatter.format(fileSystemEntry); 304a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 305a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 306a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 307a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Build a path from the two path components. Concatenate path1 and path2. Insert the path 308a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * separator character in between if necessary (i.e., if both are non-empty and path1 does not already 309a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * end with a separator character AND path2 does not begin with one). 310a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 311a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path1 - the first path component may be null or empty 312a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path2 - the second path component may be null or empty 313a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the path resulting from concatenating path1 to path2 314a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 315a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public String path(String path1, String path2) { 316a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair StringBuffer buf = new StringBuffer(); 317a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (path1 != null && path1.length() > 0) { 318a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair buf.append(path1); 319a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 320a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (path2 != null && path2.length() > 0) { 321a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if ((path1 != null && path1.length() > 0) 322a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair && (!isSeparator(path1.charAt(path1.length() - 1))) 323a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair && (!isSeparator(path2.charAt(0)))) { 324a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair buf.append(this.getSeparator()); 325a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 326a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair buf.append(path2); 327a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 328a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return buf.toString(); 329a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 330a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 331a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 332a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the parent path of the specified path. If <code>path</code> specifies a filename, 333a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * then this method returns the path of the directory containing that file. If <code>path</code> 334a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * specifies a directory, the this method returns its parent directory. If <code>path</code> is 335a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * empty or does not have a parent component, then return an empty string. 336a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * <p/> 337a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * All path separators in the returned path are converted to the system-dependent separator character. 338a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 339a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 340a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the parent of the specified path, or null if <code>path</code> has no parent 341a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if path is null 342a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 343a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public String getParent(String path) { 344a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List parts = normalizedComponents(path); 345a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (parts.size() < 2) { 346a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return null; 347a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 348a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair parts.remove(parts.size() - 1); 349a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return componentsToPath(parts); 350a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 351a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 352a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 353a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Returns the name of the file or directory denoted by this abstract 354a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * pathname. This is just the last name in the pathname's name 355a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * sequence. If the pathname's name sequence is empty, then the empty string is returned. 356a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 357a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 358a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return The name of the file or directory denoted by this abstract pathname, or the 359a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * empty string if this pathname's name sequence is empty 360a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 361a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public String getName(String path) { 362a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(path, "path"); 363a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalized = normalize(path); 364a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair int separatorIndex = normalized.lastIndexOf(this.getSeparator()); 365a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return (separatorIndex == -1) ? normalized : normalized.substring(separatorIndex + 1); 366a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 367a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 368a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 369a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Returns the FileSystemEntry object representing the file system entry at the specified path, or null 370a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * if the path does not specify an existing file or directory within this file system. 371a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 372a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path of the file or directory within this file system 373a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the FileSystemEntry containing the information for the file or directory, or else null 374a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @see FileSystem#getEntry(String) 375a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 376a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public FileSystemEntry getEntry(String path) { 377a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return (FileSystemEntry) entries.get(getFileSystemEntryKey(path)); 378a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 379a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 380a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair //------------------------------------------------------------------------- 381a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Abstract Methods 382a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair //------------------------------------------------------------------------- 383a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 384a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 385a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 386a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the specified dir/file path name is valid according to the current filesystem. 387a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 388a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected abstract boolean isValidName(String path); 389a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 390a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 391a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the file system-specific file separator as a char 392a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 393a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected abstract char getSeparatorChar(); 394a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 395a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 396a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param pathComponent - the component (piece) of the path to check 397a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the specified path component is a root for this filesystem 398a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 399a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected abstract boolean isRoot(String pathComponent); 400a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 401a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 402a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if the specified char is a separator character for this filesystem 403a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 404a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param c - the character to test 405a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the specified char is a separator character 406a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 407a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected abstract boolean isSeparator(char c); 408a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 409a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair //------------------------------------------------------------------------- 410a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Internal Helper Methods 411a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair //------------------------------------------------------------------------- 412a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 413a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 414a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the file system-specific file separator as a String 415a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 416a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected String getSeparator() { 417a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return Character.toString(getSeparatorChar()); 418a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 419a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 420a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 421a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the normalized and unique key used to access the file system entry 422a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 423a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 424a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the corresponding normalized key 425a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 426a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected String getFileSystemEntryKey(String path) { 427a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return normalize(path); 428a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 429a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 430a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 431a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the standard, normalized form of the path. 432a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 433a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 434a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the path in a standard, unique, canonical form 435a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if path is null 436a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 437a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected String normalize(String path) { 438a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return componentsToPath(normalizedComponents(path)); 439a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 440a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 441a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 442a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Throw an InvalidFilenameException if the specified path is not valid. 443a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 444a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 445a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 446a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void checkForInvalidFilename(String path) { 447a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (!isValidName(path)) { 448a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair throw new InvalidFilenameException(path); 449a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 450a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 451a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 452a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 453a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Rename the file system entry to the specified path name 454a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 455a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param entry - the file system entry 456a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param toPath - the TO path (normalized) 457a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 458a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected void renamePath(FileSystemEntry entry, String toPath) { 459a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedFrom = normalize(entry.getPath()); 460a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedTo = normalize(toPath); 461a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.info("renaming from [" + normalizedFrom + "] to [" + normalizedTo + "]"); 462a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry newEntry = entry.cloneWithNewPath(normalizedTo); 463a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair add(newEntry); 464a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // Do this at the end, in case the addEntry() failed 465a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair removeEntry(normalizedFrom); 466a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 467a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 468a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 469a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the FileSystemEntry for the specified path. Throw FileSystemException if the 470a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * specified path does not exist. 471a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 472a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 473a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the FileSystemEntry 474a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws FileSystemException - if the specified path does not exist 475a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 476a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected FileSystemEntry getRequiredEntry(String path) { 477a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair FileSystemEntry entry = getEntry(path); 478a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (entry == null) { 479a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.error("Path does not exist: " + path); 480a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair throw new FileSystemException(normalize(path), "filesystem.pathDoesNotExist"); 481a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 482a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return entry; 483a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 484a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 485a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 486a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the components of the specified path as a List. The components are normalized, and 487a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * the returned List does not include path separator characters. 488a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 489a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 490a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the List of normalized components 491a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 492a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected List normalizedComponents(String path) { 493a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Assert.notNull(path, "path"); 494a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair char otherSeparator = this.getSeparatorChar() == '/' ? '\\' : '/'; 495a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String p = path.replace(otherSeparator, this.getSeparatorChar()); 496a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 497a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair // TODO better way to do this 498a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (p.equals(this.getSeparator())) { 499a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return Collections.singletonList(""); 500a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 501a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 502a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String[] parts = p.split("\\" + this.getSeparator()); 503a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List result = new ArrayList(); 504a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair for (int i = 0; i < parts.length; i++) { 505a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String part = parts[i]; 506a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (part.equals("..")) { 507a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair result.remove(result.size() - 1); 508a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } else if (!part.equals(".")) { 509a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair result.add(part); 510a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 511a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 512a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return result; 513a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 514a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 515a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 516a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Build a path from the specified list of path components 517a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 518a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param components - the list of path components 519a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the resulting path 520a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 521a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair protected String componentsToPath(List components) { 522a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (components.size() == 1) { 523a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String first = (String) components.get(0); 524a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (first.length() == 0 || isRoot(first)) { 525a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return first + this.getSeparator(); 526a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 527a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 528a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return StringUtil.join(components, this.getSeparator()); 529a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 530a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 531a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 532a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if the specified path designates an absolute file path. 533a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 534a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 535a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if path is absolute, false otherwise 536a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @throws AssertionError - if path is null 537a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 538a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair public boolean isAbsolute(String path) { 539a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return isValidName(path); 540a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 541a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 542a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 543a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if the specified path exists 544a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 545a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 546a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the path exists 547a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 548a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private boolean pathExists(String path) { 549a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return getEntry(path) != null; 550a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 551a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 552a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 553a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * If the specified path has a parent, then verify that the parent exists 554a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 555a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 556a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the parent of the specified path exists 557a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 558a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private boolean parentDirectoryExists(String path) { 559a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String parent = getParent(path); 560a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return parent == null || pathExists(parent); 561a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 562a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 563a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 564a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return true if the specified path represents a directory that contains one or more files or subdirectories 565a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 566a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 567a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return true if the path has child entries 568a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 569a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private boolean hasChildren(String path) { 570a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (!isDirectory(path)) { 571a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return false; 572a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 573a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String key = getFileSystemEntryKey(path); 574a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Iterator iter = entries.keySet().iterator(); 575a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair while (iter.hasNext()) { 576a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String p = (String) iter.next(); 577a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (p.startsWith(key) && !key.equals(p)) { 578a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return true; 579a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 580a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 581a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return false; 582a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 583a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 584a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 585a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the List of files or subdirectory paths that are descendents of the specified path 586a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 587a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 588a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the List of the paths for the files and subdirectories that are children, grandchildren, etc. 589a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 590a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private List descendents(String path) { 591a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (isDirectory(path)) { 592a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedPath = getFileSystemEntryKey(path); 593a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String separator = (normalizedPath.endsWith(getSeparator())) ? "" : getSeparator(); 594a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedDirPrefix = normalizedPath + separator; 595a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List descendents = new ArrayList(); 596a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Iterator iter = entries.keySet().iterator(); 597a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair while (iter.hasNext()) { 598a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String p = (String) iter.next(); 599a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (p.startsWith(normalizedDirPrefix) && !normalizedPath.equals(p)) { 600a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair descendents.add(p); 601a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 602a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 603a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return descendents; 604a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 605a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return Collections.EMPTY_LIST; 606a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 607a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 608a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair /** 609a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * Return the List of files or subdirectory paths that are children of the specified path 610a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * 611a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @param path - the path 612a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair * @return the List of the paths for the files and subdirectories that are children 613a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair */ 614a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private List children(String path) { 615a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String lastComponent = getName(path); 616a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair boolean containsWildcards = PatternUtil.containsWildcards(lastComponent); 617a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String dir = containsWildcards ? getParent(path) : path; 618a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String pattern = containsWildcards ? PatternUtil.convertStringWithWildcardsToRegex(getName(path)) : null; 619a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair LOG.debug("path=" + path + " lastComponent=" + lastComponent + " containsWildcards=" + containsWildcards + " dir=" + dir + " pattern=" + pattern); 620a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 621a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List descendents = descendents(dir); 622a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair List children = new ArrayList(); 623a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String normalizedDir = normalize(dir); 624a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair Iterator iter = descendents.iterator(); 625a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair while (iter.hasNext()) { 626a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair String descendentPath = (String) iter.next(); 627a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 628a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair boolean patternEmpty = pattern == null || pattern.length() == 0; 629a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair if (normalizedDir.equals(getParent(descendentPath)) && 630a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair (patternEmpty || (getName(descendentPath).matches(pattern)))) { 631a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair children.add(descendentPath); 632a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 633a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 634a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair return children; 635a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 636a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 637a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair private void removeEntry(String path) { 638a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair entries.remove(getFileSystemEntryKey(path)); 639a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair } 640a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair 641a0ad464efff5f5e2d2523a3522cce6823ce05858chrismair}