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