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