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.commons.vfs2.provider; 018 019import java.io.InputStream; 020import java.io.OutputStream; 021import java.security.cert.Certificate; 022import java.util.HashSet; 023import java.util.Map; 024import java.util.Set; 025 026import org.apache.commons.vfs2.FileChangeEvent; 027import org.apache.commons.vfs2.FileContentInfo; 028import org.apache.commons.vfs2.FileListener; 029import org.apache.commons.vfs2.FileName; 030import org.apache.commons.vfs2.FileNotFolderException; 031import org.apache.commons.vfs2.FileObject; 032import org.apache.commons.vfs2.FileSystemException; 033import org.apache.commons.vfs2.FileType; 034import org.apache.commons.vfs2.RandomAccessContent; 035import org.apache.commons.vfs2.util.RandomAccessMode; 036import org.apache.commons.vfs2.util.WeakRefFileListener; 037 038/** 039 * A file backed by another file. 040 * <p> 041 * TODO - Extract subclass that overlays the children. 042 * 043 * @param <AFS> A subclass of AbstractFileSystem. 044 */ 045public class DelegateFileObject<AFS extends AbstractFileSystem> extends AbstractFileObject<AFS> 046 implements FileListener { 047 private FileObject file; 048 private final Set<String> children = new HashSet<>(); 049 private boolean ignoreEvent; 050 051 public DelegateFileObject(final AbstractFileName name, final AFS fileSystem, final FileObject file) 052 throws FileSystemException { 053 super(name, fileSystem); 054 this.file = file; 055 if (file != null) { 056 WeakRefFileListener.installListener(file, this); 057 } 058 } 059 060 /** 061 * Get access to the delegated file. 062 * 063 * @return The FileObject. 064 * @since 2.0 065 */ 066 public FileObject getDelegateFile() { 067 return file; 068 } 069 070 /** 071 * Adds a child to this file. 072 * 073 * @param baseName The base FileName. 074 * @param type The FileType. 075 * @throws Exception if an error occurs. 076 */ 077 public void attachChild(final FileName baseName, final FileType type) throws Exception { 078 final FileType oldType = doGetType(); 079 if (children.add(baseName.getBaseName())) { 080 childrenChanged(baseName, type); 081 } 082 maybeTypeChanged(oldType); 083 } 084 085 /** 086 * Attaches or detaches the target file. 087 * 088 * @param file The FileObject. 089 * @throws Exception if an error occurs. 090 */ 091 public void setFile(final FileObject file) throws Exception { 092 final FileType oldType = doGetType(); 093 094 if (file != null) { 095 WeakRefFileListener.installListener(file, this); 096 } 097 this.file = file; 098 maybeTypeChanged(oldType); 099 } 100 101 /** 102 * Checks whether the file's type has changed, and fires the appropriate events. 103 * 104 * @param oldType The old FileType. 105 * @throws Exception if an error occurs. 106 */ 107 private void maybeTypeChanged(final FileType oldType) throws Exception { 108 final FileType newType = doGetType(); 109 if (oldType == FileType.IMAGINARY && newType != FileType.IMAGINARY) { 110 handleCreate(newType); 111 } else if (oldType != FileType.IMAGINARY && newType == FileType.IMAGINARY) { 112 handleDelete(); 113 } 114 } 115 116 /** 117 * Determines the type of the file, returns null if the file does not exist. 118 */ 119 @Override 120 protected FileType doGetType() throws FileSystemException { 121 if (file != null) { 122 return file.getType(); 123 } else if (children.size() > 0) { 124 return FileType.FOLDER; 125 } else { 126 return FileType.IMAGINARY; 127 } 128 } 129 130 /** 131 * Determines if this file can be read. 132 */ 133 @Override 134 protected boolean doIsReadable() throws FileSystemException { 135 if (file != null) { 136 return file.isReadable(); 137 } 138 return true; 139 } 140 141 /** 142 * Determines if this file can be written to. 143 */ 144 @Override 145 protected boolean doIsWriteable() throws FileSystemException { 146 if (file != null) { 147 return file.isWriteable(); 148 } 149 return false; 150 } 151 152 /** 153 * Determines if this file is executable. 154 */ 155 @Override 156 protected boolean doIsExecutable() throws FileSystemException { 157 if (file != null) { 158 return file.isExecutable(); 159 } 160 return false; 161 } 162 163 /** 164 * Determines if this file is hidden. 165 */ 166 @Override 167 protected boolean doIsHidden() throws FileSystemException { 168 if (file != null) { 169 return file.isHidden(); 170 } 171 return false; 172 } 173 174 /** 175 * Lists the children of the file. 176 */ 177 @Override 178 protected String[] doListChildren() throws Exception { 179 if (file != null) { 180 final FileObject[] children; 181 182 try { 183 children = file.getChildren(); 184 } 185 // VFS-210 186 catch (final FileNotFolderException e) { 187 throw new FileNotFolderException(getName(), e); 188 } 189 190 final String[] childNames = new String[children.length]; 191 for (int i = 0; i < children.length; i++) { 192 childNames[i] = children[i].getName().getBaseName(); 193 } 194 return childNames; 195 } 196 return children.toArray(new String[children.size()]); 197 } 198 199 /** 200 * Creates this file as a folder. 201 */ 202 @Override 203 protected void doCreateFolder() throws Exception { 204 ignoreEvent = true; 205 try { 206 file.createFolder(); 207 } finally { 208 ignoreEvent = false; 209 } 210 } 211 212 /** 213 * Deletes the file. 214 */ 215 @Override 216 protected void doDelete() throws Exception { 217 ignoreEvent = true; 218 try { 219 file.delete(); 220 } finally { 221 ignoreEvent = false; 222 } 223 } 224 225 /** 226 * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns 227 * {@link FileType#FILE}. 228 */ 229 @Override 230 protected long doGetContentSize() throws Exception { 231 return file.getContent().getSize(); 232 } 233 234 /** 235 * Returns the attributes of this file. 236 */ 237 @Override 238 protected Map<String, Object> doGetAttributes() throws Exception { 239 return file.getContent().getAttributes(); 240 } 241 242 /** 243 * Sets an attribute of this file. 244 */ 245 @Override 246 protected void doSetAttribute(final String atttrName, final Object value) throws Exception { 247 file.getContent().setAttribute(atttrName, value); 248 } 249 250 /** 251 * Returns the certificates of this file. 252 */ 253 @Override 254 protected Certificate[] doGetCertificates() throws Exception { 255 return file.getContent().getCertificates(); 256 } 257 258 /** 259 * Returns the last-modified time of this file. 260 */ 261 @Override 262 protected long doGetLastModifiedTime() throws Exception { 263 return file.getContent().getLastModifiedTime(); 264 } 265 266 /** 267 * Sets the last-modified time of this file. 268 * 269 * @since 2.0 270 */ 271 @Override 272 protected boolean doSetLastModifiedTime(final long modtime) throws Exception { 273 file.getContent().setLastModifiedTime(modtime); 274 return true; 275 } 276 277 /** 278 * Creates an input stream to read the file content from. 279 */ 280 @Override 281 protected InputStream doGetInputStream() throws Exception { 282 return file.getContent().getInputStream(); 283 } 284 285 /** 286 * Creates an output stream to write the file content to. 287 */ 288 @Override 289 protected OutputStream doGetOutputStream(final boolean bAppend) throws Exception { 290 return file.getContent().getOutputStream(bAppend); 291 } 292 293 /** 294 * Called when a file is created. 295 * 296 * @param event The FileChangeEvent. 297 * @throws Exception if an error occurs. 298 */ 299 @Override 300 public void fileCreated(final FileChangeEvent event) throws Exception { 301 if (event.getFile() != file) { 302 return; 303 } 304 if (!ignoreEvent) { 305 handleCreate(file.getType()); 306 } 307 } 308 309 /** 310 * Called when a file is deleted. 311 * 312 * @param event The FileChangeEvent. 313 * @throws Exception if an error occurs. 314 */ 315 @Override 316 public void fileDeleted(final FileChangeEvent event) throws Exception { 317 if (event.getFile() != file) { 318 return; 319 } 320 if (!ignoreEvent) { 321 handleDelete(); 322 } 323 } 324 325 /** 326 * Called when a file is changed. 327 * <p> 328 * This will only happen if you monitor the file using {@link org.apache.commons.vfs2.FileMonitor}. 329 * 330 * @param event The FileChangeEvent. 331 * @throws Exception if an error occurs. 332 */ 333 @Override 334 public void fileChanged(final FileChangeEvent event) throws Exception { 335 if (event.getFile() != file) { 336 return; 337 } 338 if (!ignoreEvent) { 339 handleChanged(); 340 } 341 } 342 343 /** 344 * Close the delegated file. 345 * 346 * @throws FileSystemException if an error occurs. 347 */ 348 @Override 349 public void close() throws FileSystemException { 350 super.close(); 351 352 if (file != null) { 353 file.close(); 354 } 355 } 356 357 /** 358 * Refresh file information. 359 * 360 * @throws FileSystemException if an error occurs. 361 * @since 2.0 362 */ 363 @Override 364 public void refresh() throws FileSystemException { 365 super.refresh(); 366 if (file != null) { 367 file.refresh(); 368 } 369 } 370 371 /** 372 * Return file content info. 373 * 374 * @return the file content info of the delegee. 375 * @throws Exception Any thrown Exception is wrapped in FileSystemException. 376 * @since 2.0 377 */ 378 protected FileContentInfo doGetContentInfo() throws Exception { 379 return file.getContent().getContentInfo(); 380 } 381 382 /** 383 * Renames the file. 384 * 385 * @param newFile the new location/name. 386 * @throws Exception Any thrown Exception is wrapped in FileSystemException. 387 * @since 2.0 388 */ 389 @Override 390 protected void doRename(final FileObject newFile) throws Exception { 391 file.moveTo(((DelegateFileObject) newFile).file); 392 } 393 394 /** 395 * Removes an attribute of this file. 396 * 397 * @since 2.0 398 */ 399 @Override 400 protected void doRemoveAttribute(final String atttrName) throws Exception { 401 file.getContent().removeAttribute(atttrName); 402 } 403 404 /** 405 * Creates access to the file for random i/o. 406 * 407 * @since 2.0 408 */ 409 @Override 410 protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception { 411 return file.getContent().getRandomAccessContent(mode); 412 } 413}