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.local; 018 019import java.io.File; 020import java.io.FileInputStream; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.OutputStream; 024import java.nio.file.Files; 025 026import org.apache.commons.io.file.PathUtils; 027import org.apache.commons.vfs2.FileObject; 028import org.apache.commons.vfs2.FileSystemException; 029import org.apache.commons.vfs2.FileType; 030import org.apache.commons.vfs2.RandomAccessContent; 031import org.apache.commons.vfs2.provider.AbstractFileName; 032import org.apache.commons.vfs2.provider.AbstractFileObject; 033import org.apache.commons.vfs2.provider.UriParser; 034import org.apache.commons.vfs2.util.FileObjectUtils; 035import org.apache.commons.vfs2.util.RandomAccessMode; 036 037/** 038 * A file object implementation which uses direct file access. 039 */ 040public class LocalFile extends AbstractFileObject<LocalFileSystem> { 041 042 private final String rootFile; 043 044 private File file; 045 046 /** 047 * Creates a non-root file. 048 * 049 * @param fileSystem the file system this file belongs to. 050 * @param rootFile the root file for the file system. 051 * @param name the file name on this file system. 052 */ 053 protected LocalFile(final LocalFileSystem fileSystem, final String rootFile, final AbstractFileName name) { 054 super(name, fileSystem); 055 this.rootFile = rootFile; 056 } 057 058 /** 059 * Attaches this file object to its file resource. 060 */ 061 @Override 062 protected void doAttach() throws Exception { 063 if (file == null) { 064 // Remove the "file:///" 065 // LocalFileName localFileName = (LocalFileName) getName(); 066 final String fileName = rootFile + getName().getPathDecoded(); 067 // fileName = UriParser.decode(fileName); 068 file = new File(fileName); 069 } 070 } 071 072 /** 073 * Creates this folder. 074 */ 075 @Override 076 protected void doCreateFolder() throws Exception { 077 if (!file.mkdirs()) { 078 throw new FileSystemException("vfs.provider.local/create-folder.error", file); 079 } 080 } 081 082 /** 083 * Deletes this file, and all children. 084 */ 085 @Override 086 protected void doDelete() throws Exception { 087 if (!file.delete()) { 088 throw new FileSystemException("vfs.provider.local/delete-file.error", file); 089 } 090 } 091 092 /** 093 * Returns the size of the file content (in bytes). 094 */ 095 @Override 096 protected long doGetContentSize() throws Exception { 097 return file.length(); 098 } 099 100 /** 101 * Creates an input stream to read the file contents. 102 */ 103 @Override 104 protected InputStream doGetInputStream(final int bufferSize) throws Exception { 105 return new FileInputStream(file); 106 } 107 108 /** 109 * Gets the last modified time of this file. 110 */ 111 @Override 112 protected long doGetLastModifiedTime() throws FileSystemException { 113 // Workaround OpenJDK 8 and 9 bug JDK-8177809 114 // https://bugs.openjdk.java.net/browse/JDK-8177809 115 try { 116 return Files.getLastModifiedTime(file.toPath()).toMillis(); 117 } catch (final IOException e) { 118 throw new FileSystemException("vfs.provider/get-last-modified.error", file, e); 119 } 120 } 121 122 /** 123 * Creates an output stream to write the file content to. 124 */ 125 @Override 126 protected OutputStream doGetOutputStream(final boolean append) throws IOException { 127 return PathUtils.newOutputStream(file.toPath(), append); 128 } 129 130 @Override 131 protected RandomAccessContent doGetRandomAccessContent(final RandomAccessMode mode) throws Exception { 132 return new LocalFileRandomAccessContent(file, mode); 133 } 134 135 /** 136 * Returns the file's type. 137 */ 138 @Override 139 protected FileType doGetType() { 140 if (!file.exists()) { 141 return FileType.IMAGINARY; 142 } 143 if (file.isDirectory()) { 144 return FileType.FOLDER; 145 } 146 return FileType.FILE; 147 } 148 149 /** 150 * Determines if this file is hidden. 151 */ 152 @Override 153 protected boolean doIsExecutable() { 154 return file.canExecute(); 155 } 156 157 /** 158 * Determines if this file is hidden. 159 */ 160 @Override 161 protected boolean doIsHidden() { 162 return file.isHidden(); 163 } 164 165 /** 166 * Determines if this file can be read. 167 */ 168 @Override 169 protected boolean doIsReadable() throws FileSystemException { 170 return file.canRead(); 171 } 172 173 @Override 174 protected boolean doIsSameFile(final FileObject destFile) throws FileSystemException { 175 if (!FileObjectUtils.isInstanceOf(destFile, LocalFile.class)) { 176 return false; 177 } 178 179 final LocalFile destLocalFile = (LocalFile) FileObjectUtils.getAbstractFileObject(destFile); 180 if (!exists() || !destLocalFile.exists()) { 181 return false; 182 } 183 184 try { 185 return file.getCanonicalPath().equals(destLocalFile.file.getCanonicalPath()); 186 } catch (final IOException e) { 187 throw new FileSystemException(e); 188 } 189 } 190 191 /** 192 * Determines if this file is a symbolic link. 193 * 194 * @since 2.4 195 */ 196 @Override 197 protected boolean doIsSymbolicLink() throws FileSystemException { 198 return Files.isSymbolicLink(file.toPath()); 199 } 200 201 /** 202 * Determines if this file can be written to. 203 */ 204 @Override 205 protected boolean doIsWriteable() throws FileSystemException { 206 return file.canWrite(); 207 } 208 209 /** 210 * Returns the children of the file. 211 */ 212 @Override 213 protected String[] doListChildren() throws Exception { 214 return UriParser.encode(file.list()); 215 } 216 217 /** 218 * rename this file 219 */ 220 @Override 221 protected void doRename(final FileObject newFile) throws Exception { 222 final LocalFile newLocalFile = (LocalFile) FileObjectUtils.getAbstractFileObject(newFile); 223 224 if (!file.renameTo(newLocalFile.getLocalFile())) { 225 throw new FileSystemException("vfs.provider.local/rename-file.error", file.toString(), newFile.toString()); 226 } 227 } 228 229 @Override 230 protected boolean doSetExecutable(final boolean executable, final boolean ownerOnly) throws Exception { 231 return file.setExecutable(executable, ownerOnly); 232 } 233 234 /** 235 * Sets the last modified time of this file. 236 * 237 * @since 2.0 238 */ 239 @Override 240 protected boolean doSetLastModifiedTime(final long modtime) throws FileSystemException { 241 return file.setLastModified(modtime); 242 } 243 244 @Override 245 protected boolean doSetReadable(final boolean readable, final boolean ownerOnly) throws Exception { 246 return file.setReadable(readable, ownerOnly); 247 } 248 249 @Override 250 protected boolean doSetWritable(final boolean writable, final boolean ownerOnly) throws Exception { 251 return file.setWritable(writable, ownerOnly); 252 } 253 254 /** 255 * Gets the local file that this file object represents. 256 * 257 * @return the local file that this file object represents. 258 */ 259 protected File getLocalFile() { 260 return file; 261 } 262 263 /** 264 * Returns the URI of the file. 265 * 266 * @return The URI of the file. 267 */ 268 @Override 269 public String toString() { 270 try { 271 // VFS-325: URI may contain percent-encoded values as part of file name, so decode 272 // those characters before returning 273 return UriParser.decode(getName().getURI()); 274 } catch (final FileSystemException e) { 275 return getName().getURI(); 276 } 277 } 278}