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