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.lang3.ArrayUtils;
24 import org.apache.commons.vfs2.FileName;
25 import org.apache.commons.vfs2.FileSystemException;
26 import org.apache.commons.vfs2.FileType;
27 import org.apache.commons.vfs2.provider.AbstractFileName;
28 import org.apache.commons.vfs2.provider.AbstractFileObject;
29
30 /**
31 * A file in a ZIP file system.
32 */
33 public class ZipFileObject extends AbstractFileObject<ZipFileSystem> {
34
35 /** The ZipEntry. */
36 protected ZipEntry entry;
37 private final HashSet<String> children = new HashSet<>();
38 private FileType type;
39
40 protected ZipFileObject(final AbstractFileName name, final ZipEntry entry, final ZipFileSystem fs,
41 final boolean zipExists) {
42 super(name, fs);
43 setZipEntry(entry);
44 if (!zipExists) {
45 type = FileType.IMAGINARY;
46 }
47 }
48
49 /**
50 * Sets the details for this file object.
51 *
52 * @param entry ZIP information related to this file.
53 */
54 protected void setZipEntry(final ZipEntry entry) {
55 if (this.entry != null) {
56 return;
57 }
58
59 if (entry == null || entry.isDirectory()) {
60 type = FileType.FOLDER;
61 } else {
62 type = FileType.FILE;
63 }
64
65 this.entry = entry;
66 }
67
68 /**
69 * Attaches a child.
70 * <p>
71 * TODO: Shouldn't this method have package-only visibility? Cannot change this without breaking binary
72 * compatibility.
73 * </p>
74 *
75 * @param childName The name of the child.
76 */
77 public void attachChild(final FileName childName) {
78 children.add(childName.getBaseName());
79 }
80
81 /**
82 * Determines if this file can be written to.
83 *
84 * @return {@code true} if this file is writable, {@code false} if not.
85 * @throws FileSystemException if an error occurs.
86 */
87 @Override
88 public boolean isWriteable() throws FileSystemException {
89 return false;
90 }
91
92 /**
93 * Returns the file's type.
94 */
95 @Override
96 protected FileType doGetType() {
97 return type;
98 }
99
100 /**
101 * Lists the children of the file.
102 */
103 @Override
104 protected String[] doListChildren() {
105 try {
106 if (!getType().hasChildren()) {
107 return null;
108 }
109 } catch (final FileSystemException e) {
110 // should not happen as the type has already been cached.
111 throw new RuntimeException(e);
112 }
113
114 return children.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
115 }
116
117 /**
118 * Returns the size of the file content (in bytes). Is only called if {@link #doGetType} returns
119 * {@link FileType#FILE}.
120 */
121 @Override
122 protected long doGetContentSize() {
123 return entry.getSize();
124 }
125
126 /**
127 * Returns the last modified time of this file.
128 */
129 @Override
130 protected long doGetLastModifiedTime() throws Exception {
131 return entry.getTime();
132 }
133
134 /**
135 * Creates an input stream to read the file content from. Is only called if {@link #doGetType} returns
136 * {@link FileType#FILE}. The input stream returned by this method is guaranteed to be closed before this method is
137 * called again.
138 */
139 @Override
140 protected InputStream doGetInputStream(final int bufferSize) throws Exception {
141 // VFS-210: zip allows to gather an input stream even from a directory and will
142 // return -1 on the first read. getType should not be expensive and keeps the tests
143 // running
144 if (!getType().hasContent()) {
145 throw new FileSystemException("vfs.provider/read-not-file.error", getName());
146 }
147
148 return getAbstractFileSystem().getZipFile().getInputStream(entry);
149 }
150
151 @Override
152 protected void doAttach() throws Exception {
153 getAbstractFileSystem().getZipFile();
154 }
155
156 @Override
157 protected void doDetach() throws Exception {
158 final ZipFileSystem afs = getAbstractFileSystem();
159 if (!afs.isOpen()) {
160 afs.close();
161 }
162 }
163 }