/*
* Copyright (c) 2013, the authors.
*
* This file is part of 'DXFS'.
*
* DXFS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DXFS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DXFS. If not, see <http://www.gnu.org/licenses/>.
*/
/*
* Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package nextflow.fs.dx;
import java.io.IOException;
import java.nio.file.FileStore;
import java.nio.file.FileSystem;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.WatchService;
import java.nio.file.attribute.UserPrincipalLookupService;
import java.nio.file.spi.FileSystemProvider;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import nextflow.fs.dx.api.DxApi;
/**
* A file system implementation for the DnaNexus storage
*
* @author Paolo Di Tommaso <paolo.ditommaso@gmail.com>
* @author Beatriz San Juan <bmsanjuan@gmail.com>
*/
public class DxFileSystem extends FileSystem {
static private Set<String> FILE_ATTRIBUTE_VIEWS = Collections.unmodifiableSet( new HashSet<String>(Arrays.asList( DxFileAttributeView.NAME )) );
private DxFileSystemProvider provider;
private DxPath rootDirectory;
private String contextId;
private DxPath defaultDirectory;
private String contextName;
public DxFileSystem( DxFileSystemProvider provider ) {
this(provider, provider.defaultContextId);
}
public DxFileSystem( DxFileSystemProvider provider, String contextId ) {
this(provider, contextId, contextId);
}
public DxFileSystem( DxFileSystemProvider provider, String contextId, String label ) {
init(provider,contextId,label);
}
private void init( DxFileSystemProvider provider, String contextId, String label ) {
this.rootDirectory = new DxPath(this, "/");
this.defaultDirectory = new DxPath(this, "/");
this.provider = provider;
this.contextId = contextId;
this.contextName = label;
}
/**
* @return The reference of the {@code DxApi} object
*/
DxApi remoter() {
return provider.api;
}
public String getContextName() {
return contextName;
}
// package private
String getContextId() {
return contextId;
}
/**
* {@inheritDoc}
*/
@Override
public FileSystemProvider provider() {
return provider;
}
public DxPath rootDirectory() {
return rootDirectory;
}
public DxPath defaultDirectory() {
return defaultDirectory;
}
@Override
public void close() throws IOException {
// nothing to close
}
@Override
public boolean isOpen() {
return true;
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public String getSeparator() {
return "/";
}
@Override
public Iterable<Path> getRootDirectories() {
final List<Path> result = Collections.unmodifiableList(Arrays.asList((Path) rootDirectory));
return new Iterable<Path>() {
public Iterator<Path> iterator() {
return result.iterator();
}
};
}
// TODO getFileStores
@Override
public Iterable<FileStore> getFileStores() {
final List<FileStore> result = new ArrayList<FileStore>(1);
result.add( new DxFileStore(this) );
return new Iterable<FileStore>() {
@Override
public Iterator<FileStore> iterator() {
return result.iterator();
}
};
}
@Override
public Set<String> supportedFileAttributeViews() {
return FILE_ATTRIBUTE_VIEWS;
}
/**
* Factory method for {@code DxPath} object
*
* @param first First element in the path
* @param more Other path elements
*
* @return
*/
@Override
public DxPath getPath(String first, String... more) {
String path;
if (more.length == 0) {
path = first;
} else {
StringBuilder sb = new StringBuilder();
sb.append(first);
for (String segment: more) {
if (segment.length() > 0) {
if (sb.length() > 0)
sb.append('/');
sb.append(segment);
}
}
path = sb.toString();
}
return new DxPath(this, path);
}
// TODO getPathMatcher
@Override
public PathMatcher getPathMatcher(String syntaxAndPattern) {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public UserPrincipalLookupService getUserPrincipalLookupService() {
throw new UnsupportedOperationException();
}
@Override
public WatchService newWatchService() throws IOException {
throw new UnsupportedOperationException();
}
/**
* An iterator over the content of the specified folder
*
* @param path The path of the folder to iterate over
* @return
*/
@SuppressWarnings("unchecked")
Iterator<DxPath> folderIterator(DxPath path) throws IOException {
String sPath = path.normalize().toAbsolutePath().toRawPath();
Map<String,Object> all = remoter().folderList(contextId, sPath, true);
// the above method returns a map containing two lists: "folders" and "objects" (the files)
final List<String> folders = (List<String>)all.get("folders");
final List<Map<String,Object>> files = (List<Map<String,Object>>)all.get("objects");
// total number of elements
final int length = folders.size() + files.size();
return new Iterator<DxPath>() {
int count = 0;
@Override
public boolean hasNext() { return count<length; }
@Override
public DxPath next() {
int i = count++;
if( i<files.size() ) {
// find out the 'file' meta data and return a DxPath for it
Map<String,Object> entry = files.get(i);
String id = (String)entry.get("id");
Map<String,Object> attr = (Map<String,Object>)entry.get("describe");
String name = (String)attr.get("name");
String folder = (String)attr.get("folder");
// add the resulting path to the list
return DxPath.file( DxFileSystem.this, endsWithSlash(folder)+name, id, attr);
}
else if( i < length ) {
// return a DxPath for the folder
return DxPath.directory( DxFileSystem.this, folders.get(i-files.size()) );
}
else {
throw new IllegalStateException();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
/**
* ListCmd the content of the specified folder
*
* @param path The path of the folder for which list the content
* @return A list holding a collection of {@code DxPath} objects or a empty list if the folder contains nothing
*/
@SuppressWarnings("unchecked")
List<DxPath> folderList(DxPath path) throws IOException {
// the result list collecting all the results
final List<DxPath> result = new LinkedList<>( );
Iterator<DxPath> it = folderIterator(path);
while( it.hasNext() ) {
result.add( it.next() );
}
return result;
}
private static String endsWithSlash( String path ) {
return path.endsWith("/") ? path : path + '/';
}
@Deprecated
List fileFind(String path) throws IOException {
return remoter().fileFind(contextId, path);
}
public void createFolder(DxPath path) throws IOException {
createFolder(path,true);
}
public void createFolder(DxPath path, boolean createParents) throws IOException {
String str = path.normalize().toAbsolutePath().toRawPath();
remoter().folderCreate(contextId, str, createParents);
path.type = DxPath.PathType.DIRECTORY;
}
public void deleteFolder( DxPath path, boolean recurse ) throws IOException {
String str = path.normalize().toAbsolutePath().toRawPath();
remoter().folderDelete(contextId, str, recurse);
}
public void deleteFiles( DxPath... paths ) throws IOException {
int i=0;
if( paths == null ) { return; }
String[] fileIds = new String[ paths.length ];
for( DxPath item : paths ) {
fileIds[i++] = item.getFileId();
}
remoter().fileDelete(contextId, fileIds);
}
public DxFileAttributes describeFile( DxPath path ) throws IOException {
Map<String,Object> attr = remoter().fileDescribe(path.getFileId());
return DxFileAttributes.file(attr);
}
public void fileRename( DxPath path, String name) throws IOException {
remoter().rename(contextId, path.getFileId(), name);
}
public void fileAddTags(DxPath path, String[] value) throws IOException {
remoter().addFileTags(contextId, path.getFileId(), value);
}
public void fileAddTypes(DxPath path, String[] value) throws IOException {
remoter().addFileTypes(contextId, path.getFileId(), value);
}
public String fileNew( DxPath path ) throws IOException {
String str = path.normalize().toAbsolutePath().toRawPath();
String fileId = remoter().fileNew(contextId, str);
path.fileId = fileId;
path.type = DxPath.PathType.FILE;
return fileId;
}
public Map<String,Object> fileCopy( DxPath source, DxPath target ) throws IOException {
DxPath normalized = target.normalize().toAbsolutePath();
DxPath targetPath = normalized.getParent();
String targetStr = targetPath != null ? targetPath.toRawPath() : "/";
String targetCtxId = target.getFileSystem().contextId;
Map<String,Object> result = remoter().fileClone(contextId, source.getFileId(), targetCtxId, targetStr );
return result;
}
}