Add preliminary support of file I/O

This commit is contained in:
Alexey Andreev 2017-11-10 01:02:21 +03:00
parent 86d151d953
commit 76590d759c
11 changed files with 1050 additions and 0 deletions

View File

@ -0,0 +1,57 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.fs;
public abstract class AbstractInMemoryVirtualFile implements VirtualFile {
String name;
InMemoryVirtualDirectory parent;
long lastModified = System.currentTimeMillis();
AbstractInMemoryVirtualFile(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public void delete() {
parent.children.remove(name);
parent.modify();
parent = null;
}
@Override
public boolean canRead() {
return true;
}
@Override
public boolean canWrite() {
return true;
}
@Override
public long lastModified() {
return lastModified;
}
void modify() {
lastModified = System.currentTimeMillis();
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.fs;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedHashMap;
import java.util.Map;
public class InMemoryVirtualDirectory extends AbstractInMemoryVirtualFile {
final Map<String, VirtualFile> children = new LinkedHashMap<>();
InMemoryVirtualDirectory(String name) {
super(name);
}
@Override
public boolean isDirectory() {
return true;
}
@Override
public boolean isFile() {
return false;
}
@Override
public VirtualFile[] listFiles() {
return children.values().toArray(new VirtualFile[0]);
}
@Override
public VirtualFile getChildFile(String fileName) {
return children.get(fileName);
}
@Override
public InputStream read() {
throw new UnsupportedOperationException();
}
@Override
public OutputStream write(boolean append) {
throw new UnsupportedOperationException();
}
@Override
public VirtualFile createFile(String fileName) {
InMemoryVirtualFile file = new InMemoryVirtualFile(fileName);
adoptFile(file);
return file;
}
@Override
public VirtualFile createDirectory(String fileName) {
InMemoryVirtualDirectory file = new InMemoryVirtualDirectory(fileName);
adoptFile(file);
return file;
}
@Override
public int length() {
throw new UnsupportedOperationException();
}
private void adoptFile(AbstractInMemoryVirtualFile file) {
if (children.containsKey(file.name)) {
throw new IllegalArgumentException("File " + file.getName() + " already exists");
}
file.parent = this;
children.put(file.name, file);
modify();
}
}

View File

@ -0,0 +1,156 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.fs;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
public class InMemoryVirtualFile extends AbstractInMemoryVirtualFile {
byte[] data = new byte[0];
int size;
InMemoryVirtualFile(String name) {
super(name);
}
@Override
public boolean isDirectory() {
return false;
}
@Override
public boolean isFile() {
return true;
}
@Override
public VirtualFile[] listFiles() {
throw new UnsupportedOperationException();
}
@Override
public VirtualFile getChildFile(String fileName) {
throw new UnsupportedOperationException();
}
@Override
public InputStream read() {
if (parent == null) {
return null;
}
return new ByteArrayInputStream(data, 0, size);
}
@Override
public OutputStream write(boolean append) {
if (parent == null) {
return null;
}
if (!append) {
data = new byte[0];
size = 0;
}
return new OutputStreamImpl(data, size);
}
@Override
public VirtualFile createFile(String fileName) {
throw new UnsupportedOperationException();
}
@Override
public VirtualFile createDirectory(String fileName) {
throw new UnsupportedOperationException();
}
@Override
public int length() {
return size;
}
class OutputStreamImpl extends OutputStream {
byte[] data;
int pos;
OutputStreamImpl(byte[] data, int pos) {
this.data = data;
this.pos = pos;
}
private void ensureIO() throws IOException {
if (data == null) {
throw new IOException("Stream was closed");
}
}
@Override
public void write(int b) throws IOException {
ensureIO();
expandData(pos + 1);
data[pos++] = (byte) b;
sync();
}
private void expandData(int newSize) {
if (newSize > data.length) {
int newCapacity = Math.max(newSize, data.length) * 3 / 2;
boolean actual = data == InMemoryVirtualFile.this.data;
data = Arrays.copyOf(data, newCapacity);
if (actual) {
InMemoryVirtualFile.this.data = data;
}
}
}
private void sync() {
if (data == InMemoryVirtualFile.this.data) {
size = pos;
modify();
}
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
ensureIO();
if (len == 0) {
return;
}
if (off < 0 || len < 0 || off + len >= b.length) {
throw new IndexOutOfBoundsException();
}
expandData(pos + len);
while (len-- > 0) {
data[pos++] = b[off++];
}
sync();
}
@Override
public void close() throws IOException {
data = null;
}
@Override
public void flush() throws IOException {
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.fs;
public class InMemoryVirtualFileSystem implements VirtualFileSystem {
private InMemoryVirtualDirectory root = new InMemoryVirtualDirectory("");
private String userDir = "/";
@Override
public VirtualFile getRootFile() {
return root;
}
@Override
public String getUserDir() {
return userDir;
}
public void setUserDir(String userDir) {
this.userDir = userDir;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.fs;
import java.io.InputStream;
import java.io.OutputStream;
public interface VirtualFile {
String getName();
boolean isDirectory();
boolean isFile();
VirtualFile[] listFiles();
VirtualFile getChildFile(String fileName);
InputStream read();
OutputStream write(boolean append);
VirtualFile createFile(String fileName);
VirtualFile createDirectory(String fileName);
void delete();
boolean canRead();
boolean canWrite();
long lastModified();
int length();
}

View File

@ -0,0 +1,22 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.fs;
public interface VirtualFileSystem {
VirtualFile getRootFile();
String getUserDir();
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.fs;
public final class VirtualFileSystemProvider {
private static VirtualFileSystem instance = new InMemoryVirtualFileSystem();
private VirtualFileSystemProvider() {
}
public static VirtualFileSystem getInstance() {
return instance;
}
public static void setInstance(VirtualFileSystem instance) {
VirtualFileSystemProvider.instance = instance;
}
}

View File

@ -0,0 +1,445 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.io;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Objects;
import org.teavm.classlib.fs.VirtualFile;
import org.teavm.classlib.fs.VirtualFileSystem;
import org.teavm.classlib.fs.VirtualFileSystemProvider;
public class TFile implements Serializable, Comparable<TFile> {
private String path;
public static final char separatorChar = '/';
public static final String separator = "/";
public static final char pathSeparatorChar = ':';
public static final String pathSeparator = ":";
public TFile(TFile dir, String name) {
Objects.requireNonNull(name);
path = dir == null ? fixSlashes(name) : calculatePath(dir.getPath(), name);
}
public TFile(String path) {
Objects.requireNonNull(path);
this.path = fixSlashes(path);
}
public TFile(String dir, String name) {
Objects.requireNonNull(name);
path = dir == null ? fixSlashes(name) : calculatePath(dir, name);
}
public boolean canRead() {
VirtualFile virtualFile = findVirtualFile();
return virtualFile != null && virtualFile.canRead();
}
public boolean canWrite() {
VirtualFile virtualFile = findVirtualFile();
return virtualFile != null && virtualFile.canWrite();
}
@Override
public int compareTo(TFile o) {
return path.compareTo(o.path);
}
public String getPath() {
return path;
}
public String getName() {
int separatorIndex = path.lastIndexOf(separator);
return separatorIndex < 0 ? path : path.substring(separatorIndex + 1, path.length());
}
private static VirtualFileSystem fs() {
return VirtualFileSystemProvider.getInstance();
}
public String getAbsolutePath() {
if (isAbsolute()) {
return path;
}
String userdir = fs().getUserDir();
if (path.isEmpty()) {
return userdir;
}
int length = userdir.length();
StringBuilder result = new StringBuilder(userdir);
if (userdir.charAt(length - 1) != separatorChar) {
if (path.charAt(0) != separatorChar) {
result.append(separator);
}
} else if (path.charAt(0) == separatorChar) {
result.append(result.substring(0, length - 2));
}
result.append(path);
return result.toString();
}
public TFile getAbsoluteFile() {
return new TFile(getAbsolutePath());
}
public boolean isAbsolute() {
return !path.isEmpty() && path.charAt(0) == separatorChar;
}
public boolean isDirectory() {
VirtualFile virtualFile = findVirtualFile();
return virtualFile != null && virtualFile.isDirectory();
}
public boolean isHidden() {
return getName().startsWith(".");
}
public boolean isFile() {
VirtualFile virtualFile = findVirtualFile();
return virtualFile != null && virtualFile.isFile();
}
public String getCanonicalPath() throws IOException {
return getCanonicalPathImpl();
}
private String getCanonicalPathImpl() {
String result = getAbsolutePath();
int numSeparators = 1;
for (int i = 0; i < result.length(); i++) {
if (result.charAt(i) == separatorChar) {
numSeparators++;
}
}
int[] sepLocations = new int[numSeparators];
int rootLoc = 0;
char[] newResult = new char[result.length() + 1];
int newLength = 0;
int lastSlash = 0;
int foundDots = 0;
sepLocations[lastSlash] = rootLoc;
for (int i = 0; i <= result.length(); i++) {
if (i < rootLoc) {
newResult[newLength++] = result.charAt(i);
} else {
if (i == result.length() || result.charAt(i) == separatorChar) {
if (i == result.length() && foundDots == 0) {
break;
}
if (foundDots == 1) {
/* Don't write anything, just reset and continue */
foundDots = 0;
continue;
}
if (foundDots > 1) {
/* Go back N levels */
lastSlash = lastSlash > (foundDots - 1) ? lastSlash - (foundDots - 1) : 0;
newLength = sepLocations[lastSlash] + 1;
foundDots = 0;
continue;
}
sepLocations[++lastSlash] = newLength;
newResult[newLength++] = (byte) separatorChar;
continue;
}
if (result.charAt(i) == '.') {
foundDots++;
continue;
}
/* Found some dots within text, write them out */
if (foundDots > 0) {
for (int j = 0; j < foundDots; j++) {
newResult[newLength++] = (byte) '.';
}
}
newResult[newLength++] = result.charAt(i);
foundDots = 0;
}
}
// remove trailing slash
if (newLength > (rootLoc + 1) && newResult[newLength - 1] == separatorChar) {
newLength--;
}
return new String(newResult, 0, newLength);
}
public TFile getCanonicalFile() throws IOException {
return new TFile(getCanonicalPath());
}
public String getParent() {
int length = path.length();
int firstInPath = 0;
int index = path.lastIndexOf(separatorChar);
if (index == -1 || path.charAt(length - 1) == separatorChar) {
return null;
}
if (path.indexOf(separatorChar) == index && path.charAt(firstInPath) == separatorChar) {
return path.substring(0, index + 1);
}
return path.substring(0, index);
}
public TFile getParentFile() {
String path = getParent();
return path != null ? new TFile(getParent()) : null;
}
public static TFile[] listRoots() {
return new TFile[] { new TFile("/") };
}
public String[] list() {
VirtualFile virtualFile = findVirtualFile();
if (virtualFile == null || !virtualFile.isDirectory()) {
return null;
}
VirtualFile[] entries = virtualFile.listFiles();
String[] names = new String[entries.length];
for (int i = 0; i < entries.length; ++i) {
names[i] = entries[i].getName();
}
return names;
}
public TFile[] listFiles() {
VirtualFile virtualFile = findVirtualFile();
if (virtualFile == null || !virtualFile.isDirectory()) {
return null;
}
VirtualFile[] entries = virtualFile.listFiles();
TFile[] files = new TFile[entries.length];
for (int i = 0; i < entries.length; ++i) {
files[i] = new TFile(this, entries[i].getName());
}
return files;
}
public boolean exists() {
return findVirtualFile() != null;
}
public long lastModified() {
VirtualFile virtualFile = findVirtualFile();
return virtualFile != null ? virtualFile.lastModified() : 0;
}
public long length() {
VirtualFile virtualFile = findVirtualFile();
return virtualFile != null && virtualFile.isFile() ? virtualFile.length() : 0;
}
public boolean createNewFile() throws IOException {
VirtualFile parentVirtualFile = getParentFile().findVirtualFile();
if (parentVirtualFile == null) {
throw new IOException("Can't create file " + getPath() + " since parent directory does not exist");
}
if (!parentVirtualFile.isDirectory()) {
throw new IOException("Can't create file " + getPath() + " since parent path denotes regular file");
}
if (parentVirtualFile.getChildFile(getName()) != null) {
return false;
}
return parentVirtualFile.createFile(getName()) != null;
}
public boolean mkdir() {
VirtualFile virtualFile = getParentFile().findVirtualFile();
if (virtualFile == null || !virtualFile.isDirectory()) {
return false;
}
return virtualFile.createDirectory(getName()) != null;
}
public boolean mkdirs() {
String path = getCanonicalPathImpl();
if (path.startsWith("/")) {
path = path.substring(1);
}
VirtualFile virtualFile = fs().getRootFile();
int i = 0;
while (i < path.length()) {
int next = path.indexOf('/', i);
if (next < 0) {
next = path.length();
}
String name = path.substring(i, next);
VirtualFile child = virtualFile.getChildFile(name);
if (child == null) {
virtualFile = virtualFile.createDirectory(name);
} else if (child.isFile()) {
return false;
} else {
virtualFile = child;
}
i = next + 1;
}
return true;
}
public boolean delete() {
VirtualFile virtualFile = findVirtualFile();
if (virtualFile == null || (virtualFile.isDirectory() && virtualFile.listFiles().length == 0)) {
return false;
}
virtualFile.delete();
return true;
}
public URI toURI() {
String name = getAbsoluteName();
try {
if (!name.startsWith("/")) {
// start with sep.
return new URI("file", null, "/" + name, null, null);
} else if (name.startsWith("//")) {
return new URI("file", "", name, null);
}
return new URI("file", null, name, null, null);
} catch (URISyntaxException e) {
// this should never happen
return null;
}
}
private String getAbsoluteName() {
TFile f = getAbsoluteFile();
String name = f.getPath();
if (f.isDirectory() && name.charAt(name.length() - 1) != separatorChar) {
// Directories must end with a slash
name = new StringBuilder(name.length() + 1).append(name).append('/').toString();
}
if (separatorChar != '/') { // Must convert slashes.
name = name.replace(separatorChar, '/');
}
return name;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof TFile)) {
return false;
}
return path.equals(((File) obj).getPath());
}
@Override
public int hashCode() {
return path.hashCode();
}
private static String fixSlashes(String origPath) {
int uncIndex = 0;
int length = origPath.length();
int newLength = 0;
boolean foundSlash = false;
char[] newPath = origPath.toCharArray();
for (int i = 0; i < length; i++) {
char pathChar = newPath[i];
if (pathChar == '/') {
if (!foundSlash || i == uncIndex) {
newPath[newLength++] = separatorChar;
foundSlash = true;
}
} else {
newPath[newLength++] = pathChar;
foundSlash = false;
}
}
if (foundSlash && (newLength > uncIndex + 1 || newLength == 2 && newPath[0] != separatorChar)) {
newLength--;
}
return new String(newPath, 0, newLength);
}
private static String calculatePath(String dirPath, String name) {
dirPath = fixSlashes(dirPath);
if (!name.isEmpty() || dirPath.isEmpty()) {
name = fixSlashes(name);
int separatorIndex = 0;
while (separatorIndex < name.length() && name.charAt(separatorIndex) == separatorChar) {
separatorIndex++;
}
if (separatorIndex > 0) {
name = name.substring(separatorIndex, name.length());
}
if (!dirPath.isEmpty() && dirPath.charAt(dirPath.length() - 1) == separatorChar) {
return dirPath + name;
}
return dirPath + separatorChar + name;
}
return dirPath;
}
VirtualFile findVirtualFile() {
String path = getCanonicalPathImpl();
if (path.startsWith("/")) {
path = path.substring(1);
}
VirtualFile virtualFile = fs().getRootFile();
int i = 0;
while (i < path.length()) {
int next = path.indexOf('/', i);
if (next < 0) {
next = path.length();
}
virtualFile = virtualFile.getChildFile(path.substring(i, next));
if (virtualFile == null) {
return null;
}
i = next + 1;
}
return virtualFile;
}
VirtualFile findParentFile() {
return new TFile(getCanonicalPathImpl()).getParentFile().findVirtualFile();
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.io;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import org.teavm.classlib.fs.VirtualFile;
public class TFileInputStream extends InputStream {
private InputStream underlyingStream;
public TFileInputStream(TFile file) throws FileNotFoundException {
VirtualFile virtualFile = file.findVirtualFile();
if (virtualFile == null || virtualFile.isDirectory()) {
throw new FileNotFoundException();
}
underlyingStream = virtualFile.read();
if (underlyingStream == null) {
throw new FileNotFoundException();
}
}
@Override
public int read(byte[] b) throws IOException {
return underlyingStream.read(b);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return underlyingStream.read(b, off, len);
}
@Override
public long skip(long n) throws IOException {
return underlyingStream.skip(n);
}
@Override
public int available() throws IOException {
return underlyingStream.available();
}
@Override
public void close() throws IOException {
underlyingStream.close();
}
@Override
public int read() throws IOException {
return underlyingStream.read();
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.io;
import java.io.IOException;
public class TFileNotFoundException extends IOException {
public TFileNotFoundException() {
}
public TFileNotFoundException(String message) {
super(message);
}
}

View File

@ -0,0 +1,73 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.classlib.java.io;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import org.teavm.classlib.fs.VirtualFile;
public class TFileOutputStream extends OutputStream {
private OutputStream underlyingStream;
public TFileOutputStream(TFile file) throws FileNotFoundException {
this(file, false);
}
public TFileOutputStream(TFile file, boolean append) throws FileNotFoundException {
VirtualFile virtualFile = file.findVirtualFile();
if (virtualFile == null) {
VirtualFile parentVirtualFile = file.findParentFile();
if (parentVirtualFile != null && parentVirtualFile.isDirectory()) {
virtualFile = parentVirtualFile.createFile(file.getName());
}
}
if (virtualFile == null || virtualFile.isDirectory()) {
throw new FileNotFoundException();
}
underlyingStream = virtualFile.write(append);
if (underlyingStream == null) {
throw new FileNotFoundException();
}
}
@Override
public void write(byte[] b) throws IOException {
underlyingStream.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
underlyingStream.write(b, off, len);
}
@Override
public void flush() throws IOException {
underlyingStream.flush();
}
@Override
public void close() throws IOException {
underlyingStream.close();
}
@Override
public void write(int b) throws IOException {
underlyingStream.write(b);
}
}