| /* |
| * Copyright (C) 2015 The Android Open Source Project |
| * |
| * 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 com.android.sched.vfs; |
| |
| import com.google.common.base.Splitter; |
| |
| import com.android.sched.util.config.HasKeyId; |
| import com.android.sched.util.config.MessageDigestFactory; |
| import com.android.sched.util.config.ThreadConfig; |
| import com.android.sched.util.config.expression.LongExpression; |
| import com.android.sched.util.config.id.BooleanPropertyId; |
| import com.android.sched.util.config.id.IntegerPropertyId; |
| import com.android.sched.util.config.id.MessageDigestPropertyId; |
| import com.android.sched.util.file.CannotCloseException; |
| import com.android.sched.util.file.CannotCreateFileException; |
| import com.android.sched.util.file.CannotDeleteFileException; |
| import com.android.sched.util.file.CannotGetModificationTimeException; |
| import com.android.sched.util.file.NoSuchFileException; |
| import com.android.sched.util.file.NotDirectoryException; |
| import com.android.sched.util.file.NotFileException; |
| import com.android.sched.util.file.Statusful; |
| import com.android.sched.util.file.StreamFileStatus; |
| import com.android.sched.util.file.WrongPermissionException; |
| import com.android.sched.util.location.ColumnAndLineLocation; |
| import com.android.sched.util.location.Location; |
| import com.android.sched.vfs.CaseInsensitiveFS.CaseInsensitiveVDir; |
| import com.android.sched.vfs.CaseInsensitiveFS.CaseInsensitiveVFile; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.LineNumberReader; |
| import java.io.OutputStream; |
| import java.io.PrintStream; |
| import java.nio.file.attribute.FileTime; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EnumSet; |
| import java.util.Iterator; |
| import java.util.Set; |
| |
| import javax.annotation.CheckForNull; |
| import javax.annotation.Nonnegative; |
| import javax.annotation.Nonnull; |
| |
| /** |
| * A filter implementation of a {@link VFS} which take a {@link VFS}, case insensitive or not, and |
| * store in it files and directories with their name encoded through a digest algorithm. With that |
| * filter, even if the real {@link VFS} is case insensitive, the filtered {@link VFS} acts as a case |
| * sensitive {@link VFS}. |
| */ |
| @HasKeyId |
| public class CaseInsensitiveFS extends BaseVFS<CaseInsensitiveVDir, CaseInsensitiveVFile> implements |
| VFS, Statusful { |
| static final String INDEX_NAME = "index"; |
| static final String DEBUG_NAME = "index.dbg"; |
| |
| public static final IntegerPropertyId NB_GROUP = IntegerPropertyId |
| .create("sched.vfs.case-insensitive.group.count", |
| "Number of directory used to encode a path name").withMin(0).addDefaultValue(1); |
| |
| public static final IntegerPropertyId SZ_GROUP = IntegerPropertyId |
| .create("sched.vfs.case-insensitive.group.size", |
| "Number of letters in directory name used to encode a path name") |
| .requiredIf(NB_GROUP.getValue().isGreater(LongExpression.getConstant(0))).withMin(0) |
| .addDefaultValue(2); |
| |
| @Nonnull |
| public static final MessageDigestPropertyId ALGO = MessageDigestPropertyId.create( |
| "sched.vfs.case-insensitive.algo", "Algorithm used to encode a path name").addDefaultValue( |
| "SHA"); |
| |
| @Nonnull |
| public static final BooleanPropertyId DEBUG = BooleanPropertyId.create( |
| "sched.vfs.case-insensitive.debug", |
| "generate an index file '" + DEBUG_NAME + "' for debugging purpose").addDefaultValue(false); |
| |
| @Nonnull |
| private static final char INDEX_SEPARATOR = '/'; |
| |
| @Nonnull |
| private static final Splitter splitter = Splitter.on(INDEX_SEPARATOR); |
| |
| @Nonnegative |
| private final int numGroups; |
| @Nonnegative |
| private final int groupSize; |
| @Nonnull |
| private final MessageDigestFactory mdf; |
| private final boolean debug; |
| |
| @Nonnull |
| private final CaseInsensitiveVDir root = new CaseInsensitiveVDir(this, null, ""); |
| |
| @Nonnull |
| private final Set<Capabilities> capabilities; |
| |
| @Override |
| @Nonnull |
| public String getDescription() { |
| return "case insensitive wrapper"; |
| } |
| |
| static class CaseInsensitiveVDir extends InMemoryVDir { |
| @CheckForNull |
| protected final VDir parent; |
| |
| CaseInsensitiveVDir( |
| @Nonnull BaseVFS<? extends InMemoryVDir, ? extends CaseInsensitiveVFile> vfs, |
| @CheckForNull VDir parent, @Nonnull String name) { |
| super(vfs, name); |
| this.parent = parent; |
| } |
| |
| @Override |
| @Nonnull |
| public VPath getPath() { |
| if (parent != null) { |
| return parent.getPath().clone().appendPath(new VPath(name, '/')); |
| } else { |
| return VPath.ROOT; |
| } |
| } |
| |
| @Override |
| @Nonnull |
| public BaseVFile getVFile(@Nonnull String name) throws NoSuchFileException, |
| NotFileException { |
| return vfs.getVFile(this, name); |
| } |
| |
| @Override |
| @Nonnull |
| public BaseVDir getVDir(@Nonnull String name) throws NotDirectoryException, |
| NoSuchFileException { |
| return vfs.getVDir(this, name); |
| } |
| |
| @Override |
| @Nonnull |
| public BaseVFile createVFile(@Nonnull String name) throws CannotCreateFileException { |
| return vfs.createVFile(this, name); |
| } |
| |
| @Override |
| @Nonnull |
| public BaseVDir createVDir(@Nonnull String name) throws CannotCreateFileException { |
| return vfs.createVDir(this, name); |
| } |
| |
| @Override |
| @Nonnull |
| public Collection<? extends BaseVElement> list() { |
| return vfs.list(this); |
| } |
| |
| @CheckForNull |
| public VDir getParent() { |
| return parent; |
| } |
| } |
| |
| static class CaseInsensitiveVFile extends ParentVFile { |
| @CheckForNull |
| private BaseVFile encodedFile; |
| |
| CaseInsensitiveVFile( |
| @Nonnull BaseVFS<? extends InMemoryVDir, ? extends CaseInsensitiveVFile> vfs, |
| @Nonnull VDir parent, @Nonnull String name) { |
| super(vfs, parent, name); |
| } |
| |
| private void setEncodedFile(@Nonnull BaseVFile encodedFile) { |
| this.encodedFile = encodedFile; |
| } |
| |
| @CheckForNull |
| private BaseVFile getEncodedFile() { |
| return encodedFile; |
| } |
| |
| @Override |
| public void delete() throws CannotDeleteFileException { |
| vfs.delete(this); |
| } |
| |
| private void deleteFromCache() { |
| ((InMemoryVDir) parent).internalDelete(name); |
| } |
| } |
| |
| @Nonnull |
| private final BaseVFS<BaseVDir, BaseVFile> vfs; |
| |
| private boolean used = false; |
| |
| public CaseInsensitiveFS(@Nonnull VFS vfs) throws BadVFSFormatException { |
| this(vfs, ThreadConfig.get(NB_GROUP).intValue(), ThreadConfig.get(SZ_GROUP).intValue(), |
| ThreadConfig.get(ALGO), ThreadConfig.get(DEBUG).booleanValue()); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public CaseInsensitiveFS(@Nonnull VFS vfs, int numGroups, int groupSize, |
| @Nonnull MessageDigestFactory mdf, boolean debug) |
| throws BadVFSFormatException { |
| this.vfs = (BaseVFS<BaseVDir, BaseVFile>) vfs; |
| |
| Set<Capabilities> capabilities = EnumSet.copyOf(vfs.getCapabilities()); |
| capabilities.add(Capabilities.CASE_SENSITIVE); |
| capabilities.add(Capabilities.UNIQUE_ELEMENT); |
| this.capabilities = Collections.unmodifiableSet(capabilities); |
| |
| this.numGroups = numGroups; |
| this.groupSize = groupSize; |
| this.mdf = mdf; |
| this.debug = debug; |
| |
| initVFS(); |
| } |
| |
| private void initVFS() throws BadVFSFormatException { |
| LineNumberReader reader = null; |
| VFile file = null; |
| try { |
| try { |
| file = vfs.getRootDir().getVFile(INDEX_NAME); |
| } catch (NoSuchFileException e) { |
| if (!vfs.getRootDir().isEmpty()) { |
| // If VFS is not empty, index file is missing |
| throw new BadVFSFormatException(this, vfs.getLocation(), e); |
| } |
| |
| return; |
| } catch (NotFileException e) { |
| throw new BadVFSFormatException(this, vfs.getLocation(), e); |
| } |
| |
| try { |
| reader = new LineNumberReader(new InputStreamReader(file.getInputStream())); |
| } catch (WrongPermissionException e) { |
| throw new BadVFSFormatException(this, vfs.getLocation(), e); |
| } |
| |
| String line; |
| try { |
| while ((line = reader.readLine()) != null) { |
| if (line.charAt(1) != ':') { |
| throw new BadVFSFormatException(this, vfs.getLocation(), |
| new WrongFileFormatException(new ColumnAndLineLocation(file.getLocation(), |
| reader.getLineNumber()))); |
| } |
| |
| char type = line.charAt(0); |
| switch (type) { |
| case 'd': |
| loadVDir(line.substring(2)); |
| break; |
| case 'f': |
| loadVFile(line.substring(2)); |
| break; |
| default: |
| throw new BadVFSFormatException(this, vfs.getLocation(), |
| new WrongFileFormatException(new ColumnAndLineLocation(file.getLocation(), |
| reader.getLineNumber()))); |
| } |
| } |
| } catch (NotDirectoryException | NotFileException e) { |
| throw new BadVFSFormatException(this, vfs.getLocation(), e); |
| } catch (IOException e) { |
| throw new BadVFSFormatException(this, vfs.getLocation(), e); |
| } |
| } finally { |
| if (reader != null) { |
| assert file != null; |
| |
| try { |
| reader.close(); |
| } catch (IOException e) { |
| // Ignore |
| } |
| } |
| } |
| } |
| |
| private void loadVDir(@Nonnull String path) throws NotDirectoryException { |
| CaseInsensitiveVDir currentDir = getRootDir(); |
| Iterator<String> pathElementIterator = splitter.split(path).iterator(); |
| String pathElement = null; |
| while (pathElementIterator.hasNext()) { |
| pathElement = pathElementIterator.next(); |
| assert !pathElement.isEmpty(); |
| currentDir = loadVDir(currentDir, pathElement); |
| } |
| } |
| |
| private void loadVFile(@Nonnull String path) |
| throws NotDirectoryException, NotFileException { |
| CaseInsensitiveVDir currentDir = getRootDir(); |
| Iterator<String> pathElementIterator = splitter.split(path).iterator(); |
| String pathElement = null; |
| while (pathElementIterator.hasNext()) { |
| pathElement = pathElementIterator.next(); |
| assert !pathElement.isEmpty(); |
| if (pathElementIterator.hasNext()) { |
| // simpleName is a dir name |
| currentDir = loadVDir(currentDir, pathElement); |
| } |
| } |
| loadVFile(currentDir, pathElement); |
| } |
| |
| @Override |
| @Nonnull |
| public Set<Capabilities> getCapabilities() { |
| return capabilities; |
| } |
| |
| @Override |
| @Nonnull |
| public Location getLocation() { |
| return vfs.getLocation(); |
| } |
| |
| @Override |
| @Nonnull |
| public String getPath() { |
| return vfs.getPath(); |
| } |
| |
| @Override |
| @Nonnull |
| public CaseInsensitiveVDir getRootDir() { |
| used = true; |
| |
| return root; |
| } |
| |
| @Override |
| public synchronized void close() throws CannotCloseException { |
| if (!closed) { |
| try { |
| PrintStream printer = |
| new PrintStream(vfs.getRootDir().createVFile(INDEX_NAME).getOutputStream()); |
| |
| printIndex(printer, getRootDir()); |
| printer.flush(); |
| printer.close(); |
| |
| if (debug) { |
| printer = new PrintStream(vfs.getRootDir().createVFile(DEBUG_NAME).getOutputStream()); |
| |
| printDebug(printer, getRootDir()); |
| printer.flush(); |
| printer.close(); |
| } |
| } catch (WrongPermissionException | CannotCreateFileException e) { |
| throw new CannotCloseException(this, e); |
| } |
| |
| vfs.close(); |
| closed = true; |
| } |
| } |
| |
| private void printIndex(@Nonnull PrintStream printer, @Nonnull InMemoryVDir dir) { |
| Collection<? extends BaseVElement> elements = dir.list(); |
| if (elements.size() > 0) { |
| for (BaseVElement element : elements) { |
| if (element.isVDir()) { |
| printIndex(printer, (InMemoryVDir) element); |
| } else { |
| CaseInsensitiveVFile file = (CaseInsensitiveVFile) element; |
| |
| printer.print("f:"); |
| printer.print(file.getPath().getPathAsString(INDEX_SEPARATOR)); |
| printer.println(); |
| } |
| } |
| } else { |
| printer.print("d:"); |
| printer.print(dir.getPath().getPathAsString(INDEX_SEPARATOR)); |
| printer.println(); |
| } |
| } |
| |
| private void printDebug(@Nonnull PrintStream printer, @Nonnull InMemoryVDir dir) { |
| Collection<? extends BaseVElement> elements = dir.list(); |
| |
| printer.print("d:"); |
| printer.print(dir.getPath().getPathAsString(File.separatorChar)); |
| printer.println(); |
| |
| for (BaseVElement element : elements) { |
| if (element.isVDir()) { |
| printDebug(printer, (InMemoryVDir) element); |
| } else { |
| CaseInsensitiveVFile file = (CaseInsensitiveVFile) element; |
| |
| printer.print("f:"); |
| printer.print(loadAndGetEncodedFile(file).getPath().getPathAsString(File.separatorChar)); |
| printer.print(":"); |
| printer.print(file.getPath().getPathAsString(File.separatorChar)); |
| printer.println(); |
| } |
| } |
| } |
| |
| // |
| // Stream |
| // |
| |
| @Nonnull |
| private BaseVFile loadAndGetEncodedFile(@Nonnull CaseInsensitiveVFile file) { |
| |
| BaseVFile encodedFile = file.getEncodedFile(); |
| if (encodedFile == null) { |
| try { |
| encodedFile = vfs.getRootDir().getVFile(encode(file.getPath())); |
| file.setEncodedFile(encodedFile); |
| } catch (NotDirectoryException | NotFileException | NoSuchFileException e) { |
| throw new RuntimeBadVFSFormatException(vfs, vfs.getLocation(), e); |
| } |
| } |
| return encodedFile; |
| } |
| |
| @Override |
| @Nonnull |
| InputStream openRead(@Nonnull CaseInsensitiveVFile file) throws WrongPermissionException { |
| assert !isClosed(); |
| |
| return loadAndGetEncodedFile(file).getInputStream(); |
| } |
| |
| @Override |
| @Nonnull |
| OutputStream openWrite(@Nonnull CaseInsensitiveVFile file) throws WrongPermissionException { |
| return openWrite(file, false); |
| } |
| |
| @Override |
| @Nonnull |
| OutputStream openWrite(@Nonnull CaseInsensitiveVFile file, boolean append) |
| throws WrongPermissionException { |
| assert !isClosed(); |
| |
| return loadAndGetEncodedFile(file).getOutputStream(append); |
| } |
| |
| // |
| // VElement |
| // |
| |
| @Override |
| @Nonnull |
| CaseInsensitiveVDir getVDir(@Nonnull CaseInsensitiveVDir parent, @Nonnull String name) |
| throws NotDirectoryException, NoSuchFileException { |
| CaseInsensitiveVDir vDir = getVDirFromCache(parent, name); |
| if (vDir != null) { |
| return vDir; |
| } else { |
| throw new NoSuchFileException(getVDirLocation(parent, name)); |
| } |
| } |
| |
| @Override |
| @Nonnull |
| CaseInsensitiveVFile getVFile(@Nonnull CaseInsensitiveVDir parent, @Nonnull String name) |
| throws NotFileException, NoSuchFileException { |
| CaseInsensitiveVFile vFile = getVFileFromCache(parent, name); |
| if (vFile != null) { |
| return vFile; |
| } else { |
| throw new NoSuchFileException(getVFileLocation(parent, name)); |
| } |
| } |
| |
| @CheckForNull |
| CaseInsensitiveVFile getVFileFromCache(@Nonnull CaseInsensitiveVDir parent, @Nonnull String name) |
| throws NotFileException { |
| BaseVElement element = parent.getFromCache(name); |
| if (element == null) { |
| return null; |
| } else if (!element.isVDir()) { |
| return (CaseInsensitiveVFile) element; |
| } else { |
| throw new NotFileException(getVFileLocation(parent, name)); |
| } |
| } |
| |
| @CheckForNull |
| CaseInsensitiveVDir getVDirFromCache(@Nonnull CaseInsensitiveVDir parent, @Nonnull String name) |
| throws NotDirectoryException { |
| BaseVElement element = parent.getFromCache(name); |
| if (element == null) { |
| return null; |
| } else if (element.isVDir()) { |
| return (CaseInsensitiveVDir) element; |
| } else { |
| throw new NotDirectoryException(getVDirLocation(parent, name)); |
| } |
| } |
| |
| @Override |
| @Nonnull |
| CaseInsensitiveVDir createVDir(@Nonnull CaseInsensitiveVDir parent, |
| @Nonnull String name) throws CannotCreateFileException { |
| assert !isClosed(); |
| |
| try { |
| return loadVDir(parent, name); |
| } catch (NotDirectoryException e) { |
| throw new CannotCreateFileException(getVDirLocation(parent, name)); |
| } |
| } |
| |
| @Override |
| @Nonnull |
| synchronized CaseInsensitiveVFile createVFile( |
| @Nonnull CaseInsensitiveVDir parent, @Nonnull String name) throws CannotCreateFileException { |
| assert !isClosed(); |
| try { |
| CaseInsensitiveVFile vFile = getVFileFromCache(parent, name); |
| if (vFile != null) { |
| return vFile; |
| } else { |
| CaseInsensitiveVFile original = new CaseInsensitiveVFile(this, parent, name); |
| BaseVFile encoded = vfs.getRootDir().createVFile(encode(original.getPath())); |
| original.setEncodedFile(encoded); |
| parent.putInCache(name, original); |
| |
| return original; |
| } |
| } catch (NotFileException e) { |
| throw new CannotCreateFileException(getVFileLocation(parent, name)); |
| } |
| } |
| |
| @Nonnull |
| synchronized CaseInsensitiveVDir loadVDir( |
| @Nonnull CaseInsensitiveVDir parent, @Nonnull String name) throws NotDirectoryException { |
| assert !isClosed(); |
| |
| CaseInsensitiveVDir vDir = getVDirFromCache(parent, name); |
| if (vDir != null) { |
| return vDir; |
| } else { |
| CaseInsensitiveVDir dir = new CaseInsensitiveVDir(this, parent, name); |
| parent.putInCache(name, dir); |
| return dir; |
| } |
| } |
| |
| @Nonnull |
| synchronized CaseInsensitiveVFile loadVFile( |
| @Nonnull CaseInsensitiveVDir parent, @Nonnull String name) throws NotFileException { |
| assert !isClosed(); |
| CaseInsensitiveVFile vFile = getVFileFromCache(parent, name); |
| if (vFile != null) { |
| return vFile; |
| } else { |
| CaseInsensitiveVFile original = new CaseInsensitiveVFile(this, parent, name); |
| parent.putInCache(name, original); |
| |
| return original; |
| } |
| } |
| |
| @Override |
| @Nonnull |
| void delete(@Nonnull CaseInsensitiveVFile file) throws CannotDeleteFileException { |
| assert !isClosed(); |
| |
| try { |
| BaseVFile encoded = vfs.getRootDir().getVFile(encode(file.getPath())); |
| vfs.delete(encoded); |
| file.deleteFromCache(); |
| } catch (NotDirectoryException e) { |
| throw new CannotDeleteFileException(file); |
| } catch (NotFileException e) { |
| throw new CannotDeleteFileException(file); |
| } catch (NoSuchFileException e) { |
| throw new CannotDeleteFileException(file); |
| } |
| } |
| |
| @Override |
| @Nonnull |
| Collection<? extends BaseVElement> list(@Nonnull CaseInsensitiveVDir dir) { |
| return dir.getAllFromCache(); |
| } |
| |
| @Override |
| boolean isEmpty(@Nonnull CaseInsensitiveVDir dir) { |
| return dir.isEmpty(); |
| } |
| |
| @Override |
| @Nonnull |
| public FileTime getLastModified(@Nonnull CaseInsensitiveVFile file) |
| throws CannotGetModificationTimeException { |
| return vfs.getLastModified(loadAndGetEncodedFile(file)); |
| } |
| |
| // |
| // Location |
| // |
| |
| @Override |
| @Nonnull |
| Location getVFileLocation(@Nonnull CaseInsensitiveVFile file) { |
| return vfs.getVFileLocation(loadAndGetEncodedFile(file)); |
| } |
| |
| @Override |
| @Nonnull |
| Location getVFileLocation(@Nonnull CaseInsensitiveVDir parent, @Nonnull String name) { |
| return vfs.getRootDir().getVFileLocation( |
| encode(parent.getPath().clone().appendPath(new VPath(name, '/')))); |
| } |
| |
| @Override |
| @Nonnull |
| Location getVDirLocation(@Nonnull CaseInsensitiveVDir dir) { |
| return vfs.getRootDir().getVDirLocation(encode(dir.getPath())); |
| } |
| |
| @Override |
| @Nonnull |
| Location getVDirLocation(@Nonnull CaseInsensitiveVDir parent, @Nonnull String name) { |
| return vfs.getRootDir().getVDirLocation( |
| encode(parent.getPath().clone().appendPath(new VPath(name, '/')))); |
| } |
| |
| @Override |
| @Nonnull |
| Location getVFileLocation(@Nonnull CaseInsensitiveVDir parent, @Nonnull VPath path) { |
| return vfs.getRootDir().getVFileLocation(encode(parent.getPath().clone().appendPath(path))); |
| } |
| |
| @Override |
| @Nonnull |
| Location getVDirLocation(@Nonnull CaseInsensitiveVDir parent, @Nonnull VPath path) { |
| return vfs.getRootDir().getVDirLocation(encode(parent.getPath().clone().appendPath(path))); |
| } |
| |
| // |
| // Misc |
| // |
| |
| @Override |
| public boolean needsSequentialWriting() { |
| return vfs.needsSequentialWriting(); |
| } |
| |
| // |
| // Encode / Decode |
| // |
| |
| @Nonnull |
| private VPath encode(@Nonnull VPath path) { |
| char[] digest = encode(mdf.create().digest(path.getPathAsString('/').getBytes())); |
| |
| StringBuilder sb = new StringBuilder(); |
| int idx = 0; |
| try { |
| for (int groupIdx = 0; groupIdx < numGroups; groupIdx++) { |
| for (int letterIdx = 0; letterIdx < groupSize; letterIdx++) { |
| sb.append(digest[idx++]); |
| } |
| sb.append('/'); |
| } |
| |
| if (idx < digest.length) { |
| sb.append(digest, idx, digest.length - idx); |
| } else { |
| // Remove the last /, it is not a directory here |
| sb.setLength(sb.length() - 1); |
| } |
| } catch (IndexOutOfBoundsException e) { |
| } |
| |
| |
| return new VPath(sb.toString(), '/'); |
| } |
| |
| @Nonnull |
| private static final byte[] code = "0123456789ABCDEF".getBytes(); |
| |
| @Nonnull |
| static char[] encode(@Nonnull byte[] bytes) { |
| char[] array = new char[bytes.length * 2]; |
| |
| for (int idx = 0; idx < bytes.length; idx++) { |
| array[(idx << 1)] = (char) code[(bytes[idx] & 0xF0) >> 4]; |
| array[(idx << 1) + 1] = (char) code[(bytes[idx] & 0x0F)]; |
| } |
| |
| return array; |
| } |
| |
| @Override |
| @Nonnull |
| VPath getPathFromDir(@Nonnull CaseInsensitiveVDir parent, @Nonnull CaseInsensitiveVFile file) { |
| StringBuilder path = getPathFromDirInternal(parent, (CaseInsensitiveVDir) file.getParent()) |
| .append(file.getName()); |
| return new VPath(path.toString(), '/'); |
| } |
| |
| @Nonnull |
| private StringBuilder getPathFromDirInternal(@Nonnull CaseInsensitiveVDir baseDir, |
| @Nonnull CaseInsensitiveVDir currentDir) { |
| if (baseDir == currentDir) { |
| return new StringBuilder(); |
| } |
| CaseInsensitiveVDir currentParent = (CaseInsensitiveVDir) currentDir.getParent(); |
| assert currentParent != null; |
| return getPathFromDirInternal(baseDir, currentParent).append(currentDir.getName()).append('/'); |
| } |
| |
| @Override |
| @Nonnull |
| VPath getPathFromRoot(@Nonnull CaseInsensitiveVFile file) { |
| return getPathFromDir(root, file); |
| } |
| |
| @Override |
| @Nonnull |
| public StreamFileStatus getStatus() { |
| if (!used) { |
| return StreamFileStatus.NOT_USED; |
| } else if (closed) { |
| return StreamFileStatus.CLOSED; |
| } else { |
| return StreamFileStatus.OPEN; |
| } |
| } |
| |
| @Override |
| @CheckForNull |
| public String getInfoString() { |
| return vfs.getInfoString(); |
| } |
| |
| @Override |
| public String toString() { |
| return "ciFS >> " + vfs.toString(); |
| } |
| } |