package com.aptana.ide.filesystem.s3; import java.util.ArrayList; import java.util.List; import org.eclipse.core.filesystem.IFileInfo; import org.eclipse.core.filesystem.IFileStore; import org.eclipse.core.filesystem.IFileTree; import org.eclipse.core.filesystem.provider.FileInfo; import org.eclipse.core.filesystem.provider.FileTree; import com.amazon.s3.ListEntry; /** * A more efficient way of accessing/querying the S3 file tree. Since when we query root for it's children we end up * with entire hierarchy below it (not just direct descendants), just store all the sub-tree entries and generate * IFileInfo and IFileStore's from it. * * @author cwilliams */ public class S3FileTree extends FileTree implements IFileTree { private List<ListEntry> entries; public S3FileTree(IFileStore treeRoot, List<ListEntry> entries) { super(treeRoot); this.entries = entries; } @Override public IFileInfo[] getChildInfos(IFileStore store) { if (!(store instanceof S3FileStore)) return null; List<ListEntry> matches = getChildEntries(store); List<IFileInfo> infos = new ArrayList<IFileInfo>(); for (ListEntry match : matches) { FileInfo fileInfo = generateFileInfo(match); // TODO these will always be files. What about "parent directories" that these keys imply? infos.add(fileInfo); } return infos.toArray(new IFileInfo[infos.size()]); } private FileInfo generateFileInfo(ListEntry match) { String name = match.key; int lastSlash = name.lastIndexOf("/"); boolean isDirectory = false; if (lastSlash != -1) { if (lastSlash == (name.length() - 1)) { name = name.substring(0, lastSlash); isDirectory = true; lastSlash = name.lastIndexOf("/"); if (lastSlash != -1) name = name.substring(lastSlash + 1); } else { name = name.substring(lastSlash); if (name.startsWith("/")) name = name.substring(1); } } FileInfo fileInfo = new FileInfo(name); fileInfo.setExists(true); fileInfo.setLastModified(match.lastModified.getTime()); fileInfo.setLength(match.size); fileInfo.setDirectory(isDirectory); return fileInfo; } private List<ListEntry> getChildEntries(IFileStore store) { S3FileStore s3Store = (S3FileStore) store; String key = s3Store.getKey(); if (key.startsWith("/")) key = key.substring(1); // Find all the entries that have the key as prefix List<ListEntry> matches = new ArrayList<ListEntry>(); for (ListEntry entry : entries) { String relative = entry.key; if (relative.startsWith("/")) relative = relative.substring(1); if (!(key.length() == 0 || relative.startsWith(key + "/"))) continue; // Only limit to direct children! relative = relative.substring(key.length()); if (relative.startsWith("/")) relative = relative.substring(1); if (relative.endsWith("/")) relative = relative.substring(0, relative.length() - 1); if (relative.length() == 0 || relative.indexOf("/") != -1) continue; matches.add(entry); } return matches; } @Override public IFileStore[] getChildStores(IFileStore store) { if (!(store instanceof S3FileStore)) return null; S3FileStore s3Store = (S3FileStore) store; List<ListEntry> matches = getChildEntries(s3Store); List<IFileStore> childrenStores = new ArrayList<IFileStore>(); for (ListEntry match : matches) { String childName = match.key; if (childName.endsWith("/")) childName = childName.substring(0, childName.length() - 1); childrenStores.add(s3Store.getChild(childName)); } return childrenStores.toArray(new IFileStore[childrenStores.size()]); } @Override public IFileInfo getFileInfo(IFileStore store) { if (!(store instanceof S3FileStore)) return null; S3FileStore s3Store = (S3FileStore) store; String key = s3Store.getKey(); // generate an info from a ListEntry if we have a match! for (ListEntry entry : entries) { String entryKey = entry.key; if (entryKey.startsWith("/")) entryKey = entryKey.substring(1); if (entryKey.endsWith("/")) entryKey = entryKey.substring(0, entryKey.length() - 1); if (entryKey.equals(key)) return generateFileInfo(entry); } return s3Store.fetchInfo(); } }