001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.util.file; 018 019import java.io.IOException; 020import java.net.URI; 021import java.util.ArrayList; 022import java.util.Arrays; 023import java.util.List; 024 025import org.apache.wicket.util.lang.Bytes; 026 027/** 028 * This folder subclass provides some type safety and extensibility for "files" that hold other 029 * files. 030 * 031 * @author Jonathan Locke 032 */ 033public class Folder extends File 034{ 035 /** 036 * Filter for files 037 * 038 * @author Jonathan Locke 039 */ 040 public static interface FileFilter 041 { 042 /** 043 * File filter that matches all files 044 */ 045 public static FileFilter ALL_FILES = new FileFilter() 046 { 047 @Override 048 public boolean accept(final File file) 049 { 050 return true; 051 } 052 }; 053 054 /** 055 * @param file 056 * The file to test 057 * @return True if the file should be accepted 058 */ 059 public boolean accept(File file); 060 } 061 062 /** 063 * Filter for folders 064 * 065 * @author Jonathan Locke 066 */ 067 public static interface FolderFilter 068 { 069 /** 070 * @param folder 071 * The folder to test 072 * @return True if the file should be accepted 073 */ 074 public boolean accept(Folder folder); 075 } 076 077 private static final long serialVersionUID = 1L; 078 079 /** 080 * Constructor. 081 * 082 * @param parent 083 * parent 084 * @param child 085 * child 086 */ 087 public Folder(final Folder parent, final String child) 088 { 089 super(parent, child); 090 } 091 092 /** 093 * Construct. 094 * 095 * @param file 096 * File 097 */ 098 public Folder(final java.io.File file) 099 { 100 this(file.getPath()); 101 } 102 103 /** 104 * Constructor. 105 * 106 * @param pathname 107 * path name 108 */ 109 public Folder(final String pathname) 110 { 111 super(pathname); 112 } 113 114 /** 115 * Constructor. 116 * 117 * @param parent 118 * parent 119 * @param child 120 * child 121 */ 122 public Folder(final String parent, final String child) 123 { 124 super(parent, child); 125 } 126 127 /** 128 * Constructor. 129 * 130 * @param uri 131 * folder uri 132 */ 133 public Folder(final URI uri) 134 { 135 super(uri); 136 } 137 138 /** 139 * Does a mkdirs() on this folder if it does not exist. If the folder cannot be created, an 140 * IOException is thrown. 141 * 142 * @throws IOException 143 * Thrown if folder cannot be created 144 */ 145 public void ensureExists() throws IOException 146 { 147 if (!exists() && !mkdirs()) 148 { 149 throw new IOException("Unable to create folder " + this); 150 } 151 } 152 153 /** 154 * @param name 155 * Name of child folder 156 * @return Child file object 157 */ 158 public Folder folder(final String name) 159 { 160 return new Folder(this, name); 161 } 162 163 /** 164 * @return Disk space free on the partition where this folder lives 165 */ 166 public Bytes freeDiskSpace() 167 { 168 return Bytes.bytes(super.getFreeSpace()); 169 } 170 171 /** 172 * @return Files in this folder 173 */ 174 public File[] getFiles() 175 { 176 return getFiles(FileFilter.ALL_FILES); 177 } 178 179 /** 180 * @return All files nested within this folder 181 */ 182 public File[] getNestedFiles() 183 { 184 return getNestedFiles(FileFilter.ALL_FILES); 185 } 186 187 /** 188 * Gets files in this folder matching a given filter recursively. 189 * 190 * @param filter 191 * The filter 192 * @return The list of files 193 */ 194 public File[] getNestedFiles(final FileFilter filter) 195 { 196 final List<File> files = new ArrayList<>(); 197 files.addAll(Arrays.asList(getFiles(filter))); 198 final Folder[] folders = getFolders(); 199 for (Folder folder : folders) 200 { 201 files.addAll(Arrays.asList(folder.getNestedFiles(filter))); 202 } 203 return files.toArray(new File[files.size()]); 204 } 205 206 /** 207 * @param filter 208 * File filter 209 * @return Files 210 */ 211 public File[] getFiles(final FileFilter filter) 212 { 213 // Get list of java.io files 214 final java.io.File[] files = listFiles(new java.io.FileFilter() 215 { 216 /** 217 * @see java.io.FileFilter#accept(java.io.File) 218 */ 219 @Override 220 public boolean accept(final java.io.File file) 221 { 222 return file.isFile() && filter.accept(new File(file)); 223 } 224 }); 225 226 // Convert java.io files to org.apache.wicket files 227 if (files != null) 228 { 229 final File[] wicketFiles = new File[files.length]; 230 for (int i = 0; i < files.length; i++) 231 { 232 wicketFiles[i] = new File(files[i]); 233 } 234 return wicketFiles; 235 } 236 return new File[0]; 237 } 238 239 /** 240 * Gets all folders in this folder, except "." and ".." 241 * 242 * @return Folders 243 */ 244 public Folder[] getFolders() 245 { 246 return getFolders(new FolderFilter() 247 { 248 @Override 249 public boolean accept(final Folder folder) 250 { 251 final String name = folder.getName(); 252 return !name.equals(".") && !name.equals(".."); 253 } 254 }); 255 } 256 257 /** 258 * @param filter 259 * Folder filter 260 * @return Folders 261 */ 262 public Folder[] getFolders(final FolderFilter filter) 263 { 264 // Get java io files that are directories matching the filter 265 final java.io.File[] files = listFiles(new java.io.FileFilter() 266 { 267 /** 268 * @see java.io.FileFilter#accept(java.io.File) 269 */ 270 @Override 271 public boolean accept(final java.io.File file) 272 { 273 return file.isDirectory() && filter.accept(new Folder(file.getPath())); 274 } 275 }); 276 277 // Convert 278 if (files != null) 279 { 280 final Folder[] wicketFolders = new Folder[files.length]; 281 for (int i = 0; i < files.length; i++) 282 { 283 wicketFolders[i] = new Folder(files[i]); 284 } 285 return wicketFolders; 286 } 287 return new Folder[0]; 288 } 289 290 /** 291 * Removes this folder and everything in it, recursively. A best effort is made to remove nested 292 * folders and files in depth-first order. 293 * 294 * @return True if the folder was successfully removed 295 */ 296 @Override 297 public boolean remove() 298 { 299 return remove(this); 300 } 301 302 /** 303 * Removes all the files in this folder. 304 * 305 * @return True if any files were successfully removed 306 */ 307 public boolean removeFiles() 308 { 309 final File[] files = getFiles(); 310 boolean success = true; 311 for (File file : files) 312 { 313 success = file.remove() && success; 314 } 315 return success; 316 } 317 318 /** 319 * Removes everything in the given folder and then the folder itself. 320 * 321 * @param folder 322 * The folder 323 * @return True if the folder was successfully removed 324 */ 325 private boolean remove(final Folder folder) 326 { 327 final Folder[] folders = getFolders(); 328 boolean success = true; 329 for (Folder subfolder : folders) 330 { 331 success = subfolder.remove() && success; 332 } 333 success = removeFiles() && success; 334 return folder.delete() && success; 335 } 336}