/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016–2017, 2019–2023, 2025 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.microedition.system.protocol.file;
import java.io.*;
import java.util.*;
import javax.microedition.io.file.*;
import malik.emulator.io.*;
import malik.emulator.io.cloud.*;
import malik.emulator.io.vfs.*;
import malik.emulator.util.*;
public final class VFSConnection extends malik.emulator.microedition.io.CustomConnection implements FileConnection
{
private static final String[] FORBIDDEN_EXTENSIONS;
private static final String[] FORBIDDEN_DIRECTORIES;
static {
FORBIDDEN_EXTENSIONS = new String[] { "bindbg", "properties" };
FORBIDDEN_DIRECTORIES = new String[] { "/meta-inf", "/res", "/rms", "/ui" };
}
public static boolean isForbiddenPath(VirtualFileSystemReadOnly place, String path) {
if(place == null || place == CloudFileSystem.instance)
{
String[] rules;
if((path = path != null ? path.toLowerCase() : "").lastIndexOf('/') <= 0) for(int i = (rules = FORBIDDEN_EXTENSIONS).length; i-- > 0; ) if(path.endsWith(".".concat(rules[i]))) return true;
for(int i = (rules = FORBIDDEN_DIRECTORIES).length; i-- > 0; )
{
String rule;
if(path.equals(rule = rules[i]) || path.startsWith(rule.concat("/"))) return true;
}
}
return false;
}
private static boolean isObjectExists(VirtualFileSystemReadOnly place, String path) {
try
{
(place == null ? CloudFileSystem.instance : place).readAttributes(path, null);
}
catch(IOException e)
{
return false;
}
return true;
}
private static boolean isMatches(String name, String[] matches) {
int len;
if(matches == null || (len = matches.length) <= 0) return false;
if(len-- > 1)
{
for(int pos, i = pos = 0; i <= len; i++)
{
int fpos;
String s = matches[i];
if(i == len)
{
if(name.length() - s.length() < pos || !name.endsWith(s)) return false;
}
else if(i == 0)
{
if(!name.startsWith(s)) return false;
pos = s.length();
}
else
{
if((fpos = name.indexOf(s, pos)) < 0) return false;
pos = fpos + s.length();
}
}
return true;
}
return name.equals(matches[0]);
}
private static long directorySize(VirtualFileSystemReadOnly place, String path, boolean includeSubDirectories) throws IOException {
long result = 0L;
FileEnumeration enumr;
if((enumr = place.findFirst(path)) != null)
{
try
{
do
{
String name = enumr.getName();
if(isForbiddenPath(place, path.concat(name))) continue;
if(!enumr.isDirectory())
{
result += enumr.getSize();
}
else if(includeSubDirectories)
{
result += directorySize(place, (new StringBuilder()).append(path).append(name).append('/').toString(), true);
}
} while(enumr.findNext());
}
finally
{
enumr.close();
}
}
return result;
}
private static String[] getMatches(String filter) {
int i;
int j;
int len;
int index;
String[] result;
if(filter == null) return null;
for(len = 1, j = 0; (j = filter.indexOf('*', j)) >= 0; j++) len++;
for(result = new String[len], i = j = index = 0; (j = filter.indexOf('*', j)) >= 0; i = ++j) result[index++] = filter.substring(i, j);
result[index] = filter.substring(i);
return result;
}
private final int mode;
private String path;
private final String host;
private final VirtualFileSystemReadOnly placero;
private final VirtualFileSystemReadWrite placerw;
public VFSConnection(String url, VirtualFileSystemReadOnly place, String path, int mode) {
super(url);
int len1 = url.length();
int len2 = path.length();
if(len1 < len2) len1 = len2;
if(place == null) place = CloudFileSystem.instance;
this.mode = mode;
this.path = path;
this.host = url.substring(0, len1 - len2);
this.placero = place;
this.placerw = place instanceof VirtualFileSystemReadWrite ? (VirtualFileSystemReadWrite) place : null;
}
public void setURL(String url) {
throw new IllegalStateException("CustomConnection.setURL: невозможно изменить адрес соединения.");
}
public void mkdir() throws IOException {
int lim;
int pos;
String path;
VirtualFileSystemReadWrite place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.mkdir: соединение закрыто.");
}
if((mode & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.mkdir: требуется доступ для записи.");
}
if((lim = (path = this.path).length() - 1) > 0 && path.charAt(lim) == '/') path = path.substring(0, lim);
if(isObjectExists(place = placerw, path))
{
throw new IOException("FileConnection.mkdir: объект существует.");
}
if((pos = path.lastIndexOf('/')) > 1 && !isObjectExists(place, path.substring(0, pos)))
{
throw new IOException("FileConnection.mkdir: промежуточные папки не существуют.");
}
place.createDirectory(path);
}
public void create() throws IOException {
String path;
VirtualFileSystemReadWrite place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.create: соединение закрыто.");
}
if((mode & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.create: требуется доступ для записи.");
}
if((path = this.path).endsWith("/"))
{
throw new IOException("FileConnection.create: адрес объекта заканчивается на «/».");
}
if(isObjectExists(place = placerw, path))
{
throw new IOException("FileConnection.create: объект существует.");
}
place.createFile(path).close();
}
public void delete() throws IOException {
int lim;
File file;
String path;
FileAttributes attrs;
VirtualFileSystemReadWrite place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.delete: соединение закрыто.");
}
if((mode & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.delete: требуется доступ для записи.");
}
if((lim = (path = this.path).length() - 1) > 0 && path.charAt(lim) == '/') path = path.substring(0, lim);
if((file = File.getOpened(place = placerw, path)) != null) file.close();
place.readAttributes(path, attrs = new FileAttributes());
if(attrs.isDirectory())
{
place.deleteDirectory(path);
return;
}
place.deleteFile(path);
}
public void rename(String newName) throws IOException {
boolean hasDir;
int lim;
File file;
String oldPath;
String newPath;
VirtualFileSystemReadWrite place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.rename: соединение закрыто.");
}
if((mode & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.rename: требуется доступ для записи.");
}
if(newName == null)
{
throw new NullPointerException("FileConnection.rename: аргумент newName равен нулевой ссылке.");
}
if(newName.indexOf('/') >= 0)
{
throw new IllegalArgumentException("FileConnection.rename: аргумент newName содержит «/».");
}
if((lim = (oldPath = path).length() - 1) <= 0)
{
throw new IOException("FileConnection.rename: нельзя переименовать папку с приложением.");
}
if(hasDir = oldPath.charAt(lim) == '/') oldPath = oldPath.substring(0, lim);
if(isForbiddenPath(place = placerw, newPath = oldPath.substring(0, oldPath.lastIndexOf('/') + 1).concat(newName)))
{
throw new SecurityException("FileConnection.rename: доступ к заданному объекту запрещён для протокола file.");
}
if((file = File.getOpened(place, oldPath)) != null) file.close();
place.move(oldPath, newPath);
if(hasDir) newPath = newPath.concat("/");
super.setURL(host.concat(path = newPath));
}
public void truncate(long position) throws IOException {
boolean closed;
int lim;
File file;
String path;
VirtualFileSystemReadOnly place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.truncate: соединение закрыто.");
}
if((mode & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.truncate: требуется доступ для записи.");
}
if(position < 0L)
{
throw new IllegalArgumentException("FileConnection.truncate: аргумент position не может быть отрицательным.");
}
if((lim = (path = this.path).length() - 1) > 0 && path.charAt(lim) == '/') path = path.substring(0, lim);
if(closed = (file = File.getOpened(place = placero, path)) == null) file = File.open(place, path, WRITE);
try
{
file.truncate(position);
}
finally
{
if(closed) file.close();
}
}
public void setHidden(boolean hidden) throws IOException {
int a;
int lim;
String path;
FileAttributes attrs;
VirtualFileSystemReadWrite place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.setHidden: соединение закрыто.");
}
if((mode & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.setHidden: требуется доступ для записи.");
}
if((lim = (path = this.path).length() - 1) > 0 && path.charAt(lim) == '/') path = path.substring(0, lim);
(place = placerw).readAttributes(path, attrs = new FileAttributes());
a = attrs.getAttributes();
if(hidden)
{
a |= FileAttributes.HIDDEN;
} else
{
a &= ~FileAttributes.HIDDEN;
}
attrs.setAttributes(a, attrs.getCreationTime(), attrs.getLastWriteTime(), attrs.getLastAccessTime());
place.writeAttributes(path, attrs);
}
public void setReadable(boolean readable) throws IOException {
/* Игнорируется, canRead всегда возвращает true. */
}
public void setWritable(boolean writable) throws IOException {
int a;
int lim;
String path;
FileAttributes attrs;
VirtualFileSystemReadWrite place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.setWritable: соединение закрыто.");
}
if((mode & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.setWritable: требуется доступ для записи.");
}
if((lim = (path = this.path).length() - 1) > 0 && path.charAt(lim) == '/') path = path.substring(0, lim);
(place = placerw).readAttributes(path, attrs = new FileAttributes());
a = attrs.getAttributes();
if(writable)
{
a &= ~FileAttributes.READ_ONLY;
} else
{
a |= FileAttributes.READ_ONLY;
}
attrs.setAttributes(a, attrs.getCreationTime(), attrs.getLastWriteTime(), attrs.getLastAccessTime());
place.writeAttributes(path, attrs);
}
public void setFileConnection(String objectName) throws IOException {
int len;
String path;
VirtualFileSystemReadOnly place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.setFileConnection: соединение закрыто.");
}
if(objectName == null)
{
throw new NullPointerException("FileConnection.setFileConnection: аргумент objectName равен нулевой ссылке.");
}
if(objectName.indexOf('/') >= 0)
{
throw new IllegalArgumentException("FileConnection.setFileConnection: аргумент objectName содержит «/».");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
place = placero;
if((len = path.length()) > 1)
{
FileAttributes attrs;
place.readAttributes(path, attrs = new FileAttributes());
if(!attrs.isDirectory())
{
throw new IOException("FileConnection.setFileConnection: соединение связано не с папкой.");
}
}
if(".".equals(objectName)) return;
if("..".equals(objectName))
{
if(len <= 0)
{
throw new IllegalArgumentException("FileConnection.setFileConnection: целевой путь не существует.");
}
if((path = path.substring(0, path.lastIndexOf('/'))).length() <= 0) path = "/";
} else
{
path = (new StringBuilder()).append(path).append('/').append(objectName).toString();
}
if(isForbiddenPath(place, path))
{
throw new SecurityException("FileConnection.setFileConnection: доступ к заданному объекту запрещён для протокола file.");
}
if(path.length() > 1)
{
try
{
place.readAttributes(path, null);
}
catch(IOException e)
{
throw new IllegalArgumentException("FileConnection.setFileConnection: целевой путь не существует.");
}
}
super.setURL(host.concat(this.path = path));
}
public boolean exists() {
String path;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.exists: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.exists: требуется доступ для чтения.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() > 1)
{
try
{
placero.readAttributes(path, null);
}
catch(IOException e)
{
return false;
}
}
return true;
}
public boolean canRead() {
return true;
}
public boolean canWrite() {
String path;
FileAttributes attrs;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.canWrite: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.canWrite: требуется доступ для чтения.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() <= 0) return true;
try
{
placero.readAttributes(path, attrs = new FileAttributes());
}
catch(IOException e)
{
return false;
}
return !attrs.isReadOnly();
}
public boolean isOpen() {
return !isConnectionClosed();
}
public boolean isHidden() {
String path;
FileAttributes attrs;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.isHidden: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.isHidden: требуется доступ для чтения.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() <= 0) return false;
try
{
placero.readAttributes(path, attrs = new FileAttributes());
}
catch(IOException e)
{
return false;
}
return attrs.isHidden();
}
public boolean isDirectory() {
String path;
FileAttributes attrs;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.isDirectory: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.isDirectory: требуется доступ для чтения.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() <= 0) return true;
try
{
placero.readAttributes(path, attrs = new FileAttributes());
}
catch(IOException e)
{
return false;
}
return attrs.isDirectory();
}
public long usedSize() {
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.usedSize: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.usedSize: требуется доступ для чтения.");
}
throw new SecurityException("FileConnection.usedSize: доступ к корневой папке запрещён для протокола file.");
}
public long totalSize() {
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.totalSize: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.totalSize: требуется доступ для чтения.");
}
throw new SecurityException("FileConnection.totalSize: доступ к корневой папке запрещён для протокола file.");
}
public long availableSize() {
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.availableSize: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.availableSize: требуется доступ для чтения.");
}
throw new SecurityException("FileConnection.availableSize: доступ к корневой папке запрещён для протокола file.");
}
public long lastModified() {
String path;
FileAttributes attrs;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.lastModified: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.lastModified: требуется доступ для чтения.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
try
{
placero.readAttributes(path, attrs = new FileAttributes());
}
catch(IOException e)
{
return 0L;
}
return attrs.getLastWriteTime();
}
public long fileSize() throws IOException {
long result;
String path;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.fileSize: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.fileSize: требуется доступ для чтения.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() <= 0)
{
throw new IOException("FileConnection.fileSize: соединение связано с папкой.");
}
try
{
FileEnumeration enumr;
if((enumr = placero.findFirst(path)) != null)
{
try
{
result = enumr.isDirectory() ? -2L : enumr.getSize();
}
finally
{
enumr.close();
}
} else
{
result = -1L;
}
}
catch(IOException e)
{
result = -1L;
}
if(result == -2L)
{
throw new IOException("FileConnection.fileSize: соединение связано с папкой.");
}
return result;
}
public long directorySize(boolean includeSubDirectories) throws IOException {
long result;
String path;
String pathd;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.directorySize: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.directorySize: требуется доступ для чтения.");
}
if((pathd = this.path).endsWith("/"))
{
path = pathd.substring(0, pathd.length() - 1);
} else
{
pathd = (path = pathd).concat("/");
}
try
{
if(path.length() > 0)
{
FileAttributes attrs;
VirtualFileSystemReadOnly place;
(place = placero).readAttributes(path, attrs = new FileAttributes());
result = attrs.isDirectory() ? directorySize(place, pathd, includeSubDirectories) : -2L;
} else
{
result = directorySize(placero, "/", includeSubDirectories);
}
}
catch(IOException e)
{
result = -1L;
}
if(result == -2L)
{
throw new IOException("FileConnection.directorySize: соединение связано не с папкой.");
}
return result;
}
public InputStream openInputStream() throws IOException {
int mode;
File file;
String path;
FileAttributes attrs;
VirtualFileSystemReadOnly place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.openInputStream: соединение закрыто.");
}
if(((mode = this.mode) & READ) == 0)
{
throw new IllegalModeException("FileConnection.openInputStream: требуется доступ для чтения.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() <= 0)
{
throw new IOException("FileConnection.openInputStream: соединение связано с папкой.");
}
(place = placero).readAttributes(path, attrs = new FileAttributes());
if(attrs.isDirectory())
{
throw new IOException("FileConnection.openInputStream: соединение связано с папкой.");
}
return ((file = File.getOpened(place, path)) != null ? file : File.open(place, path, mode)).openInputStream();
}
public DataInputStream openDataInputStream() throws IOException {
InputStream result = openInputStream();
return new DataInputStream(result);
}
public OutputStream openOutputStream() throws IOException {
int mode;
File file;
String path;
FileAttributes attrs;
VirtualFileSystemReadOnly place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.openOutputStream: соединение закрыто.");
}
if(((mode = this.mode) & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.openOutputStream: требуется доступ для записи.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() <= 0)
{
throw new IOException("FileConnection.openOutputStream: соединение связано с папкой.");
}
(place = placero).readAttributes(path, attrs = new FileAttributes());
if(attrs.isDirectory())
{
throw new IOException("FileConnection.openOutputStream: соединение связано с папкой.");
}
return ((file = File.getOpened(place, path)) != null ? file : File.open(place, path, mode)).openOutputStream();
}
public OutputStream openOutputStream(long position) throws IOException {
int mode;
File file;
String path;
FileAttributes attrs;
VirtualFileSystemReadOnly place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.openOutputStream: соединение закрыто.");
}
if(((mode = this.mode) & WRITE) == 0)
{
throw new IllegalModeException("FileConnection.openOutputStream: требуется доступ для записи.");
}
if((path = this.path).endsWith("/")) path = path.substring(0, path.length() - 1);
if(path.length() <= 0)
{
throw new IOException("FileConnection.openOutputStream: соединение связано с папкой.");
}
(place = placero).readAttributes(path, attrs = new FileAttributes());
if(attrs.isDirectory())
{
throw new IOException("FileConnection.openOutputStream: соединение связано с папкой.");
}
return ((file = File.getOpened(place, path)) != null ? file : File.open(place, path, mode)).openOutputStream(position);
}
public DataOutputStream openDataOutputStream() throws IOException {
OutputStream result = openOutputStream();
return new DataOutputStream(result);
}
public Enumeration list(String filter, boolean includeHidden) throws IOException {
int len;
String[] filter1matches;
String[] filter2matches;
String path;
String pathd;
Vector result;
String filter2;
FileAttributes attrs;
FileEnumeration enumr;
VirtualFileSystemReadOnly place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.list: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.list: требуется доступ для чтения.");
}
if(filter == null)
{
throw new NullPointerException("FileConnection.list: аргумент filter равен нулевой ссылке.");
}
if(filter.indexOf('/') >= 0)
{
throw new IllegalArgumentException("FileConnection.list: аргумент filter содержит «/».");
}
if((len = filter.length()) <= 0) len = (filter = "*").length();
filter2 = filter.endsWith(".*") ? filter.substring(0, len - ".*".length()) : null;
if((pathd = this.path).endsWith("/"))
{
path = pathd.substring(0, pathd.length() - 1);
} else
{
pathd = (path = pathd).concat("/");
}
place = placero;
if(path.length() > 1)
{
place.readAttributes(path, attrs = new FileAttributes());
if(!attrs.isDirectory())
{
throw new IOException("FileConnection.list: соединение связано не с папкой.");
}
}
filter1matches = getMatches(filter);
filter2matches = getMatches(filter2);
result = new Vector();
if((enumr = place.findFirst(pathd)) != null)
{
try
{
do
{
if(includeHidden || !enumr.isHidden())
{
boolean dir = enumr.isDirectory();
String name = enumr.getName();
if(dir || isMatches(name, filter1matches) || isMatches(name, filter2matches)) result.addElement(dir ? name.concat("/") : name);
}
} while(enumr.findNext());
}
finally
{
enumr.close();
}
}
return result.elements();
}
public Enumeration list() throws IOException {
String path;
String pathd;
Vector result;
FileAttributes attrs;
FileEnumeration enumr;
VirtualFileSystemReadOnly place;
if(isConnectionClosed())
{
throw new ConnectionClosedException("FileConnection.list: соединение закрыто.");
}
if((mode & READ) == 0)
{
throw new IllegalModeException("FileConnection.list: требуется доступ для чтения.");
}
if((pathd = this.path).endsWith("/"))
{
path = pathd.substring(0, pathd.length() - 1);
} else
{
pathd = (path = pathd).concat("/");
}
place = placero;
if(path.length() > 1)
{
place.readAttributes(path, attrs = new FileAttributes());
if(!attrs.isDirectory())
{
throw new IOException("FileConnection.list: соединение связано не с папкой.");
}
}
result = new Vector();
if((enumr = place.findFirst(pathd)) != null)
{
try
{
do
{
if(!enumr.isHidden())
{
String name = enumr.getName();
result.addElement(enumr.isDirectory() ? name.concat("/") : name);
}
} while(enumr.findNext());
}
finally
{
enumr.close();
}
}
return result.elements();
}
public String getName() {
String path;
return (path = this.path).substring(path.lastIndexOf('/', path.length() - (path.endsWith("/") ? 2 : 1)) + 1);
}
public String getPath() {
String path;
return (path = this.path).substring(0, path.lastIndexOf('/', path.length() - (path.endsWith("/") ? 2 : 1)) + 1);
}
}
final class File extends Object
{
private static final class PlacePathPair extends Object
{
public final Object place;
public final String path;
public PlacePathPair(Object place, String path) {
this.place = place;
this.path = path;
}
public boolean equals(Object anot) {
PlacePathPair ppp;
return anot instanceof PlacePathPair && place == (ppp = (PlacePathPair) anot).place && path.equals(ppp.path);
}
public int hashCode() {
return System.identityHashCode(place) ^ path.hashCode();
}
}
public static final int READ = 1;
public static final int WRITE = 2;
public static final int READ_WRITE = 3;
private static final Hashtable OPENED;
static {
OPENED = new Hashtable();
}
public static File getOpened(VirtualFileSystemReadOnly place, String path) {
return (File) OPENED.get(new PlacePathPair(place, path));
}
public static File open(VirtualFileSystemReadOnly place, String path, int mode) throws IOException {
File result;
PlacePathPair pair;
IOStream openedIOStream;
InputStream sourceInputStream;
OutputStream destinationOutputStream;
if(place == null) place = CloudFileSystem.instance;
if(path == null)
{
throw new NullPointerException("File.open: аргумент path равен нулевой ссылке.");
}
if(mode < READ || mode > READ_WRITE)
{
throw new IllegalArgumentException("File.open: аргумент mode имеет недопустимое значение.");
}
if((mode & WRITE) != 0 && !(place instanceof VirtualFileSystemReadWrite))
{
throw new IllegalArgumentException("File.open: аргумент place имеет недопустимое значение.");
}
if(OPENED.get(pair = new PlacePathPair(place, path)) != null)
{
throw new FileNotFoundException((new StringBuilder()).append("File.open: файл ").append(path).append(" не найден или занят.").toString(), path);
}
if(mode == READ)
{
openedIOStream = null;
sourceInputStream = place.openFileForRead(path);
destinationOutputStream = null;
} else
{
openedIOStream = ((VirtualFileSystemReadWrite) place).openFileForReadWrite(path);
sourceInputStream = mode == READ_WRITE ? openedIOStream.getInputStream() : null;
destinationOutputStream = openedIOStream.getOutputStream();
}
(result = new File(place, path, mode)).openedIOStream = openedIOStream;
if(sourceInputStream != null) result.openedInputStream = result.new LocalInputStream(sourceInputStream);
if(destinationOutputStream != null) result.openedOutputStream = result.new LocalOutputStream(destinationOutputStream);
OPENED.put(pair, result);
return result;
}
private final class LocalInputStream extends InputStream
{
final InputStream stream;
public LocalInputStream(InputStream stream) {
this.stream = stream;
}
public void close() throws IOException {
File owner;
synchronized((owner = File.this).monitor)
{
if(owner.hasInputStream)
{
owner.hasInputStream = false;
if(!owner.hasOutputStream) owner.fastClose();
}
}
}
public void reset() throws IOException {
if(!(File.this).hasInputStream)
{
throw new IOException("InputStream.reset: поток ввода закрыт.");
}
stream.reset();
}
public void mark(int readLimit) {
stream.mark(readLimit);
}
public boolean markSupported() {
return stream.markSupported();
}
public int available() throws IOException {
if(!(File.this).hasInputStream)
{
throw new IOException("InputStream.available: поток ввода закрыт.");
}
return stream.available();
}
public int read() throws IOException {
if(!(File.this).hasInputStream)
{
throw new IOException("InputStream.read: поток ввода закрыт.");
}
return stream.read();
}
public int read(byte[] dst) throws IOException {
if(!(File.this).hasInputStream)
{
throw new IOException("InputStream.read: поток ввода закрыт.");
}
return stream.read(dst);
}
public int read(byte[] dst, int offset, int length) throws IOException {
if(!(File.this).hasInputStream)
{
throw new IOException("InputStream.read: поток ввода закрыт.");
}
return stream.read(dst, offset, length);
}
public long skip(long quantity) throws IOException {
if(!(File.this).hasInputStream)
{
throw new IOException("InputStream.skip: поток ввода закрыт.");
}
return stream.skip(quantity);
}
public String toString() {
return stream.toString();
}
}
private final class LocalOutputStream extends OutputStream
{
final OutputStream stream;
public LocalOutputStream(OutputStream stream) {
this.stream = stream;
}
public void close() throws IOException {
File owner;
synchronized((owner = File.this).monitor)
{
if(owner.hasOutputStream)
{
owner.hasOutputStream = false;
if(!owner.hasInputStream) owner.fastClose();
}
}
}
public void flush() throws IOException {
if(!(File.this).hasOutputStream)
{
throw new IOException("OutputStream.flush: поток вывода закрыт.");
}
stream.flush();
}
public void write(int byteData) throws IOException {
if(!(File.this).hasOutputStream)
{
throw new IOException("OutputStream.write: поток вывода закрыт.");
}
stream.write(byteData);
}
public void write(byte[] src) throws IOException {
if(!(File.this).hasOutputStream)
{
throw new IOException("OutputStream.write: поток вывода закрыт.");
}
stream.write(src);
}
public void write(byte[] src, int offset, int length) throws IOException {
if(!(File.this).hasOutputStream)
{
throw new IOException("OutputStream.write: поток вывода закрыт.");
}
stream.write(src, offset, length);
}
public String toString() {
return stream.toString();
}
}
boolean closed;
boolean hasInputStream;
boolean hasOutputStream;
private final int mode;
private final String path;
private IOStream openedIOStream;
private LocalInputStream openedInputStream;
private LocalOutputStream openedOutputStream;
private final VirtualFileSystemReadOnly place;
final Object monitor;
private File(VirtualFileSystemReadOnly place, String path, int mode) {
this.mode = mode;
this.path = path;
this.place = place;
this.monitor = new Object();
}
public void close() throws IOException {
synchronized(monitor)
{
if(!closed) fastClose();
}
}
public void truncate(long position) throws IOException {
int error = 0;
long pos;
IOStream stream = null;
synchronized(monitor)
{
label0:
{
if(closed)
{
error = 1;
break label0;
}
if((mode & WRITE) == 0)
{
error = 2;
break label0;
}
stream = openedIOStream;
}
}
switch(error)
{
case 1:
throw new ClosedFileException("FileConnection.truncate: файл закрыт.");
case 2:
throw new IllegalModeException("FileConnection.truncate: требуется доступ для записи.");
}
pos = stream.position();
stream.resetOutputStream();
stream.position(position);
stream.truncate();
stream.resetOutputStream();
stream.position(pos);
}
public int getMode() {
return mode;
}
public InputStream openInputStream() throws IOException {
int error = 0;
InputStream result;
synchronized(monitor)
{
label0:
{
IOStream stream;
if(closed)
{
error = 1;
result = null;
break label0;
}
if((mode & READ) == 0)
{
error = 2;
result = null;
break label0;
}
if(hasInputStream)
{
error = 3;
result = null;
break label0;
}
if((stream = openedIOStream) != null) stream.resetInputStream();
hasInputStream = true;
result = openedInputStream;
}
}
switch(error)
{
case 1:
throw new ClosedFileException("FileConnection.openInputStream: файл закрыт.");
case 2:
throw new IllegalModeException("FileConnection.openInputStream: требуется доступ для чтения.");
case 3:
throw new IOException("FileConnection.openInputStream: поток ввода уже был открыт раннее.");
}
return result;
}
public OutputStream openOutputStream() throws IOException {
int error = 0;
OutputStream result;
synchronized(monitor)
{
label0:
{
if(closed)
{
error = 1;
result = null;
break label0;
}
if((mode & WRITE) == 0)
{
error = 2;
result = null;
break label0;
}
if(hasOutputStream)
{
error = 3;
result = null;
break label0;
}
openedIOStream.resetOutputStream();
hasOutputStream = true;
result = openedOutputStream;
}
}
switch(error)
{
case 1:
throw new ClosedFileException("FileConnection.openOutputStream: файл закрыт.");
case 2:
throw new IllegalModeException("FileConnection.openOutputStream: требуется доступ для записи.");
case 3:
throw new IOException("FileConnection.openOutputStream: поток вывода уже был открыт раннее.");
}
return result;
}
public InputStream openInputStream(long position) throws IOException {
int error = 0;
InputStream result;
synchronized(monitor)
{
label0:
{
IOStream stream;
if(closed)
{
error = 1;
result = null;
break label0;
}
if((mode & READ) == 0)
{
error = 2;
result = null;
break label0;
}
if(hasInputStream)
{
error = 3;
result = null;
break label0;
}
if((stream = openedIOStream) != null) stream.resetInputStream();
hasInputStream = true;
(result = openedInputStream).skip(position);
}
}
switch(error)
{
case 1:
throw new ClosedFileException("FileConnection.openInputStream: файл закрыт.");
case 2:
throw new IllegalModeException("FileConnection.openInputStream: требуется доступ для чтения.");
case 3:
throw new IOException("FileConnection.openInputStream: поток ввода уже был открыт раннее.");
}
return result;
}
public OutputStream openOutputStream(long position) throws IOException {
int error = 0;
OutputStream result;
synchronized(monitor)
{
label0:
{
IOStream stream;
if(closed)
{
error = 1;
result = null;
break label0;
}
if((mode & WRITE) == 0)
{
error = 2;
result = null;
break label0;
}
if(hasOutputStream)
{
error = 3;
result = null;
break label0;
}
(stream = openedIOStream).resetOutputStream();
stream.position(position);
hasOutputStream = true;
result = openedOutputStream;
}
}
switch(error)
{
case 1:
throw new ClosedFileException("FileConnection.openOutputStream: файл закрыт.");
case 2:
throw new IllegalModeException("FileConnection.openOutputStream: требуется доступ для записи.");
case 3:
throw new IOException("FileConnection.openOutputStream: поток вывода уже был открыт раннее.");
}
return result;
}
public String getPath() {
return path;
}
public InputStream getInputStream() {
return hasInputStream ? openedInputStream : null;
}
public OutputStream getOutputStream() {
return hasOutputStream ? openedOutputStream : null;
}
public VirtualFileSystemReadOnly getPlace() {
return place;
}
void fastClose() throws IOException {
IOStream io = openedIOStream;
LocalInputStream in = openedInputStream;
OPENED.remove(new PlacePathPair(place, path));
closed = true;
hasInputStream = false;
hasOutputStream = false;
openedIOStream = null;
openedInputStream = null;
openedOutputStream = null;
if(io != null)
{
io.close();
} else
{
in.stream.close();
}
}
}