1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.vfs2.provider.zip;
18
19 import java.io.InputStream;
20 import java.util.HashSet;
21 import java.util.zip.ZipEntry;
22
23 import org.apache.commons.io.function.Uncheck;
24 import org.apache.commons.lang3.ArrayUtils;
25 import org.apache.commons.vfs2.FileName;
26 import org.apache.commons.vfs2.FileSystemException;
27 import org.apache.commons.vfs2.FileType;
28 import org.apache.commons.vfs2.provider.AbstractFileName;
29 import org.apache.commons.vfs2.provider.AbstractFileObject;
30
31 /**
32 * A file in a ZIP file system.
33 */
34 public class ZipFileObject extends AbstractFileObject<ZipFileSystem> {
35
36 /** The ZipEntry. */
37 protected ZipEntry entry;
38 private final HashSet<String> children = new HashSet<>();
39 private FileType type;
40
41 /**
42 * Constructs a new instance.
43 *
44 * @param fileName the file name.
45 * @param entry The zip entry.
46 * @param fileSystem the file system.
47 * @param zipExists whether the zip file exists.
48 */
49 protected ZipFileObject(final AbstractFileName fileName, final ZipEntry entry, final ZipFileSystem fileSystem, final boolean zipExists) {
50 super(fileName, fileSystem);
51 setZipEntry(entry);
52 if (!zipExists) {
53 type = FileType.IMAGINARY;
54 }
55 }
56
57 /**
58 * Attaches a child.
59 * <p>
60 * TODO: Shouldn't this method have package-only visibility? Cannot change this without breaking binary
61 * compatibility.
62 * </p>
63 *
64 * @param childName The name of the child.
65 */
66 public void attachChild(final FileName childName) {
67 children.add(childName.getBaseName());
68 }
69
70 @Override
71 protected void doAttach() throws Exception {
72 getAbstractFileSystem().getZipFile();
73 }
74
75 @Override
76 protected void doDetach() throws Exception {
77 final ZipFileSystem afs = getAbstractFileSystem();
78 if (!afs.isOpen()) {
79 afs.close();
80 }
81 }
82
83 /**
84 * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
85 * {@link FileType#FILE}.
86 */
87 @Override
88 protected long doGetContentSize() {
89 return entry.getSize();
90 }
91
92 /**
93 * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
94 * {@link FileType#FILE}. The input stream returned by this method is guaranteed to be closed before this method is
95 * called again.
96 */
97 @Override
98 protected InputStream doGetInputStream(final int bufferSize) throws Exception {
99 // VFS-210: zip allows to gather an input stream even from a directory and will
100 // return -1 on the first read. getType should not be expensive and keeps the tests
101 // running
102 if (!getType().hasContent()) {
103 throw new FileSystemException("vfs.provider/read-not-file.error", getName());
104 }
105
106 return getAbstractFileSystem().getZipFile().getInputStream(entry);
107 }
108
109 /**
110 * Returns the last modified time of this file.
111 */
112 @Override
113 protected long doGetLastModifiedTime() throws Exception {
114 return entry.getTime();
115 }
116
117 /**
118 * Returns the file's type.
119 */
120 @Override
121 protected FileType doGetType() {
122 return type;
123 }
124
125 /**
126 * Lists the children of the file.
127 */
128 @Override
129 protected String[] doListChildren() {
130 if (!Uncheck.get(this::getType).hasChildren()) {
131 return null;
132 }
133 return children.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
134 }
135
136 /**
137 * Determines if this file can be written to.
138 *
139 * @return {@code true} if this file is writable, {@code false} if not.
140 * @throws FileSystemException if an error occurs.
141 */
142 @Override
143 public boolean isWriteable() throws FileSystemException {
144 return false;
145 }
146
147 /**
148 * Sets the details for this file object.
149 *
150 * @param entry ZIP information related to this file.
151 */
152 protected void setZipEntry(final ZipEntry entry) {
153 if (this.entry != null) {
154 return;
155 }
156
157 if (entry == null || entry.isDirectory()) {
158 type = FileType.FOLDER;
159 } else {
160 type = FileType.FILE;
161 }
162
163 this.entry = entry;
164 }
165 }