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.tar;
018
019import java.io.InputStream;
020import java.util.HashSet;
021
022import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
023import org.apache.commons.vfs2.FileName;
024import org.apache.commons.vfs2.FileSystemException;
025import org.apache.commons.vfs2.FileType;
026import org.apache.commons.vfs2.provider.AbstractFileName;
027import org.apache.commons.vfs2.provider.AbstractFileObject;
028
029/**
030 * A file in a Tar file system.
031 */
032public class TarFileObject extends AbstractFileObject<TarFileSystem> {
033    /** The TarArchiveEntry */
034    private TarArchiveEntry entry;
035    private final HashSet<String> children = new HashSet<>();
036    private FileType type;
037
038    protected TarFileObject(final AbstractFileName name, final TarArchiveEntry entry, final TarFileSystem fs,
039            final boolean tarExists) throws FileSystemException {
040        super(name, fs);
041        setTarEntry(entry);
042        if (!tarExists) {
043            type = FileType.IMAGINARY;
044        }
045    }
046
047    /**
048     * Sets the details for this file object.
049     *
050     * Consider this method package private. TODO Might be made package private in the next major version.
051     */
052    protected void setTarEntry(final TarArchiveEntry entry) {
053        if (this.entry != null) {
054            return;
055        }
056
057        if (entry == null || entry.isDirectory()) {
058            type = FileType.FOLDER;
059        } else {
060            type = FileType.FILE;
061        }
062
063        this.entry = entry;
064    }
065
066    /**
067     * Attaches a child.
068     *
069     * @param childName Name of child to remember.
070     */
071    protected void attachChild(final FileName childName) {
072        children.add(childName.getBaseName());
073    }
074
075    /**
076     * Determines if this file can be written to.
077     *
078     * @return {@code true} if this file is writeable, {@code false} if not.
079     * @throws FileSystemException if an error occurs.
080     */
081    @Override
082    public boolean isWriteable() throws FileSystemException {
083        return false;
084    }
085
086    /**
087     * Returns the file's type.
088     */
089    @Override
090    protected FileType doGetType() {
091        return type;
092    }
093
094    /**
095     * Lists the children of the file.
096     */
097    @Override
098    protected String[] doListChildren() {
099        try {
100            if (!getType().hasChildren()) {
101                return null;
102            }
103        } catch (final FileSystemException e) {
104            // should not happen as the type has already been cached.
105            throw new RuntimeException(e);
106        }
107
108        return children.toArray(new String[children.size()]);
109    }
110
111    /**
112     * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
113     * {@link FileType#FILE}.
114     */
115    @Override
116    protected long doGetContentSize() {
117        if (entry == null) {
118            return 0;
119        }
120
121        return entry.getSize();
122    }
123
124    /**
125     * Returns the last modified time of this file.
126     */
127    @Override
128    protected long doGetLastModifiedTime() throws Exception {
129        if (entry == null) {
130            return 0;
131        }
132
133        return entry.getModTime().getTime();
134    }
135
136    /**
137     * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
138     * {@link FileType#FILE}. The input stream returned by this method is guaranteed to be closed before this method is
139     * called again.
140     */
141    @Override
142    protected InputStream doGetInputStream() throws Exception {
143        // VFS-210: zip allows to gather an input stream even from a directory and will
144        // return -1 on the first read. getType should not be expensive and keeps the tests
145        // running
146        if (!getType().hasContent()) {
147            throw new FileSystemException("vfs.provider/read-not-file.error", getName());
148        }
149
150        return getAbstractFileSystem().getInputStream(entry);
151    }
152}