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