mirror of
https://github.com/mamoe/mirai.git
synced 2025-04-24 20:43:33 +08:00
no java nio
This commit is contained in:
parent
12533859ec
commit
723cd8458a
mirai-console/backend/mirai-console/src
MiraiConsole.ktMiraiConsoleImplementation.kt
com/llamalab/safs
AccessDeniedException.javaAtomicMoveNotSupportedException.javaClosedFileSystemException.javaClosedWatchServiceException.javaCopyOption.javaDirectoryIteratorException.javaDirectoryNotEmptyException.javaDirectoryStream.javaFileAlreadyExistsException.javaFileStore.javaFileSystem.javaFileSystemAlreadyExistsException.javaFileSystemException.javaFileSystemLoopException.javaFileSystemNotFoundException.javaFileSystems.javaFileVisitOption.javaFileVisitResult.javaFileVisitor.javaFiles.javaInvalidPathException.javaLinkOption.javaNoSuchFileException.javaNotDirectoryException.javaNotLinkException.javaOpenOption.javaPath.javaPathMatcher.javaPaths.javaProviderMismatchException.javaProviderNotFoundException.javaReadOnlyFileSystemException.javaSimpleFileVisitor.javaStandardCopyOption.javaStandardOpenOption.javaStandardWatchEventKinds.javaWatchEvent.javaWatchKey.javaWatchService.javaWatchable.java
attribute
AttributeView.javaBasicFileAttributeView.javaBasicFileAttributes.javaFileAttribute.javaFileAttributeView.javaFileOwnerAttributeView.javaFileTime.javaGroupPrincipal.javaPosixFileAttributeView.javaPosixFileAttributes.javaPosixFilePermission.javaPosixFilePermissions.javaUserPrincipal.javaUserPrincipalLookupService.javaUserPrincipalNotFoundException.java
channels
internal
AbstractDirectoryStream.javaAbstractFileSystemProvider.javaAbstractWatchKey.javaAbstractWatchService.javaAttributeParser.javaBasicFileAttribute.javaBasicFileAttributeValue.javaCompleteBasicFileAttributes.javaDefaultFileSystem.javaFileType.javaPartialBasicFileAttributes.javaPathDescender.javaSearchSet.javaSegmentEntry.javaUserPrincipalFactory.javaUtils.javaWatchEventKind.java
java
DefaultJavaFileSystemProvider.javaJavaFileSystem.javaJavaFileSystemProvider.javaSeekableByteChannelWrapper.java
kotlin/io/path
spi
unix
data
internal
auth
data
plugin
shutdown
plugin
@ -34,7 +34,7 @@ import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||
import net.mamoe.mirai.console.util.SemVersion
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
import java.time.Instant
|
||||
|
||||
/**
|
||||
|
@ -40,7 +40,7 @@ import net.mamoe.mirai.console.util.ConsoleInput
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalApi
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.annotation.AnnotationTarget.*
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class AccessDeniedException extends FileSystemException {
|
||||
|
||||
public AccessDeniedException (String file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public AccessDeniedException (String file, String otherFile, String reason) {
|
||||
super(file, otherFile, reason);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class AtomicMoveNotSupportedException extends FileSystemException {
|
||||
|
||||
public AtomicMoveNotSupportedException (String source, String target, String reason) {
|
||||
super(source, target, reason);
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ClosedFileSystemException extends IllegalStateException {
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ClosedWatchServiceException extends IllegalStateException {}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public interface CopyOption {
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class DirectoryIteratorException extends ConcurrentModificationException {
|
||||
|
||||
public DirectoryIteratorException (IOException cause) {
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final IOException getCause () {
|
||||
return (IOException)super.getCause();
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class DirectoryNotEmptyException extends FileSystemException {
|
||||
|
||||
public DirectoryNotEmptyException (String file) {
|
||||
super(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface DirectoryStream<T> extends Iterable<T>, Closeable {
|
||||
public interface Filter<T> {
|
||||
public boolean accept (T entry) throws IOException;
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class FileAlreadyExistsException extends FileSystemException {
|
||||
|
||||
public FileAlreadyExistsException (String file) {
|
||||
super(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class FileStore {
|
||||
protected FileStore () {}
|
||||
public abstract String name ();
|
||||
public abstract String type ();
|
||||
public abstract boolean isReadOnly ();
|
||||
public abstract long getTotalSpace () throws IOException;
|
||||
public abstract long getUsableSpace () throws IOException;
|
||||
public abstract long getUnallocatedSpace () throws IOException;
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import com.llamalab.safs.attribute.UserPrincipalLookupService;
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class FileSystem implements Closeable {
|
||||
public abstract FileSystemProvider provider ();
|
||||
public abstract boolean isOpen ();
|
||||
public abstract boolean isReadOnly ();
|
||||
public abstract Set<String> supportedFileAttributeViews();
|
||||
public abstract String getSeparator ();
|
||||
public abstract Path getPath (String first, String... more);
|
||||
public abstract PathMatcher getPathMatcher (String syntaxAndPattern);
|
||||
public abstract Iterable<FileStore> getFileStores ();
|
||||
public abstract Iterable<Path> getRootDirectories ();
|
||||
public abstract UserPrincipalLookupService getUserPrincipalLookupService ();
|
||||
public abstract WatchService newWatchService () throws IOException;
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class FileSystemAlreadyExistsException extends RuntimeException {
|
||||
|
||||
public FileSystemAlreadyExistsException () {}
|
||||
|
||||
public FileSystemAlreadyExistsException (String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class FileSystemException extends IOException {
|
||||
|
||||
private final String file;
|
||||
private final String otherFile;
|
||||
|
||||
public FileSystemException (String file) {
|
||||
this(file, null, null);
|
||||
}
|
||||
|
||||
public FileSystemException (String file, String otherFile, String reason) {
|
||||
super(reason);
|
||||
this.file = file;
|
||||
this.otherFile = otherFile;
|
||||
}
|
||||
|
||||
public String getFile () {
|
||||
return file;
|
||||
}
|
||||
|
||||
public String getOtherFile () {
|
||||
return otherFile;
|
||||
}
|
||||
|
||||
public String getReason () {
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (file == null && otherFile == null)
|
||||
return getReason();
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
if (file != null)
|
||||
sb.append(file);
|
||||
if (otherFile != null)
|
||||
sb.append(" -> ").append(otherFile);
|
||||
if (getReason() != null)
|
||||
sb.append(": ").append(getReason());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class FileSystemLoopException extends FileSystemException {
|
||||
|
||||
public FileSystemLoopException (String file) {
|
||||
super(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class FileSystemNotFoundException extends RuntimeException {
|
||||
|
||||
public FileSystemNotFoundException () {}
|
||||
|
||||
public FileSystemNotFoundException (String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import com.llamalab.safs.java.DefaultJavaFileSystemProvider;
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public final class FileSystems {
|
||||
|
||||
private FileSystems () {}
|
||||
|
||||
public static FileSystem getDefault () {
|
||||
return DefaultFileSystemHolder.fileSystem;
|
||||
}
|
||||
|
||||
public static FileSystem getFileSystem (URI uri) {
|
||||
for (final FileSystemProvider provider : FileSystemProvider.installedProviders()) {
|
||||
if (provider.getScheme().equalsIgnoreCase(uri.getScheme()))
|
||||
return provider.getFileSystem(uri);
|
||||
}
|
||||
throw new ProviderNotFoundException(uri.getScheme());
|
||||
}
|
||||
|
||||
public static FileSystem newFileSystem (URI uri, Map<String,?> env) throws IOException {
|
||||
for (final FileSystemProvider provider : FileSystemProvider.installedProviders()) {
|
||||
if (provider.getScheme().equalsIgnoreCase(uri.getScheme()))
|
||||
return provider.newFileSystem(uri, env);
|
||||
}
|
||||
throw new ProviderNotFoundException(uri.getScheme());
|
||||
}
|
||||
|
||||
|
||||
private static final class DefaultFileSystemHolder {
|
||||
|
||||
static final FileSystem fileSystem = loadDefaultProvider().getFileSystem(URI.create("file:///"));
|
||||
|
||||
private static FileSystemProvider loadDefaultProvider () {
|
||||
FileSystemProvider provider = new DefaultJavaFileSystemProvider();
|
||||
final String value = System.getProperty("com.llamalab.safs.spi.DefaultFileSystemProvider");
|
||||
if (value != null) {
|
||||
try {
|
||||
for (final String className : value.split(",")) {
|
||||
provider = (FileSystemProvider)Class.forName(className)
|
||||
.getDeclaredConstructor(FileSystemProvider.class)
|
||||
.newInstance(provider);
|
||||
}
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new Error(t);
|
||||
}
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
} // class DefaultFileSystemHolder
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public enum FileVisitOption {
|
||||
/** Follow symbolic links. */
|
||||
FOLLOW_LINKS,
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public enum FileVisitResult {
|
||||
CONTINUE,
|
||||
TERMINATE,
|
||||
SKIP_SUBTREE,
|
||||
SKIP_SIBLINGS,
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface FileVisitor<T> {
|
||||
public FileVisitResult preVisitDirectory (T dir, BasicFileAttributes attrs) throws IOException;
|
||||
public FileVisitResult postVisitDirectory (T dir, IOException e) throws IOException;
|
||||
public FileVisitResult visitFile (T file, BasicFileAttributes attrs) throws IOException;
|
||||
public FileVisitResult visitFileFailed (T file, IOException e) throws IOException;
|
||||
}
|
@ -0,0 +1,630 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
import com.llamalab.safs.attribute.FileAttribute;
|
||||
import com.llamalab.safs.attribute.FileAttributeView;
|
||||
import com.llamalab.safs.attribute.FileTime;
|
||||
import com.llamalab.safs.internal.BasicFileAttribute;
|
||||
import com.llamalab.safs.internal.DefaultFileSystem;
|
||||
import com.llamalab.safs.internal.SearchSet;
|
||||
import com.llamalab.safs.internal.Utils;
|
||||
import com.llamalab.safs.channels.SeekableByteChannel;
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
import com.llamalab.safs.spi.FileTypeDetector;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
public final class Files {
|
||||
|
||||
//private static final OpenOption[] COPY_REPLACE_EXISTING = { StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE };
|
||||
//private static final OpenOption[] COPY_KEEP_EXISTING = { StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW };
|
||||
private static final LinkOption[] LINK_NOFOLLOW_LINKS = { LinkOption.NOFOLLOW_LINKS };
|
||||
private static final Set<FileVisitOption> VISIT_EMPTY = EnumSet.noneOf(FileVisitOption.class);
|
||||
|
||||
private static final String COPIED_ATTRIBUTES = BasicFileAttribute.lastModifiedTime + "," + BasicFileAttribute.lastAccessTime + "," + BasicFileAttribute.creationTime;
|
||||
|
||||
private static final SecureRandom TEMP_RAND = new SecureRandom();
|
||||
|
||||
private Files () {}
|
||||
|
||||
public static FileStore getFileStore (Path path) throws IOException {
|
||||
return provider(path).getFileStore(path);
|
||||
}
|
||||
|
||||
public static void delete (Path path) throws IOException {
|
||||
provider(path).delete(path);
|
||||
}
|
||||
|
||||
private static void deleteQuietly (Path path) {
|
||||
try {
|
||||
delete(path);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
public static boolean deleteIfExists (Path path) throws IOException {
|
||||
return provider(path).deleteIfExists(path);
|
||||
}
|
||||
|
||||
public static Path createDirectory (Path dir, FileAttribute<?>... attrs) throws IOException {
|
||||
provider(dir).createDirectory(dir, attrs);
|
||||
return dir;
|
||||
}
|
||||
|
||||
public static Path createDirectories (Path dir, FileAttribute<?>... attrs) throws IOException {
|
||||
final FileSystemProvider provider = provider(dir);
|
||||
try {
|
||||
provider.createDirectory(dir, attrs);
|
||||
return dir;
|
||||
}
|
||||
catch (FileAlreadyExistsException e) {
|
||||
if (Files.isDirectory(dir))
|
||||
return dir;
|
||||
throw e;
|
||||
}
|
||||
catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
final Path absolute = dir.toAbsolutePath();
|
||||
Path parent = absolute.getParent();
|
||||
for (;; parent = parent.getParent()) {
|
||||
if (parent == null)
|
||||
throw new FileSystemException(dir.toString(), null, "No root directory");
|
||||
try {
|
||||
final BasicFileAttributes basic = provider.readAttributes(parent, BasicFileAttributes.class);
|
||||
if (!basic.isDirectory())
|
||||
throw new FileAlreadyExistsException(dir.toString());
|
||||
break;
|
||||
}
|
||||
catch (NoSuchFileException e) {
|
||||
// continue
|
||||
}
|
||||
}
|
||||
Path child = parent;
|
||||
for (final Path name : parent.relativize(absolute))
|
||||
provider.createDirectory(child = child.resolve(name));
|
||||
return dir;
|
||||
}
|
||||
|
||||
public static Path createFile (Path path, FileAttribute<?>... attrs) throws IOException {
|
||||
try {
|
||||
newByteChannel(path, EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW), attrs).close();
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
if (attrs.length != 0)
|
||||
throw e;
|
||||
newOutputStream(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW).close();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private static Path makeTempPath (Path dir, String prefix, String suffix) {
|
||||
long n = TEMP_RAND.nextLong();
|
||||
if (n == Long.MIN_VALUE)
|
||||
n = 0;
|
||||
else if (n < 0)
|
||||
n *= -1;
|
||||
final Path name = dir.getFileSystem().getPath(prefix + Long.toString(n) + suffix);
|
||||
if (name.getParent() != null)
|
||||
throw new IllegalArgumentException("Invalid prefix or suffix");
|
||||
return dir.resolve(name);
|
||||
}
|
||||
|
||||
public static Path createTempFile (Path dir, String prefix, String suffix, FileAttribute<?>... attrs) throws IOException {
|
||||
if (prefix == null)
|
||||
prefix = "";
|
||||
if (suffix == null)
|
||||
suffix = ".tmp";
|
||||
for (;;) {
|
||||
try {
|
||||
return createFile(makeTempPath(dir, prefix, suffix), attrs);
|
||||
}
|
||||
catch (FileAlreadyExistsException e) {
|
||||
// retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Path createTempFile (String prefix, String suffix, FileAttribute<?>... attrs) throws IOException {
|
||||
final FileSystem fs = FileSystems.getDefault();
|
||||
if (!(fs instanceof DefaultFileSystem))
|
||||
throw new UnsupportedOperationException();
|
||||
return createTempFile(((DefaultFileSystem)fs).getCacheDirectory(), prefix, suffix, attrs);
|
||||
}
|
||||
|
||||
public static Path createTempDirectory (Path dir, String prefix, FileAttribute<?>... attrs) throws IOException {
|
||||
if (prefix == null)
|
||||
prefix = "";
|
||||
for (;;) {
|
||||
try {
|
||||
return Files.createDirectory(makeTempPath(dir, prefix, ""), attrs);
|
||||
}
|
||||
catch (FileAlreadyExistsException e) {
|
||||
// retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Path createTempDirectory (String prefix, FileAttribute<?>... attrs) throws IOException {
|
||||
final FileSystem fs = FileSystems.getDefault();
|
||||
if (!(fs instanceof DefaultFileSystem))
|
||||
throw new UnsupportedOperationException();
|
||||
return createTempDirectory(((DefaultFileSystem)fs).getCacheDirectory(), prefix, attrs);
|
||||
|
||||
}
|
||||
|
||||
public static byte[] readAllBytes (Path path) throws IOException {
|
||||
final InputStream in = newInputStream(path);
|
||||
try {
|
||||
final long size = size(path);
|
||||
if (size > Integer.MAX_VALUE)
|
||||
throw new OutOfMemoryError("Array size exceeded");
|
||||
return Utils.readAllBytes(in, (int)size);
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static Path write (Path path, byte[] bytes, OpenOption... options) throws IOException {
|
||||
final OutputStream out = newOutputStream(path, options);
|
||||
try {
|
||||
out.write(bytes);
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not delete target upon failure.
|
||||
*/
|
||||
public static long copy (InputStream in, Path target, CopyOption... options) throws IOException {
|
||||
if (in == null)
|
||||
throw new NullPointerException("in");
|
||||
for (final CopyOption option : options) {
|
||||
if (StandardCopyOption.REPLACE_EXISTING == option) {
|
||||
deleteIfExists(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
final OutputStream out = newOutputStream(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
|
||||
try {
|
||||
return Utils.transfer(in, out);
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static long copy (Path source, OutputStream out) throws IOException {
|
||||
final InputStream in = newInputStream(source);
|
||||
try {
|
||||
return Utils.transfer(in, out);
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static Path copy (Path source, Path target, CopyOption... options) throws IOException {
|
||||
return transfer(source, target, false, options);
|
||||
}
|
||||
|
||||
public static Path move (Path source, Path target, CopyOption... options) throws IOException {
|
||||
return transfer(source, target, true, options);
|
||||
}
|
||||
|
||||
private static Path transfer (Path source, Path target, boolean move, CopyOption[] options) throws IOException {
|
||||
final FileSystemProvider provider = provider(source);
|
||||
if (provider == provider(target)) {
|
||||
if (move)
|
||||
provider.move(source, target, options);
|
||||
else
|
||||
provider.copy(source, target, options);
|
||||
return target;
|
||||
}
|
||||
boolean replaceExisting = false;
|
||||
boolean copyAttributes = false;
|
||||
LinkOption[] linkOptions = Utils.EMPTY_LINK_OPTION_ARRAY;
|
||||
for (final CopyOption option : options) {
|
||||
if (StandardCopyOption.REPLACE_EXISTING == option)
|
||||
replaceExisting = true;
|
||||
else if (StandardCopyOption.COPY_ATTRIBUTES == option)
|
||||
copyAttributes = true;
|
||||
else if (LinkOption.NOFOLLOW_LINKS == option)
|
||||
linkOptions = LINK_NOFOLLOW_LINKS;
|
||||
else if (StandardCopyOption.ATOMIC_MOVE == option && move)
|
||||
throw new AtomicMoveNotSupportedException(source.toString(), target.toString(), "Different providers");
|
||||
}
|
||||
if (replaceExisting)
|
||||
deleteIfExists(target);
|
||||
if (isDirectory(source))
|
||||
createDirectory(target);
|
||||
else {
|
||||
final InputStream in = newInputStream(source);
|
||||
try {
|
||||
final OutputStream out = newOutputStream(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
|
||||
try {
|
||||
Utils.transfer(in, out);
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (copyAttributes) {
|
||||
for (final Map.Entry<String, Object> attr : readAttributes(source, COPIED_ATTRIBUTES, linkOptions).entrySet()) {
|
||||
try {
|
||||
setAttribute(target, attr.getKey(), attr.getValue());
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// suppressed
|
||||
}
|
||||
}
|
||||
}
|
||||
if (move)
|
||||
delete(source);
|
||||
}
|
||||
catch (IOException e) {
|
||||
deleteQuietly(target);
|
||||
throw e;
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
deleteQuietly(target);
|
||||
throw e;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
public static SeekableByteChannel newByteChannel(Path path, OpenOption... options) throws IOException {
|
||||
return provider(path).newByteChannel(path, new SearchSet<OpenOption>(options));
|
||||
}
|
||||
|
||||
public static SeekableByteChannel newByteChannel(Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
|
||||
return provider(path).newByteChannel(path, options, attrs);
|
||||
}
|
||||
|
||||
public static InputStream newInputStream (Path path, OpenOption...options) throws IOException {
|
||||
return provider(path).newInputStream(path);
|
||||
}
|
||||
|
||||
public static OutputStream newOutputStream (Path path, OpenOption...options) throws IOException {
|
||||
return provider(path).newOutputStream(path, options);
|
||||
}
|
||||
|
||||
public static BufferedReader newBufferedReader (Path path) throws IOException {
|
||||
return newBufferedReader(path, Utils.UTF_8);
|
||||
}
|
||||
|
||||
public static BufferedReader newBufferedReader (Path path, Charset charset) throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(newInputStream(path), charset));
|
||||
}
|
||||
|
||||
public static BufferedWriter newBufferedWriter (Path path, OpenOption... options) throws IOException {
|
||||
return newBufferedWriter(path, Utils.UTF_8, options);
|
||||
}
|
||||
|
||||
public static BufferedWriter newBufferedWriter (Path path, Charset charset, OpenOption... options) throws IOException {
|
||||
return new BufferedWriter(new OutputStreamWriter(newOutputStream(path, options), charset));
|
||||
}
|
||||
|
||||
public static boolean exists (Path path, LinkOption... options) {
|
||||
try {
|
||||
readAttributes(path, BasicFileAttributes.class, options);
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean notExists (Path path, LinkOption... options) {
|
||||
try {
|
||||
readAttributes(path, BasicFileAttributes.class, options);
|
||||
return false;
|
||||
}
|
||||
catch (NoSuchFileException e) {
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSameFile (Path path1, Path path2) throws IOException {
|
||||
final FileSystem fs = path1.getFileSystem();
|
||||
return fs.equals(path2.getFileSystem())
|
||||
&& fs.provider().isSameFile(path1, path2);
|
||||
}
|
||||
|
||||
public static boolean isHidden (Path path) throws IOException {
|
||||
return provider(path).isHidden(path);
|
||||
}
|
||||
|
||||
public static boolean isDirectory (Path path, LinkOption... options) {
|
||||
try {
|
||||
return readAttributes(path, BasicFileAttributes.class, options).isDirectory();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isRegularFile (Path path, LinkOption... options) {
|
||||
try {
|
||||
return readAttributes(path, BasicFileAttributes.class, options).isRegularFile();
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isSymbolicLink (Path path) {
|
||||
try {
|
||||
readSymbolicLink(path);
|
||||
return true;
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static long size (Path path) throws IOException {
|
||||
return readAttributes(path, BasicFileAttributes.class).size();
|
||||
}
|
||||
|
||||
public static FileTime getLastModifiedTime (Path path, LinkOption... options) throws IOException {
|
||||
return readAttributes(path, BasicFileAttributes.class, options).lastModifiedTime();
|
||||
}
|
||||
|
||||
public static Path setLastModifiedTime (Path path, FileTime time) throws IOException {
|
||||
return setAttribute(path, BasicFileAttribute.lastModifiedTime.toString(), time);
|
||||
}
|
||||
|
||||
public static Path readSymbolicLink (Path link) throws IOException {
|
||||
return provider(link).readSymbolicLink(link);
|
||||
}
|
||||
|
||||
public static <A extends BasicFileAttributes> A readAttributes (Path path, Class<A> type, LinkOption... options) throws IOException {
|
||||
return provider(path).readAttributes(path, type, options);
|
||||
}
|
||||
|
||||
public static Map<String,Object> readAttributes (Path path, String attributes, LinkOption... options) throws IOException {
|
||||
return provider(path).readAttributes(path, attributes, options);
|
||||
}
|
||||
|
||||
public static Path setAttribute (Path path, String attribute, Object value, LinkOption... options) throws IOException {
|
||||
provider(path).setAttribute(path, attribute, value, options);
|
||||
return path;
|
||||
}
|
||||
|
||||
public static <V extends FileAttributeView> V getFileAttributeView (Path path, Class<V> type, LinkOption... options) {
|
||||
return provider(path).getFileAttributeView(path, type, options);
|
||||
}
|
||||
|
||||
public static String probeContentType (Path path) throws IOException {
|
||||
for (final FileTypeDetector detector : InstalledFileTypeDetectorsHolder.detectors) {
|
||||
final String type = detector.probeContentType(path);
|
||||
if (type != null)
|
||||
return type;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DirectoryStream<Path> newDirectoryStream (Path dir) throws IOException{
|
||||
return newDirectoryStream(dir, Utils.ACCEPT_ALL_FILTER);
|
||||
}
|
||||
|
||||
public static DirectoryStream<Path> newDirectoryStream (Path dir, String glob) throws IOException {
|
||||
if ("*".equals(glob))
|
||||
return newDirectoryStream(dir);
|
||||
final PathMatcher matcher = dir.getFileSystem().getPathMatcher("glob:"+glob);
|
||||
//noinspection RedundantThrows
|
||||
return newDirectoryStream(dir, new DirectoryStream.Filter<Path>() {
|
||||
@Override
|
||||
public boolean accept (Path entry) throws IOException {
|
||||
return matcher.matches(entry.getFileName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static DirectoryStream<Path> newDirectoryStream (Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException{
|
||||
return provider(dir).newDirectoryStream(dir, filter);
|
||||
}
|
||||
|
||||
public static Path walkFileTree (Path start, FileVisitor<? super Path> visitor) throws IOException {
|
||||
return walkFileTree(start, VISIT_EMPTY, Integer.MAX_VALUE, visitor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
public static Path walkFileTree (Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<? super Path> visitor) throws IOException {
|
||||
final boolean followLinks = options.contains(FileVisitOption.FOLLOW_LINKS);
|
||||
final LinkOption[] linkOptions = followLinks ? Utils.EMPTY_LINK_OPTION_ARRAY : LINK_NOFOLLOW_LINKS;
|
||||
WalkDirectory dir = null;
|
||||
try {
|
||||
Path path = start;
|
||||
FileVisitResult result;
|
||||
int depth = 0;
|
||||
walk: do {
|
||||
|
||||
// iterate
|
||||
while (dir != null) {
|
||||
IOException cause = null;
|
||||
try {
|
||||
if (dir.iterator.hasNext()) {
|
||||
path = dir.iterator.next();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (DirectoryIteratorException e) {
|
||||
cause = e.getCause();
|
||||
}
|
||||
if (FileVisitResult.TERMINATE == visitor.postVisitDirectory(dir.path, cause) || --depth <= 0)
|
||||
break walk;
|
||||
Utils.closeQuietly(dir.stream);
|
||||
dir = dir.parent;
|
||||
}
|
||||
|
||||
// descend
|
||||
DirectoryStream<Path> stream = null;
|
||||
BasicFileAttributes attrs = null;
|
||||
try {
|
||||
attrs = readAttributes(path, BasicFileAttributes.class, linkOptions);
|
||||
if (attrs.isDirectory() && depth < maxDepth) {
|
||||
if (followLinks) {
|
||||
// check for recursion
|
||||
for (WalkDirectory ancestor = dir; ancestor != null; ancestor = ancestor.parent) {
|
||||
if (ancestor.isSameFile(path, attrs.fileKey()))
|
||||
throw new FileSystemLoopException(path.toString());
|
||||
}
|
||||
}
|
||||
stream = Files.newDirectoryStream(path);
|
||||
}
|
||||
}
|
||||
catch (NotDirectoryException e) {
|
||||
// visitFile below
|
||||
}
|
||||
catch (IOException e) {
|
||||
result = visitor.visitFileFailed(path, e);
|
||||
if (FileVisitResult.SKIP_SIBLINGS == result && dir != null)
|
||||
dir.iterator = Utils.emptyIterator();
|
||||
continue;
|
||||
}
|
||||
if (stream != null) {
|
||||
result = visitor.preVisitDirectory(path, attrs);
|
||||
if (FileVisitResult.CONTINUE == result) {
|
||||
dir = new WalkDirectory(dir, path, attrs.fileKey(), stream);
|
||||
++depth;
|
||||
}
|
||||
else
|
||||
Utils.closeQuietly(stream);
|
||||
}
|
||||
else {
|
||||
result = visitor.visitFile(path, attrs);
|
||||
if (FileVisitResult.SKIP_SIBLINGS == result && dir != null)
|
||||
dir.iterator = Utils.emptyIterator();
|
||||
}
|
||||
} while (FileVisitResult.TERMINATE != result && depth > 0);
|
||||
}
|
||||
finally {
|
||||
for (; dir != null; dir = dir.parent)
|
||||
Utils.closeQuietly(dir.stream);
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
public static File[] walk(@Nullable Path path) {
|
||||
throw new NotImplementedException("我懒得写");
|
||||
}
|
||||
|
||||
|
||||
private static final class WalkDirectory {
|
||||
|
||||
public final WalkDirectory parent;
|
||||
public final Path path;
|
||||
public final Object key;
|
||||
public final DirectoryStream<Path> stream;
|
||||
public Iterator<Path> iterator;
|
||||
|
||||
public WalkDirectory (WalkDirectory parent, Path path, Object key, DirectoryStream<Path> stream) {
|
||||
this.parent = parent;
|
||||
this.path = path;
|
||||
this.key = key;
|
||||
this.stream = stream;
|
||||
this.iterator = stream.iterator();
|
||||
}
|
||||
|
||||
public boolean isSameFile (Path path, Object key) {
|
||||
if (key != null && this.key != null)
|
||||
return key.equals(this.key);
|
||||
try {
|
||||
return Files.isSameFile(path, this.path);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // class WalkDirectory
|
||||
|
||||
|
||||
private static FileSystemProvider provider (Path path) {
|
||||
return path.getFileSystem().provider();
|
||||
}
|
||||
|
||||
|
||||
private static final class InstalledFileTypeDetectorsHolder {
|
||||
|
||||
private static final List<FileTypeDetector> detectors = loadInstalledProviders();
|
||||
|
||||
private static List<FileTypeDetector> loadInstalledProviders () {
|
||||
final List<FileTypeDetector> detectors = new ArrayList<FileTypeDetector>();
|
||||
for (final FileTypeDetector detector : ServiceLoader.load(FileTypeDetector.class, FileTypeDetector.class.getClassLoader()))
|
||||
detectors.add(detector);
|
||||
//noinspection RedundantThrows
|
||||
detectors.add(new FileTypeDetector() {
|
||||
private final boolean apacheHarmony = isApacheHarmony();
|
||||
@Override
|
||||
public String probeContentType (Path path) throws IOException {
|
||||
// TODO: Use URLConnection.guessContentTypeFromStream()
|
||||
String filename = path.toString();
|
||||
// BUG: https://code.google.com/p/android/issues/detail?id=162883
|
||||
// https://android.googlesource.com/platform/libcore/+/marshmallow-release/luni/src/main/java/java/net/DefaultFileNameMap.java
|
||||
if (apacheHarmony)
|
||||
filename = filename.replace('#', '_');
|
||||
return URLConnection.guessContentTypeFromName(filename);
|
||||
}
|
||||
});
|
||||
return detectors;
|
||||
}
|
||||
|
||||
private static boolean isApacheHarmony () {
|
||||
try {
|
||||
Class.forName("libcore.net.MimeUtils", false, InstalledFileTypeDetectorsHolder.class.getClassLoader());
|
||||
return true;
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // class InstalledFileTypeDetectorsHolder
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class InvalidPathException extends IllegalArgumentException {
|
||||
|
||||
private final String input;
|
||||
private final int index;
|
||||
|
||||
public InvalidPathException (String input, String reason) {
|
||||
this(input, reason, -1);
|
||||
}
|
||||
|
||||
public InvalidPathException (String input, String reason, int index) {
|
||||
super(reason);
|
||||
this.input = input;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
public String getInput () {
|
||||
return input;
|
||||
}
|
||||
|
||||
public String getReason () {
|
||||
return super.getMessage();
|
||||
}
|
||||
|
||||
public int getIndex () {
|
||||
return index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage () {
|
||||
final StringBuilder sb = new StringBuilder().append(getReason());
|
||||
if (index >= 0)
|
||||
sb.append(" at index ").append(index);
|
||||
return sb.append(": ").append(input).toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public enum LinkOption implements OpenOption, CopyOption {
|
||||
/** Do not follow symbolic links. */
|
||||
NOFOLLOW_LINKS,
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class NoSuchFileException extends FileSystemException {
|
||||
|
||||
public NoSuchFileException (String file) {
|
||||
super(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class NotDirectoryException extends FileSystemException {
|
||||
|
||||
public NotDirectoryException (String file) {
|
||||
super(file);
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class NotLinkException extends FileSystemException {
|
||||
|
||||
public NotLinkException (String file) {
|
||||
super(file);
|
||||
}
|
||||
|
||||
public NotLinkException (String file, String otherFile, String reason) {
|
||||
super(file, otherFile, reason);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public interface OpenOption {}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
|
||||
public interface Path extends Comparable<Path>, Iterable<Path>, Watchable {
|
||||
public FileSystem getFileSystem ();
|
||||
|
||||
public boolean isAbsolute ();
|
||||
public Path getRoot ();
|
||||
public Path getParent ();
|
||||
public Path getFileName ();
|
||||
public int getNameCount ();
|
||||
public Path getName (int index);
|
||||
public Path subpath (int beginIndex, int endIndex);
|
||||
public boolean startsWith (Path other);
|
||||
public boolean startsWith (String other);
|
||||
public boolean endsWith (Path other);
|
||||
public boolean endsWith (String other);
|
||||
|
||||
public Path normalize ();
|
||||
public Path relativize (Path other);
|
||||
public Path resolve (Path other);
|
||||
public Path resolve (String other);
|
||||
public Path resolveSibling (Path other);
|
||||
public Path resolveSibling (String other);
|
||||
|
||||
public Path toAbsolutePath ();
|
||||
public Path toRealPath (LinkOption... options) throws IOException;
|
||||
public File toFile ();
|
||||
public URI toUri ();
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public interface PathMatcher {
|
||||
public boolean matches (Path path);
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
|
||||
public final class Paths {
|
||||
|
||||
private Paths () {}
|
||||
|
||||
public static Path get (String first, String... more) {
|
||||
return FileSystems.getDefault().getPath(first, more);
|
||||
}
|
||||
|
||||
public static Path get (URI uri) {
|
||||
final String scheme = uri.getScheme();
|
||||
if (scheme == null)
|
||||
throw new IllegalArgumentException("No scheme");
|
||||
for (final FileSystemProvider provider: FileSystemProvider.installedProviders()) {
|
||||
if (scheme.equalsIgnoreCase(provider.getScheme()))
|
||||
return provider.getPath(uri);
|
||||
}
|
||||
throw new FileSystemNotFoundException(scheme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Non-standard. Since we can't add toPath to {@link java.io.File}.
|
||||
*/
|
||||
public static Path get (File file) {
|
||||
return get(file.toString());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ProviderMismatchException extends IllegalArgumentException {
|
||||
|
||||
public ProviderMismatchException () {}
|
||||
|
||||
public ProviderMismatchException (String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ProviderNotFoundException extends RuntimeException {
|
||||
|
||||
public ProviderNotFoundException () {}
|
||||
|
||||
public ProviderNotFoundException (String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ReadOnlyFileSystemException extends UnsupportedOperationException {
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SimpleFileVisitor<T> implements FileVisitor<T> {
|
||||
|
||||
@Override
|
||||
public FileVisitResult preVisitDirectory (T dir, BasicFileAttributes attrs) throws IOException {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory (T dir, IOException e) throws IOException {
|
||||
if (e != null)
|
||||
throw e;
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile (T file, BasicFileAttributes attrs) throws IOException {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed (T file, IOException e) throws IOException {
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public enum StandardCopyOption implements CopyOption {
|
||||
REPLACE_EXISTING,
|
||||
COPY_ATTRIBUTES,
|
||||
ATOMIC_MOVE,
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public enum StandardOpenOption implements OpenOption {
|
||||
READ,
|
||||
WRITE,
|
||||
APPEND,
|
||||
TRUNCATE_EXISTING,
|
||||
CREATE,
|
||||
CREATE_NEW,
|
||||
/** Unsupported. */
|
||||
DELETE_ON_CLOSE,
|
||||
SPARSE,
|
||||
SYNC,
|
||||
DSYNC,
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import com.llamalab.safs.internal.WatchEventKind;
|
||||
|
||||
public final class StandardWatchEventKinds {
|
||||
|
||||
public static final WatchEvent.Kind<Object> OVERFLOW = new WatchEventKind<Object>("OVERFLOW", Object.class);
|
||||
public static final WatchEvent.Kind<Path> ENTRY_CREATE = new WatchEventKind<Path>("ENTRY_CREATE", Path.class);
|
||||
public static final WatchEvent.Kind<Path> ENTRY_DELETE = new WatchEventKind<Path>("ENTRY_DELETE", Path.class);
|
||||
public static final WatchEvent.Kind<Path> ENTRY_MODIFY = new WatchEventKind<Path>("ENTRY_MODIFY", Path.class);
|
||||
|
||||
private StandardWatchEventKinds() {}
|
||||
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
public interface WatchEvent<T> {
|
||||
|
||||
public static interface Kind<T> {
|
||||
public String name ();
|
||||
public Class<T> type ();
|
||||
}
|
||||
|
||||
public static interface Modifier {
|
||||
public String name ();
|
||||
}
|
||||
|
||||
public Kind<T> kind ();
|
||||
public T context ();
|
||||
public int count ();
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface WatchKey {
|
||||
public Watchable watchable ();
|
||||
public boolean isValid ();
|
||||
public boolean reset ();
|
||||
public void cancel ();
|
||||
public List<WatchEvent<?>> pollEvents ();
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public interface WatchService extends Closeable {
|
||||
public WatchKey poll ();
|
||||
public WatchKey poll (long timeout, TimeUnit unit) throws InterruptedException;
|
||||
public WatchKey take () throws InterruptedException;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface Watchable {
|
||||
public WatchKey register (WatchService service, WatchEvent.Kind<?>[] kinds, WatchEvent.Modifier... modifiers) throws IOException;
|
||||
public WatchKey register (WatchService service, WatchEvent.Kind<?>... kinds) throws IOException;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
public interface AttributeView {
|
||||
public String name ();
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface BasicFileAttributeView extends FileAttributeView {
|
||||
public BasicFileAttributes readAttributes () throws IOException;
|
||||
public void setTimes (FileTime lastModifiedTime, FileTime lastAccessTime, FileTime createTime) throws IOException;
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
public interface BasicFileAttributes {
|
||||
public Object fileKey ();
|
||||
public boolean isDirectory ();
|
||||
public boolean isOther ();
|
||||
public boolean isRegularFile ();
|
||||
public boolean isSymbolicLink ();
|
||||
public long size ();
|
||||
public FileTime creationTime ();
|
||||
public FileTime lastModifiedTime ();
|
||||
public FileTime lastAccessTime ();
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
public interface FileAttribute<T> {
|
||||
public String name ();
|
||||
public T value ();
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
public interface FileAttributeView extends AttributeView {
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public interface FileOwnerAttributeView extends FileAttributeView {
|
||||
public UserPrincipal getOwner () throws IOException;
|
||||
public void setOwner (UserPrincipal owner) throws IOException;
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import com.llamalab.safs.internal.Utils;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public final class FileTime implements Comparable<FileTime> {
|
||||
|
||||
private static final FileTime ZERO = new FileTime(0);
|
||||
|
||||
private final long value;
|
||||
|
||||
private FileTime (long value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* YYYY-MM-DDThh:mm:ss[.s+]Z
|
||||
*/
|
||||
@Override
|
||||
public String toString () {
|
||||
return Utils.formatRfc3339(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode () {
|
||||
return (int)(value ^ (value >>> 32));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!(obj instanceof FileTime))
|
||||
return false;
|
||||
return value == ((FileTime)obj).value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo (FileTime other) {
|
||||
final long lhs = this.value;
|
||||
final long rhs = other.value;
|
||||
//noinspection UseCompareMethod
|
||||
return (lhs < rhs) ? -1 : (lhs == rhs) ? 0 : 1;
|
||||
}
|
||||
|
||||
public long to (TimeUnit unit) {
|
||||
return unit.convert(value, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public long toMillis () {
|
||||
return to(TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public static FileTime from (long value, TimeUnit unit) {
|
||||
if (unit == null)
|
||||
throw new NullPointerException("unit");
|
||||
return fromMillis(TimeUnit.MILLISECONDS.convert(value, unit));
|
||||
}
|
||||
|
||||
public static FileTime fromMillis (long value) {
|
||||
return (value != 0) ? new FileTime(value) : ZERO;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
public interface GroupPrincipal extends UserPrincipal {
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Set;
|
||||
|
||||
public interface PosixFileAttributeView extends BasicFileAttributeView, FileOwnerAttributeView {
|
||||
public void setGroup (GroupPrincipal group) throws IOException;
|
||||
public void setPermissions (Set<PosixFilePermission> perms) throws IOException;
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface PosixFileAttributes extends BasicFileAttributes {
|
||||
public GroupPrincipal group ();
|
||||
public UserPrincipal owner ();
|
||||
public Set<PosixFilePermission> permissions ();
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
public enum PosixFilePermission {
|
||||
OWNER_READ,
|
||||
OWNER_WRITE,
|
||||
OWNER_EXECUTE,
|
||||
GROUP_READ,
|
||||
GROUP_WRITE,
|
||||
GROUP_EXECUTE,
|
||||
OTHERS_READ,
|
||||
OTHERS_WRITE,
|
||||
OTHERS_EXECUTE
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import com.llamalab.safs.internal.SearchSet;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Set;
|
||||
|
||||
public final class PosixFilePermissions {
|
||||
|
||||
private PosixFilePermissions () {}
|
||||
|
||||
public static String toString (Set<PosixFilePermission> perms) {
|
||||
final StringBuilder sb = new StringBuilder(9);
|
||||
append(sb,
|
||||
perms.contains(PosixFilePermission.OWNER_READ),
|
||||
perms.contains(PosixFilePermission.OWNER_WRITE),
|
||||
perms.contains(PosixFilePermission.OWNER_EXECUTE));
|
||||
append(sb,
|
||||
perms.contains(PosixFilePermission.GROUP_READ),
|
||||
perms.contains(PosixFilePermission.GROUP_WRITE),
|
||||
perms.contains(PosixFilePermission.GROUP_EXECUTE));
|
||||
append(sb,
|
||||
perms.contains(PosixFilePermission.OTHERS_READ),
|
||||
perms.contains(PosixFilePermission.OTHERS_WRITE),
|
||||
perms.contains(PosixFilePermission.OTHERS_EXECUTE));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void append (StringBuilder sb, boolean r, boolean w, boolean x) {
|
||||
sb.append(r ? 'r' : '-').append(w ? 'w' : '-').append(x ? 'x' : '-');
|
||||
}
|
||||
|
||||
public static Set<PosixFilePermission> fromString (String mode) {
|
||||
if (mode.length() != 9)
|
||||
throw new IllegalArgumentException();
|
||||
final Set<PosixFilePermission> set = EnumSet.noneOf(PosixFilePermission.class);
|
||||
add(set, mode, 0, 'r', PosixFilePermission.OWNER_READ);
|
||||
add(set, mode, 1, 'w', PosixFilePermission.OWNER_WRITE);
|
||||
add(set, mode, 2, 'x', PosixFilePermission.OWNER_EXECUTE);
|
||||
|
||||
add(set, mode, 3, 'r', PosixFilePermission.GROUP_READ);
|
||||
add(set, mode, 4, 'w', PosixFilePermission.GROUP_WRITE);
|
||||
add(set, mode, 5, 'x', PosixFilePermission.GROUP_EXECUTE);
|
||||
|
||||
add(set, mode, 6, 'r', PosixFilePermission.OTHERS_READ);
|
||||
add(set, mode, 7, 'w', PosixFilePermission.OTHERS_WRITE);
|
||||
add(set, mode, 8, 'x', PosixFilePermission.OTHERS_EXECUTE);
|
||||
return set;
|
||||
}
|
||||
|
||||
private static void add (Set<PosixFilePermission> set, String mode, int index, char flag, PosixFilePermission perm) {
|
||||
final char c = mode.charAt(index);
|
||||
if (flag == c)
|
||||
set.add(perm);
|
||||
else if ('-' != c)
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
public static FileAttribute<Set<PosixFilePermission>> asFileAttribute (Set<PosixFilePermission> perms) {
|
||||
//noinspection ToArrayCallWithZeroLengthArrayArgument
|
||||
final Set<PosixFilePermission> value = new SearchSet<PosixFilePermission>(perms.toArray(new PosixFilePermission[perms.size()]));
|
||||
return new FileAttribute<Set<PosixFilePermission>>() {
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "posix:permissions";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<PosixFilePermission> value () {
|
||||
return value;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import java.security.Principal;
|
||||
|
||||
public interface UserPrincipal extends Principal {
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class UserPrincipalLookupService {
|
||||
|
||||
protected UserPrincipalLookupService () {}
|
||||
|
||||
public abstract UserPrincipal lookupPrincipalByName (String name) throws IOException;
|
||||
public abstract GroupPrincipal lookupPrincipalByGroupName (String group) throws IOException;
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.attribute;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class UserPrincipalNotFoundException extends IOException {
|
||||
|
||||
private final String name;
|
||||
|
||||
public UserPrincipalNotFoundException (String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName (){
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.channels;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.ByteChannel;
|
||||
|
||||
public interface SeekableByteChannel extends ByteChannel {
|
||||
public long position () throws IOException;
|
||||
public SeekableByteChannel position (long newPosition) throws IOException;
|
||||
public long size () throws IOException;
|
||||
public SeekableByteChannel truncate (long size) throws IOException;
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.DirectoryIteratorException;
|
||||
import com.llamalab.safs.DirectoryStream;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public abstract class AbstractDirectoryStream<T> implements DirectoryStream<T>, Iterator<T> {
|
||||
|
||||
private T next;
|
||||
private boolean started;
|
||||
private boolean closed;
|
||||
|
||||
/**
|
||||
* Make sure to call super.close();
|
||||
*/
|
||||
@Override
|
||||
public final void close () throws IOException {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
implCloseStream();
|
||||
}
|
||||
}
|
||||
|
||||
protected void implCloseStream () throws IOException {}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public final Iterator<T> iterator () {
|
||||
if (started)
|
||||
throw new IllegalStateException();
|
||||
started = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasNext () {
|
||||
if (next != null)
|
||||
return true;
|
||||
if (closed)
|
||||
return false;
|
||||
try {
|
||||
return (next = advance()) != null;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new DirectoryIteratorException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T next () {
|
||||
if (closed || !hasNext())
|
||||
throw new NoSuchElementException();
|
||||
final T result = next;
|
||||
next = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract T advance () throws IOException;
|
||||
}
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.LinkOption;
|
||||
import com.llamalab.safs.NoSuchFileException;
|
||||
import com.llamalab.safs.OpenOption;
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.ProviderMismatchException;
|
||||
import com.llamalab.safs.StandardOpenOption;
|
||||
import com.llamalab.safs.attribute.BasicFileAttributeView;
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
import com.llamalab.safs.attribute.FileAttribute;
|
||||
import com.llamalab.safs.attribute.FileAttributeView;
|
||||
import com.llamalab.safs.attribute.FileTime;
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
import com.llamalab.safs.unix.UnixPath;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Only support {@link UnixPath}.
|
||||
*/
|
||||
public abstract class AbstractFileSystemProvider extends FileSystemProvider {
|
||||
|
||||
protected static final Set<? extends OpenOption> DEFAULT_NEW_INPUT_STREAM_OPTIONS = EnumSet.of(StandardOpenOption.READ);
|
||||
protected static final Set<? extends OpenOption> DEFAULT_NEW_OUTPUT_STREAM_OPTIONS = EnumSet.of(StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
|
||||
protected abstract Class<? extends Path> getPathType ();
|
||||
|
||||
protected void checkPath (Path path) {
|
||||
if (!getPathType().isInstance(path))
|
||||
throw (path == null) ? new NullPointerException() : new ProviderMismatchException();
|
||||
if (path.getFileSystem().provider() != this)
|
||||
throw new ProviderMismatchException();
|
||||
}
|
||||
|
||||
protected void checkUri (URI uri) {
|
||||
if (!getScheme().equalsIgnoreCase(uri.getScheme()))
|
||||
throw new ProviderMismatchException();
|
||||
}
|
||||
|
||||
protected IOException toProperException (IOException ioe, String file, String otherFile) {
|
||||
return (ioe instanceof FileNotFoundException) ? new NoSuchFileException(file) : ioe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> readAttributes (Path path, String attributes, LinkOption... options) throws IOException {
|
||||
final Map<String, Object> map = new HashMap<String, Object>();
|
||||
BasicFileAttributes basic = null;
|
||||
for (final BasicFileAttribute attribute : BasicFileAttribute.parse(attributes)) {
|
||||
if (basic == null)
|
||||
basic = readAttributes(path, BasicFileAttributes.class, options); // read once
|
||||
map.put(attribute.toString(), attribute.valueOf(basic));
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute (Path path, String attribute, Object value, LinkOption... options) throws IOException {
|
||||
String viewName = BasicFileAttribute.VIEW_NAME;
|
||||
final int colon = attribute.indexOf(':');
|
||||
if (colon != -1) {
|
||||
viewName = attribute.substring(0, colon);
|
||||
attribute = attribute.substring(colon + 1);
|
||||
}
|
||||
final FileAttribute<?> attr = newFileAttribute(viewName, attribute, value);
|
||||
setAttributes(path, Collections.singleton(attr), options);
|
||||
}
|
||||
|
||||
protected FileAttribute<?> newFileAttribute (String viewName, String attribute, Object value) {
|
||||
if (BasicFileAttribute.VIEW_NAME.equals(viewName))
|
||||
return BasicFileAttribute.valueOf(attribute).newFileAttribute(value);
|
||||
throw new UnsupportedOperationException("Attribute: "+viewName+":"+attribute);
|
||||
}
|
||||
|
||||
protected abstract void setAttributes (Path path, Set<? extends FileAttribute<?>> attrs, LinkOption... options) throws IOException;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <V extends FileAttributeView> V getFileAttributeView (final Path path, Class<V> type, final LinkOption... options) {
|
||||
checkPath(path);
|
||||
if (BasicFileAttributeView.class == type)
|
||||
return (V)new BasicFileAttributeViewImpl(path, options);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
protected class BasicFileAttributeViewImpl implements BasicFileAttributeView {
|
||||
|
||||
protected final Path path;
|
||||
protected final LinkOption[] options;
|
||||
|
||||
public BasicFileAttributeViewImpl (Path path, LinkOption[] options) {
|
||||
this.path = path;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name () {
|
||||
return BasicFileAttribute.VIEW_NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicFileAttributes readAttributes () throws IOException {
|
||||
return AbstractFileSystemProvider.this.readAttributes(path, BasicFileAttributes.class, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTimes (FileTime lastModifiedTime, FileTime lastAccessTime, FileTime creationTime) throws IOException {
|
||||
final Set<FileAttribute<?>> attrs = new HashSet<FileAttribute<?>>();
|
||||
if (lastModifiedTime != null)
|
||||
attrs.add(BasicFileAttribute.lastModifiedTime.newFileAttribute(lastModifiedTime));
|
||||
if (lastAccessTime != null)
|
||||
attrs.add(BasicFileAttribute.lastAccessTime.newFileAttribute(lastAccessTime));
|
||||
if (creationTime != null)
|
||||
attrs.add(BasicFileAttribute.creationTime.newFileAttribute(creationTime));
|
||||
AbstractFileSystemProvider.this.setAttributes(path, attrs, options);
|
||||
}
|
||||
|
||||
} // class BasicFileAttributeViewImpl
|
||||
|
||||
}
|
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.StandardWatchEventKinds;
|
||||
import com.llamalab.safs.WatchEvent;
|
||||
import com.llamalab.safs.WatchKey;
|
||||
import com.llamalab.safs.Watchable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractWatchKey implements WatchKey {
|
||||
|
||||
private enum State {
|
||||
READY,
|
||||
SIGNALLED,
|
||||
}
|
||||
private final Object eventLock = new Object();
|
||||
private final AbstractWatchService service;
|
||||
private final Watchable watchable;
|
||||
private final int overflowLimit;
|
||||
private List<WatchEvent<?>> events = new ArrayList<WatchEvent<?>>();
|
||||
private State state = State.READY;
|
||||
|
||||
public AbstractWatchKey (AbstractWatchService service, Watchable watchable, int overflowLimit) {
|
||||
this.service = service;
|
||||
this.watchable = watchable;
|
||||
this.overflowLimit = overflowLimit;
|
||||
}
|
||||
|
||||
public final AbstractWatchService service () {
|
||||
return service;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Watchable watchable () {
|
||||
return watchable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid () {
|
||||
return service.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean reset () {
|
||||
synchronized (eventLock) {
|
||||
if (!isValid())
|
||||
return false;
|
||||
if (State.SIGNALLED == state) {
|
||||
if (events.isEmpty())
|
||||
state = State.READY;
|
||||
else
|
||||
service.offer(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<WatchEvent<?>> pollEvents () {
|
||||
synchronized (eventLock) {
|
||||
final List<WatchEvent<?>> result = events;
|
||||
events = new ArrayList<WatchEvent<?>>();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
protected final <T> void signalEvent (WatchEvent.Kind<T> kind, T context) {
|
||||
synchronized (eventLock) {
|
||||
final int size = events.size();
|
||||
if (size > 0) {
|
||||
final Event<?> event = (Event<?>)events.get(size - 1);
|
||||
if ( StandardWatchEventKinds.OVERFLOW == event.kind
|
||||
|| (event.kind == kind && Utils.equals(event.context, context))) {
|
||||
++event.count;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (size < overflowLimit)
|
||||
events.add(new Event<T>(kind, context));
|
||||
else
|
||||
events.add(new Event<Object>(StandardWatchEventKinds.OVERFLOW, null));
|
||||
if (State.READY == state) {
|
||||
state = State.SIGNALLED;
|
||||
service.offer(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Event<T> implements WatchEvent<T> {
|
||||
|
||||
private final WatchEvent.Kind<T> kind;
|
||||
private final T context;
|
||||
private int count = 1;
|
||||
|
||||
public Event (WatchEvent.Kind<T> kind, T context) {
|
||||
this.kind = kind;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Kind<T> kind () {
|
||||
return kind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T context () {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count () {
|
||||
return count;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return super.toString()+"[kind="+kind+", context="+context+", count="+count+"]";
|
||||
}
|
||||
|
||||
} // class Event
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.ClosedWatchServiceException;
|
||||
import com.llamalab.safs.WatchEvent;
|
||||
import com.llamalab.safs.WatchKey;
|
||||
import com.llamalab.safs.WatchService;
|
||||
import com.llamalab.safs.Watchable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public abstract class AbstractWatchService implements WatchService {
|
||||
|
||||
private final WatchKey CLOSE_KEY = new WatchKey() {
|
||||
|
||||
@Override
|
||||
public Watchable watchable () {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid () {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean reset () {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel () {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<WatchEvent<?>> pollEvents () {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
private final LinkedBlockingDeque<WatchKey> pendingKeys = new LinkedBlockingDeque<WatchKey>();
|
||||
private final AtomicBoolean closed = new AtomicBoolean();
|
||||
|
||||
final void offer (WatchKey key) {
|
||||
pendingKeys.offer(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey poll () {
|
||||
checkOpen();
|
||||
return checkKey(pendingKeys.poll());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey poll (long timeout, TimeUnit unit) throws InterruptedException {
|
||||
checkOpen();
|
||||
return checkKey(pendingKeys.poll(timeout, unit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey take () throws InterruptedException {
|
||||
checkOpen();
|
||||
return checkKey(pendingKeys.take());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close () throws IOException {
|
||||
if (closed.compareAndSet(false, true)) {
|
||||
try {
|
||||
implCloseService();
|
||||
}
|
||||
finally {
|
||||
pendingKeys.clear();
|
||||
pendingKeys.offer(CLOSE_KEY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void implCloseService () throws IOException;
|
||||
|
||||
public final boolean isOpen () {
|
||||
return !closed.get();
|
||||
}
|
||||
|
||||
private void checkOpen () {
|
||||
if (closed.get())
|
||||
throw new ClosedWatchServiceException();
|
||||
}
|
||||
|
||||
private WatchKey checkKey (WatchKey key) {
|
||||
if (CLOSE_KEY == key)
|
||||
pendingKeys.offer(key);
|
||||
checkOpen();
|
||||
return key;
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
|
||||
final class AttributeParser<E extends Enum<E>> implements Iterable<E> {
|
||||
|
||||
private final Class<E> enumType;
|
||||
private final String attributes;
|
||||
private final String viewName;
|
||||
private final boolean isDefault;
|
||||
|
||||
public AttributeParser (Class<E> enumType, String attributes, String viewName, boolean isDefault) {
|
||||
this.enumType = enumType;
|
||||
this.attributes = attributes;
|
||||
this.viewName = viewName;
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public Iterator<E> iterator () {
|
||||
final int length = attributes.length();
|
||||
int start = 0, index = 0;
|
||||
boolean prefixed = false;
|
||||
loop: while (index < length) {
|
||||
switch (attributes.charAt(index)) {
|
||||
case ':':
|
||||
if (prefixed)
|
||||
throw new IllegalArgumentException();
|
||||
if (index != viewName.length() || !attributes.startsWith(viewName))
|
||||
return Utils.emptyIterator();
|
||||
start = ++index;
|
||||
prefixed = true;
|
||||
break;
|
||||
case '*':
|
||||
if (index != start || ++index != length)
|
||||
throw new IllegalArgumentException();
|
||||
if (!prefixed && !isDefault)
|
||||
return Utils.emptyIterator();
|
||||
return EnumSet.allOf(enumType).iterator();
|
||||
case ',':
|
||||
break loop;
|
||||
default:
|
||||
++index;
|
||||
}
|
||||
}
|
||||
if (!prefixed && !isDefault)
|
||||
return Utils.emptyIterator();
|
||||
return new CommaSeparatedIterator(start, index);
|
||||
}
|
||||
|
||||
private final class CommaSeparatedIterator implements Iterator<E> {
|
||||
|
||||
private int start;
|
||||
private int index;
|
||||
|
||||
public CommaSeparatedIterator (int start, int index) {
|
||||
this.start = start;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext () {
|
||||
return start < index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public E next () {
|
||||
final E element = Enum.valueOf(enumType, attributes.substring(start, index));
|
||||
index = attributes.indexOf(',', start = index + 1);
|
||||
if (index == -1)
|
||||
index = attributes.length();
|
||||
return element;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
} // class CommaSeparatedIterator
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
import com.llamalab.safs.attribute.FileAttribute;
|
||||
|
||||
public enum BasicFileAttribute {
|
||||
fileKey {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.fileKey();
|
||||
}
|
||||
},
|
||||
isDirectory {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.isDirectory();
|
||||
}
|
||||
},
|
||||
isRegularFile {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.isRegularFile();
|
||||
}
|
||||
},
|
||||
isSymbolicLink {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.isSymbolicLink();
|
||||
}
|
||||
},
|
||||
isOther {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.isOther();
|
||||
}
|
||||
},
|
||||
size {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.size();
|
||||
}
|
||||
},
|
||||
creationTime {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.creationTime();
|
||||
}
|
||||
},
|
||||
lastModifiedTime {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.lastModifiedTime();
|
||||
}
|
||||
},
|
||||
lastAccessTime {
|
||||
@Override
|
||||
public Object valueOf (BasicFileAttributes attrs) {
|
||||
return attrs.lastAccessTime();
|
||||
}
|
||||
};
|
||||
|
||||
public static final String VIEW_NAME = "basic";
|
||||
|
||||
public abstract Object valueOf (BasicFileAttributes attrs);
|
||||
|
||||
public FileAttribute newFileAttribute (Object value) {
|
||||
return new BasicFileAttributeValue(this, value);
|
||||
}
|
||||
|
||||
public static Iterable<BasicFileAttribute> parse (String attributes) {
|
||||
return new AttributeParser<BasicFileAttribute>(BasicFileAttribute.class, attributes, VIEW_NAME, true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.attribute.FileAttribute;
|
||||
|
||||
public final class BasicFileAttributeValue implements FileAttribute<Object> {
|
||||
|
||||
private final BasicFileAttribute type;
|
||||
private final Object value;
|
||||
|
||||
public BasicFileAttributeValue (BasicFileAttribute type, Object value) {
|
||||
if (type == null || value == null)
|
||||
throw new NullPointerException();
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name () {
|
||||
return BasicFileAttribute.VIEW_NAME + ":" + type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object value () {
|
||||
return value;
|
||||
}
|
||||
|
||||
public BasicFileAttribute type () {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode () {
|
||||
return type.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object other) {
|
||||
return other instanceof BasicFileAttributeValue
|
||||
&& type == ((BasicFileAttributeValue)other).type;
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.attribute.FileTime;
|
||||
|
||||
public class CompleteBasicFileAttributes extends PartialBasicFileAttributes {
|
||||
|
||||
private final Object fileKey;
|
||||
private final FileTime creationTime;
|
||||
private final FileTime lastModifiedTime;
|
||||
private final FileTime lastAccessTime;
|
||||
|
||||
public CompleteBasicFileAttributes (Object fileKey, FileType type, long size, FileTime creationTime, FileTime lastModifiedTime, FileTime lastAccessTime) {
|
||||
super(type, size);
|
||||
this.fileKey = fileKey;
|
||||
this.creationTime = creationTime;
|
||||
this.lastModifiedTime = lastModifiedTime;
|
||||
this.lastAccessTime = lastAccessTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object fileKey () {
|
||||
return fileKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FileTime creationTime () {
|
||||
return creationTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FileTime lastModifiedTime () {
|
||||
return lastModifiedTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FileTime lastAccessTime () {
|
||||
return lastAccessTime;
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.Path;
|
||||
|
||||
public interface DefaultFileSystem {
|
||||
public Path getCacheDirectory ();
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
public enum FileType {
|
||||
DIRECTORY,
|
||||
REGULAR_FILE,
|
||||
SYMBOLIC_LINK,
|
||||
OTHER,
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
|
||||
public abstract class PartialBasicFileAttributes implements BasicFileAttributes {
|
||||
|
||||
private final FileType type;
|
||||
private final long size;
|
||||
|
||||
protected PartialBasicFileAttributes (FileType type, long size) {
|
||||
this.type = type;
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isDirectory () {
|
||||
return FileType.DIRECTORY == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isRegularFile () {
|
||||
return FileType.REGULAR_FILE == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isSymbolicLink () {
|
||||
return FileType.SYMBOLIC_LINK == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOther () {
|
||||
return FileType.OTHER == type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final long size () {
|
||||
return size;
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
public class PathDescender<T extends SegmentEntry> implements Iterator<PathDescender.Event> {
|
||||
|
||||
public enum Event {
|
||||
DIRECTORY,
|
||||
FILE,
|
||||
MISSING_DIRECTORY,
|
||||
MISSING_FILE
|
||||
}
|
||||
|
||||
private final Iterator<String> segments;
|
||||
private SegmentEntry parent;
|
||||
private String segment;
|
||||
private int index;
|
||||
|
||||
public PathDescender (T root, Iterator<String> segments) {
|
||||
this.parent = root;
|
||||
this.segments = segments;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext () {
|
||||
return index >= 0 && segments.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event next () {
|
||||
if (segment != null)
|
||||
parent = parent.children[index];
|
||||
if ((index = parent.binarySearch(segment = segments.next())) >= 0)
|
||||
return segments.hasNext() ? Event.DIRECTORY : Event.FILE;
|
||||
else
|
||||
return segments.hasNext() ? Event.MISSING_DIRECTORY : Event.MISSING_FILE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove () {
|
||||
parent.remove(index);
|
||||
index = -1;
|
||||
}
|
||||
|
||||
public void set (T entry) {
|
||||
entry.segment = segment;
|
||||
index = parent.put(index, entry);
|
||||
}
|
||||
|
||||
public String segment () {
|
||||
return segment;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T parent () {
|
||||
return (T)parent;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T entry () {
|
||||
return (T)parent.children[index];
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* Values must MUST implement Comparable.
|
||||
*/
|
||||
public class SearchSet<T> extends AbstractSet<T> implements Comparator<Object> {
|
||||
|
||||
private final Object[] elements;
|
||||
|
||||
@SafeVarargs
|
||||
public SearchSet (T... elements) {
|
||||
this.elements = elements;
|
||||
Arrays.sort(elements, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public Iterator<T> iterator () {
|
||||
return new Iterator<T>() {
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public boolean hasNext () {
|
||||
return index < elements.length;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T next () {
|
||||
if (index >= elements.length)
|
||||
throw new NoSuchElementException();
|
||||
return (T)elements[index++];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size () {
|
||||
return elements.length;
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean addAll (Collection<? extends T> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains (Object object) {
|
||||
return Arrays.binarySearch(elements, object, this) >= 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove (Object object) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public boolean retainAll (Collection<?> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeAll (Collection<?> collection) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public int compare (Object lhs, Object rhs) {
|
||||
if (lhs == rhs)
|
||||
return 0;
|
||||
if (lhs == null)
|
||||
return 1;
|
||||
if (rhs == null)
|
||||
return -1;
|
||||
final Class lc = lhs.getClass();
|
||||
final Class rc = rhs.getClass();
|
||||
return (lc != rc) ? lc.getName().compareTo(rc.getName()) : ((Comparable)lhs).compareTo(rhs);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.unix.UnixPath;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class SegmentEntry<T extends SegmentEntry<T>> implements Iterable<T> {
|
||||
|
||||
private static final SegmentEntry[] EMPTY = new SegmentEntry[0];
|
||||
|
||||
String segment;
|
||||
SegmentEntry[] children = EMPTY;
|
||||
int size;
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public Iterator<T> iterator () {
|
||||
return new Iterator<T>() {
|
||||
|
||||
private int index;
|
||||
|
||||
@Override
|
||||
public boolean hasNext () {
|
||||
return index < size;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public T next () {
|
||||
if (index >= size)
|
||||
throw new NoSuchElementException();
|
||||
return (T)children[index++];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove () {
|
||||
if (index == 0)
|
||||
throw new IllegalStateException();
|
||||
SegmentEntry.this.remove(--index);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final boolean isEmpty () {
|
||||
return size == 0;
|
||||
}
|
||||
|
||||
public final void clear () {
|
||||
size = 0;
|
||||
Arrays.fill(children, null);
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "SuspiciousSystemArraycopy" })
|
||||
public T[] toArray (T[] a) {
|
||||
if (size > a.length)
|
||||
a = (T[])Array.newInstance(a.getClass().getComponentType(), size);
|
||||
System.arraycopy(children, 0, a, 0, size);
|
||||
return a;
|
||||
}
|
||||
|
||||
public T getDescendant (UnixPath path) {
|
||||
return getDescendant(path.stringIterator());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public T getDescendant (Iterator<String> segments) {
|
||||
SegmentEntry parent = this;
|
||||
while (segments.hasNext()) {
|
||||
final int index = parent.binarySearch(segments.next());
|
||||
if (index < 0)
|
||||
return null;
|
||||
parent = parent.children[index];
|
||||
}
|
||||
return (T)parent;
|
||||
}
|
||||
|
||||
public final PathDescender<T> descentor (UnixPath path) {
|
||||
return descentor(path.stringIterator());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final PathDescender<T> descentor (Iterator<String> segments) {
|
||||
return new PathDescender<T>((T)this, segments);
|
||||
}
|
||||
|
||||
final int binarySearch (String segment) {
|
||||
final SegmentEntry[] c = children;
|
||||
int low = 0;
|
||||
int high = size - 1;
|
||||
while (low <= high) {
|
||||
final int mid = (low + high) >>> 1;
|
||||
final int cmp = c[mid].segment.compareTo(segment);
|
||||
if (cmp < 0)
|
||||
low = mid + 1;
|
||||
else if (cmp > 0)
|
||||
high = mid - 1;
|
||||
else
|
||||
return mid; // key found
|
||||
}
|
||||
return -(low + 1); // key not found.
|
||||
}
|
||||
|
||||
final int put (int index, SegmentEntry child) {
|
||||
if (index < 0) {
|
||||
index = ~index;
|
||||
final SegmentEntry[] oc = children;
|
||||
final int s = size++;
|
||||
if (s == oc.length) {
|
||||
final SegmentEntry[] nc = new SegmentEntry[1 << (32 - Integer.numberOfLeadingZeros(s))];
|
||||
System.arraycopy(oc, 0, nc, 0, index);
|
||||
System.arraycopy(oc, index, nc, index + 1, s - index);
|
||||
children = nc;
|
||||
nc[index] = child;
|
||||
}
|
||||
else {
|
||||
System.arraycopy(oc, index, oc, index + 1, s - index);
|
||||
oc[index] = child;
|
||||
}
|
||||
}
|
||||
else
|
||||
children[index] = child;
|
||||
return index;
|
||||
}
|
||||
|
||||
final void remove (int index) {
|
||||
if (index < 0 || size <= index)
|
||||
throw new IndexOutOfBoundsException();
|
||||
System.arraycopy(children, index + 1, children, index, --size - index);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.attribute.GroupPrincipal;
|
||||
import com.llamalab.safs.attribute.UserPrincipal;
|
||||
import com.llamalab.safs.attribute.UserPrincipalLookupService;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class UserPrincipalFactory extends UserPrincipalLookupService {
|
||||
|
||||
@Override
|
||||
public UserPrincipal lookupPrincipalByName (String name) throws IOException {
|
||||
return new User(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GroupPrincipal lookupPrincipalByGroupName (String group) throws IOException {
|
||||
return new Group(group);
|
||||
}
|
||||
|
||||
protected static class User implements UserPrincipal {
|
||||
|
||||
private final String name;
|
||||
|
||||
public User (String name) {
|
||||
if (name == null)
|
||||
throw new NullPointerException();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName () {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString () {
|
||||
return super.toString()+"[name="+name+"]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object other) {
|
||||
return other instanceof User && name.equals(((User)other).name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode () {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected static class Group extends User implements GroupPrincipal {
|
||||
|
||||
public Group (String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,455 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.CopyOption;
|
||||
import com.llamalab.safs.DirectoryStream;
|
||||
import com.llamalab.safs.FileVisitOption;
|
||||
import com.llamalab.safs.FileVisitResult;
|
||||
import com.llamalab.safs.FileVisitor;
|
||||
import com.llamalab.safs.Files;
|
||||
import com.llamalab.safs.LinkOption;
|
||||
import com.llamalab.safs.OpenOption;
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.SimpleFileVisitor;
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
import com.llamalab.safs.attribute.FileTime;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.TimeZone;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class Utils {
|
||||
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
|
||||
public static final Charset UTF_8 = Charset.forName("utf-8");
|
||||
public static final Charset US_ASCII = Charset.forName("US-ASCII");
|
||||
|
||||
public static final TimeZone UTC = TimeZone.getTimeZone("UTC");
|
||||
public static final FileTime ZERO_TIME = FileTime.fromMillis(0);
|
||||
|
||||
public static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
public static final CharSequence[] EMPTY_CHAR_SEQUENCE_ARRAY = new CharSequence[0];
|
||||
public static final CopyOption[] EMPTY_COPY_OPTION_ARRAY = new CopyOption[0];
|
||||
public static final LinkOption[] EMPTY_LINK_OPTION_ARRAY = new LinkOption[0];
|
||||
public static final FileVisitOption[] EMPTY_FILE_VISIT_OPTION_ARRAY = new FileVisitOption[0];
|
||||
public static final OpenOption[] EMPTY_OPEN_OPTION_ARRAY = new OpenOption[0];
|
||||
|
||||
public static final DirectoryStream.Filter<Path> ACCEPT_ALL_FILTER = new DirectoryStream.Filter<Path> () {
|
||||
@Override
|
||||
public boolean accept (Path entry) throws IOException {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
public static final FileVisitor<Path> DELETE_FILE_VISITOR = new SimpleFileVisitor<Path>() {
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory (Path dir, IOException e) throws IOException {
|
||||
if (e != null)
|
||||
throw e;
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile (Path file, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFileFailed (Path file, IOException e) throws IOException {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private static final Iterator<?> EMPTY_ITERATOR = new Iterator<Object> () {
|
||||
@Override
|
||||
public boolean hasNext () {
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public Object next () {
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
public void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
|
||||
private Utils () {}
|
||||
|
||||
public static boolean equals (Object a, Object b) {
|
||||
return a != null ? a.equals(b) : b == null;
|
||||
}
|
||||
|
||||
public static boolean contentEquals (CharSequence cs1, CharSequence cs2) {
|
||||
int l = cs1.length();
|
||||
if (l != cs2.length())
|
||||
return false;
|
||||
for (int i = 0; --l >= 0; ++i) {
|
||||
if (cs1.charAt(i) != cs2.charAt(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean contentEqualsIgnoreCase (CharSequence cs1, CharSequence cs2) {
|
||||
int l = cs1.length();
|
||||
if (l != cs2.length())
|
||||
return false;
|
||||
for (int i = 0; --l >= 0; ++i) {
|
||||
char c1 = cs1.charAt(i);
|
||||
char c2 = cs2.charAt(i);
|
||||
if (c1 != c2) {
|
||||
c1 = Character.toUpperCase(c1);
|
||||
c2 = Character.toUpperCase(c2);
|
||||
if (c1 != c2) {
|
||||
c1 = Character.toLowerCase(c1);
|
||||
c2 = Character.toLowerCase(c2);
|
||||
if (c1 != c2)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Available in Java 1.8
|
||||
* @see java.lang.String#join(CharSequence, Iterable)
|
||||
*/
|
||||
public static String join (CharSequence delimiter, Iterable<? extends CharSequence> elements) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
CharSequence d = "";
|
||||
for (final CharSequence element : elements) {
|
||||
sb.append(d).append(element);
|
||||
d = delimiter;
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Available in Java 1.7 (Android 4.4)
|
||||
* @see java.util.Collections#emptyIterator()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E> Iterator<E> emptyIterator () {
|
||||
return (Iterator<E>)EMPTY_ITERATOR;
|
||||
}
|
||||
|
||||
public static <E> Iterator<E> singletonIterator (final E element) {
|
||||
return new Iterator<E>() {
|
||||
private boolean started;
|
||||
@Override
|
||||
public boolean hasNext () {
|
||||
return !started;
|
||||
}
|
||||
@Override
|
||||
public E next () {
|
||||
if (started)
|
||||
throw new NoSuchElementException();
|
||||
started = true;
|
||||
return element;
|
||||
}
|
||||
@Override
|
||||
public void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T> DirectoryStream<T> singletonDirectoryStream (final T entry) {
|
||||
return new AbstractDirectoryStream<T>() {
|
||||
private boolean started;
|
||||
@Override
|
||||
protected T advance () throws IOException {
|
||||
if (started)
|
||||
return null;
|
||||
started = true;
|
||||
return entry;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public static <T> List<T> listOf (Iterator<T> i) {
|
||||
final List<T> list = new ArrayList<T>();
|
||||
while (i.hasNext())
|
||||
list.add(i.next());
|
||||
return list;
|
||||
}
|
||||
|
||||
public static <T> List<T> listOf (Iterable<T> i) {
|
||||
return listOf(i.iterator());
|
||||
}
|
||||
|
||||
|
||||
public static void closeQuietly (Closeable c) {
|
||||
try {
|
||||
c.close();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public static void unmap (MappedByteBuffer buf) {
|
||||
try {
|
||||
final Object cleaner = buf.getClass().getMethod("cleaner").invoke(buf);
|
||||
cleaner.getClass().getMethod("clean").invoke(cleaner);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
public static long transfer (ReadableByteChannel in, WritableByteChannel out) throws IOException {
|
||||
if (in instanceof FileChannel)
|
||||
return transfer((FileChannel)in, out);
|
||||
else
|
||||
return transfer(in, out, ByteBuffer.allocate(BUFFER_SIZE));
|
||||
}
|
||||
|
||||
public static long transfer (ReadableByteChannel in, WritableByteChannel out, ByteBuffer buf) throws IOException {
|
||||
long written = 0;
|
||||
while (in.read(buf) != -1) {
|
||||
buf.flip();
|
||||
while (buf.hasRemaining())
|
||||
written += out.write(buf);
|
||||
buf.clear();
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
public static long transfer (FileChannel in, WritableByteChannel out) throws IOException {
|
||||
long remaining = in.size();
|
||||
long written = 0;
|
||||
long b;
|
||||
while (remaining > 0) {
|
||||
b = in.transferTo(written, remaining, out);
|
||||
remaining -= b;
|
||||
written += b;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
public static long transfer (InputStream in, OutputStream out) throws IOException {
|
||||
return transfer(in, out, new byte[BUFFER_SIZE]);
|
||||
}
|
||||
|
||||
public static long transfer (InputStream in, OutputStream out, byte[] buf) throws IOException {
|
||||
long written = 0;
|
||||
int b;
|
||||
while ((b = in.read(buf)) != -1) {
|
||||
out.write(buf, 0, b);
|
||||
written += b;
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
public static byte[] readAllBytes (InputStream in, int capacity) throws IOException {
|
||||
byte[] data = new byte[capacity];
|
||||
int size = 0;
|
||||
for (int b;;) {
|
||||
while ((b = in.read(data, size, capacity - size)) > 0)
|
||||
size += b;
|
||||
if (b < 0 || (b = in.read()) < 0)
|
||||
break;
|
||||
if (capacity == Integer.MAX_VALUE)
|
||||
throw new OutOfMemoryError("Array size exceeded");
|
||||
capacity = (int)Math.min(Math.max(capacity * 2L, BUFFER_SIZE), Integer.MAX_VALUE);
|
||||
data = Arrays.copyOf(data, capacity);
|
||||
data[size++] = (byte)b;
|
||||
}
|
||||
return (size == data.length) ? data : Arrays.copyOf(data, size);
|
||||
}
|
||||
|
||||
|
||||
// http://stackoverflow.com/a/522281/445360
|
||||
private static final Pattern RFC3339 = Pattern.compile("(\\d{4})-(\\d{2})-(\\d{2})T(\\d{2}):(\\d{2}):(\\d{2})(?:\\.(\\d+))?(?:Z|([+-]\\d{2}:\\d{2}))");
|
||||
public static long parseRfc3339 (CharSequence text) {
|
||||
final Matcher m = RFC3339.matcher(text);
|
||||
if (!m.matches())
|
||||
throw new IllegalArgumentException();
|
||||
String g = m.group(8);
|
||||
final GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone((g != null) ? g : "UTC"), Locale.US);
|
||||
//noinspection MagicConstant
|
||||
gc.set(
|
||||
Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)) - 1, Integer.parseInt(m.group(3)),
|
||||
Integer.parseInt(m.group(4)), Integer.parseInt(m.group(5)), Integer.parseInt(m.group(6)));
|
||||
g = m.group(7);
|
||||
gc.set(Calendar.MILLISECOND, (g != null) ? Integer.parseInt(g)%1000 : 0);
|
||||
return gc.getTimeInMillis();
|
||||
}
|
||||
|
||||
public static String formatRfc3339 (long millis) {
|
||||
final GregorianCalendar gc = new GregorianCalendar(Utils.UTC, Locale.US);
|
||||
gc.setTimeInMillis(millis);
|
||||
//noinspection MagicConstant
|
||||
return String.format(Locale.US, (gc.get(Calendar.MILLISECOND) != 0) ? "%1$tFT%1$tT.%1$tLZ" : "%1$tFT%1$tTZ", gc);
|
||||
}
|
||||
|
||||
/*
|
||||
public static String formatRfc3339 (long time) {
|
||||
final GregorianCalendar gc = new GregorianCalendar(TimeZone.getTimeZone("UTC"), Locale.US);
|
||||
gc.setTimeInMillis(time);
|
||||
return String.format(Locale.US, "%04d-%02d-%02dT%02d:%02d:%02d.%dZ",
|
||||
gc.get(Calendar.YEAR), gc.get(Calendar.MONTH) + 1, gc.get(Calendar.DAY_OF_MONTH),
|
||||
gc.get(Calendar.HOUR_OF_DAY), gc.get(Calendar.MINUTE), gc.get(Calendar.SECOND), gc.get(Calendar.MILLISECOND));
|
||||
}
|
||||
*/
|
||||
|
||||
public static FileTime parseFileTime (CharSequence text) {
|
||||
return FileTime.fromMillis(parseRfc3339(text));
|
||||
}
|
||||
|
||||
public static String getFileExtension (Path path) {
|
||||
final Path fileName = path.getFileName();
|
||||
if (fileName != null) {
|
||||
final String name = fileName.toString();
|
||||
final int i = name.lastIndexOf('.');
|
||||
if (i > 0 && i < name.length() - 1)
|
||||
return name.substring(i + 1).toLowerCase(Locale.US);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/** Must be sorted! */
|
||||
private static final char[] REGEX_ESCAPEES = { '$', '(', ')', '*', '+', '.', '?', '[', '\\', ']', '^', '{', '|' };
|
||||
|
||||
/**
|
||||
* Only works with unix path separator (/).
|
||||
*/
|
||||
public static String globToRegex (String glob, int s, int e) {
|
||||
final StringBuilder regex = new StringBuilder("^");
|
||||
int g = -1;
|
||||
char c;
|
||||
while (s < e) {
|
||||
switch (c = glob.charAt(s++)) {
|
||||
|
||||
case '?':
|
||||
regex.append("[^/]");
|
||||
break;
|
||||
|
||||
case '*':
|
||||
if (s < e && '*' == glob.charAt(s)) {
|
||||
regex.append(".*");
|
||||
++s;
|
||||
}
|
||||
else
|
||||
regex.append("[^/]*");
|
||||
break;
|
||||
|
||||
case '[':
|
||||
regex.append("[[");
|
||||
c = glob.charAt(checkGlobEnd(glob, s++, e, "Invalid class"));
|
||||
if ('^' == c) {
|
||||
regex.append("\\^");
|
||||
c = glob.charAt(checkGlobEnd(glob, s++, e, "Invalid class"));
|
||||
}
|
||||
else {
|
||||
if ('!' == c) {
|
||||
regex.append('^');
|
||||
c = glob.charAt(checkGlobEnd(glob, s++, e, "Invalid class"));
|
||||
}
|
||||
if ('-' == c) {
|
||||
regex.append('-');
|
||||
c = glob.charAt(checkGlobEnd(glob, s++, e, "Invalid class"));
|
||||
}
|
||||
}
|
||||
int l = Integer.MAX_VALUE;
|
||||
while (']' != c) {
|
||||
if ('/' == c)
|
||||
throw new PatternSyntaxException("Invalid class character: /", glob, s - 1);
|
||||
regex.append(c);
|
||||
if ('-' == c) {
|
||||
c = glob.charAt(checkGlobEnd(glob, s++, e, "Invalid range"));
|
||||
if (c < l)
|
||||
throw new PatternSyntaxException("Invalid range", glob, s - 3);
|
||||
l = Integer.MAX_VALUE;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
l = c;
|
||||
c = glob.charAt(checkGlobEnd(glob, s++, e, "Invalid range"));
|
||||
}
|
||||
regex.append("]&&[^/]]");
|
||||
break;
|
||||
|
||||
case '{':
|
||||
if (g != -1)
|
||||
throw new PatternSyntaxException("Nested group", glob, s - 1);
|
||||
regex.append("(?:");
|
||||
g = s;
|
||||
break;
|
||||
case ',':
|
||||
regex.append((g != -1) ? '|' : ',');
|
||||
break;
|
||||
case '}':
|
||||
regex.append((g != -1) ? ')' : '}');
|
||||
g = -1;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
c = glob.charAt(checkGlobEnd(glob, s++, e, "Invalid escape"));
|
||||
// fall thu
|
||||
default:
|
||||
if (Arrays.binarySearch(REGEX_ESCAPEES, c) >= 0)
|
||||
regex.append("\\");
|
||||
regex.append(c);
|
||||
}
|
||||
}
|
||||
if (g != -1)
|
||||
throw new PatternSyntaxException("Invalid group", glob, g - 1);
|
||||
return regex.append('$').toString();
|
||||
}
|
||||
|
||||
private static int checkGlobEnd (String glob, int s, int e, String message) {
|
||||
if (s == e)
|
||||
throw new PatternSyntaxException(message, glob, s - 1);
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.internal;
|
||||
|
||||
import com.llamalab.safs.WatchEvent;
|
||||
|
||||
public class WatchEventKind<T> implements WatchEvent.Kind<T> {
|
||||
|
||||
private final String name;
|
||||
private final Class<T> type;
|
||||
|
||||
public WatchEventKind (String name, Class<T> type) {
|
||||
this.name = name;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String name () {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Class<T> type () {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString () {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.java;
|
||||
|
||||
import com.llamalab.safs.FileSystem;
|
||||
import com.llamalab.safs.FileSystemAlreadyExistsException;
|
||||
import com.llamalab.safs.Path;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
public final class DefaultJavaFileSystemProvider extends JavaFileSystemProvider {
|
||||
|
||||
private final FileSystem fileSystem = new JavaFileSystem(this);
|
||||
|
||||
@Override
|
||||
public FileSystem getFileSystem (URI uri) {
|
||||
checkUri(uri);
|
||||
return fileSystem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystem newFileSystem (URI uri, Map<String,?> env) throws IOException {
|
||||
checkUri(uri);
|
||||
throw new FileSystemAlreadyExistsException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileSystem newFileSystem (Path path, Map<String,?> env) throws IOException {
|
||||
checkPath(path);
|
||||
throw new FileSystemAlreadyExistsException();
|
||||
}
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.java;
|
||||
|
||||
import com.llamalab.safs.LinkOption;
|
||||
import com.llamalab.safs.NoSuchFileException;
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.internal.DefaultFileSystem;
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
import com.llamalab.safs.unix.AbstractUnixFileSystem;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Only support {@link com.llamalab.safs.unix.UnixPath}.
|
||||
*/
|
||||
public class JavaFileSystem extends AbstractUnixFileSystem implements DefaultFileSystem {
|
||||
|
||||
protected volatile Path cacheDirectory;
|
||||
protected volatile Path currentDirectory;
|
||||
|
||||
public JavaFileSystem (FileSystemProvider provider) {
|
||||
super(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void close () throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isOpen () {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly () {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getCacheDirectory () {
|
||||
if (cacheDirectory == null)
|
||||
cacheDirectory = getPathSanitized(System.getProperty("java.io.tmpdir"));
|
||||
return cacheDirectory;
|
||||
}
|
||||
|
||||
public final Path getCurrentDirectory () {
|
||||
if (currentDirectory == null)
|
||||
currentDirectory = getPathSanitized(System.getProperty("user.dir"));
|
||||
return currentDirectory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Path toRealPath (Path path, LinkOption... options) throws IOException {
|
||||
final File file = path.toFile();
|
||||
if (!file.exists())
|
||||
throw new NoSuchFileException(path.toString());
|
||||
for (final LinkOption option : options) {
|
||||
if (LinkOption.NOFOLLOW_LINKS == option)
|
||||
return path.toAbsolutePath().normalize();
|
||||
}
|
||||
return getPathSanitized(file.getCanonicalPath());
|
||||
}
|
||||
}
|
@ -0,0 +1,490 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.java;
|
||||
|
||||
import com.llamalab.safs.AccessDeniedException;
|
||||
import com.llamalab.safs.AtomicMoveNotSupportedException;
|
||||
import com.llamalab.safs.CopyOption;
|
||||
import com.llamalab.safs.DirectoryNotEmptyException;
|
||||
import com.llamalab.safs.DirectoryStream;
|
||||
import com.llamalab.safs.FileAlreadyExistsException;
|
||||
import com.llamalab.safs.FileStore;
|
||||
import com.llamalab.safs.FileSystemException;
|
||||
import com.llamalab.safs.LinkOption;
|
||||
import com.llamalab.safs.NoSuchFileException;
|
||||
import com.llamalab.safs.NotDirectoryException;
|
||||
import com.llamalab.safs.NotLinkException;
|
||||
import com.llamalab.safs.OpenOption;
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.StandardCopyOption;
|
||||
import com.llamalab.safs.StandardOpenOption;
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
import com.llamalab.safs.attribute.FileAttribute;
|
||||
import com.llamalab.safs.attribute.FileTime;
|
||||
import com.llamalab.safs.channels.SeekableByteChannel;
|
||||
import com.llamalab.safs.internal.AbstractDirectoryStream;
|
||||
import com.llamalab.safs.internal.BasicFileAttributeValue;
|
||||
import com.llamalab.safs.internal.CompleteBasicFileAttributes;
|
||||
import com.llamalab.safs.internal.FileType;
|
||||
import com.llamalab.safs.internal.SearchSet;
|
||||
import com.llamalab.safs.internal.Utils;
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
import com.llamalab.safs.unix.AbstractUnixFileSystemProvider;
|
||||
import com.llamalab.safs.unix.UnixPath;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Only support {@link UnixPath}.
|
||||
*/
|
||||
public abstract class JavaFileSystemProvider extends AbstractUnixFileSystemProvider {
|
||||
|
||||
public JavaFileSystemProvider () {}
|
||||
public JavaFileSystemProvider (FileSystemProvider provider) {}
|
||||
|
||||
@Override
|
||||
public String getScheme () {
|
||||
return "file";
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStore getFileStore (Path path) throws IOException {
|
||||
// TODO: possible?
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameFile (Path path1, Path path2) throws IOException {
|
||||
if (path1.equals(path2))
|
||||
return true;
|
||||
return getPathType().isInstance(path1) && getPathType().isInstance(path2)
|
||||
&& path1.getFileSystem().equals(path2.getFileSystem())
|
||||
&& isSameFile(path1.toFile(), path2.toFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHidden (Path path) throws IOException {
|
||||
checkPath(path);
|
||||
return ((UnixPath)path).isHidden();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createDirectory (Path dir, FileAttribute<?>... attrs) throws IOException {
|
||||
checkPath(dir);
|
||||
createDirectory(dir.toFile(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete (Path path) throws IOException {
|
||||
checkPath(path);
|
||||
delete(path.toFile(), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy (Path source, Path target, CopyOption... options) throws IOException {
|
||||
checkPath(source);
|
||||
checkPath(target);
|
||||
transfer(source, target, false, new SearchSet<CopyOption>(options));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move (Path source, Path target, CopyOption... options) throws IOException {
|
||||
checkPath(source);
|
||||
checkPath(target);
|
||||
transfer(source, target, true, new SearchSet<CopyOption>(options));
|
||||
}
|
||||
|
||||
// TODO: symbolic links
|
||||
private void transfer (Path source, Path target, boolean move, Set<CopyOption> options) throws IOException {
|
||||
final File sourceFile = source.toFile();
|
||||
final BasicFileAttributes sourceAttrs = readBasicFileAttributes(sourceFile);
|
||||
final File targetFile = target.toFile();
|
||||
if (sourceFile.getCanonicalPath().equals(targetFile.getCanonicalPath()))
|
||||
return;
|
||||
// atomic
|
||||
if (move && options.contains(StandardCopyOption.ATOMIC_MOVE)) {
|
||||
if (sourceFile.renameTo(targetFile))
|
||||
return;
|
||||
throw new AtomicMoveNotSupportedException(source.toString(), target.toString(), "Rename failed");
|
||||
}
|
||||
// delete target
|
||||
if (options.contains(StandardCopyOption.REPLACE_EXISTING))
|
||||
delete(targetFile, true); // throws DirectoryNotEmptyException
|
||||
else if (targetFile.exists())
|
||||
throw new FileAlreadyExistsException(target.toString());
|
||||
// rename
|
||||
if (move && sourceFile.renameTo(targetFile))
|
||||
return;
|
||||
// transfer
|
||||
if (sourceAttrs.isDirectory()) {
|
||||
if (move && isNonEmptyDirectory(sourceFile))
|
||||
throw new DirectoryNotEmptyException(source.toString());
|
||||
createDirectory(target);
|
||||
}
|
||||
else
|
||||
copyFile(sourceFile, targetFile);
|
||||
try {
|
||||
if (options.contains(StandardCopyOption.COPY_ATTRIBUTES))
|
||||
setLastModifiedTime(targetFile, sourceAttrs.lastModifiedTime());
|
||||
if (move)
|
||||
delete(sourceFile, false);
|
||||
}
|
||||
catch (IOException e) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
targetFile.delete();
|
||||
throw e;
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
targetFile.delete();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream newInputStream (Path path, OpenOption... options) throws IOException {
|
||||
checkPath(path);
|
||||
return newInputStream(path.toFile(), (options.length == 0) ? DEFAULT_NEW_INPUT_STREAM_OPTIONS : new SearchSet<OpenOption>(options));
|
||||
}
|
||||
|
||||
// TODO: LinkOption.NOFOLLOW_LINKS
|
||||
private InputStream newInputStream (File file, Set<? extends OpenOption> options) throws IOException {
|
||||
if (options.contains(StandardOpenOption.WRITE))
|
||||
throw new IllegalArgumentException();
|
||||
try {
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, file.toString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OutputStream newOutputStream (Path path, OpenOption... options) throws IOException {
|
||||
checkPath(path);
|
||||
return newOutputStream(path.toFile(), (options.length == 0) ? DEFAULT_NEW_OUTPUT_STREAM_OPTIONS : new SearchSet<OpenOption>(options));
|
||||
}
|
||||
|
||||
// TODO: LinkOption.NOFOLLOW_LINKS
|
||||
private OutputStream newOutputStream (File file, Set<? extends OpenOption> options) throws IOException {
|
||||
if (!options.contains(StandardOpenOption.WRITE))
|
||||
throw new IllegalArgumentException();
|
||||
try {
|
||||
checkCreateOptions(file, options);
|
||||
return new FileOutputStream(file, options.contains(StandardOpenOption.APPEND));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, file.toString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel newByteChannel (Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
|
||||
checkPath(path);
|
||||
return newByteChannel(path.toFile(), options, attrs);
|
||||
}
|
||||
|
||||
// TODO: LinkOption.NOFOLLOW_LINKS
|
||||
private SeekableByteChannel newByteChannel (File file, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException {
|
||||
try {
|
||||
if (!options.contains(StandardOpenOption.WRITE))
|
||||
return new SeekableByteChannelWrapper(new RandomAccessFile(file, "r").getChannel(), false);
|
||||
checkCreateOptions(file, options);
|
||||
final RandomAccessFile raf = new RandomAccessFile(file, toModeString(options));
|
||||
if (options.contains(StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||
try {
|
||||
raf.setLength(0);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Utils.closeQuietly(raf);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return new SeekableByteChannelWrapper(raf.getChannel(), options.contains(StandardOpenOption.APPEND));
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, file.toString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
private static String toModeString (Set<? extends OpenOption> options) {
|
||||
if (options.contains(StandardOpenOption.SYNC))
|
||||
return "rws";
|
||||
if (options.contains(StandardOpenOption.DSYNC))
|
||||
return "rwd";
|
||||
return "rw";
|
||||
}
|
||||
|
||||
private static void checkCreateOptions (File file, Set<? extends OpenOption> options) throws IOException {
|
||||
if (options.contains(StandardOpenOption.CREATE_NEW)) {
|
||||
if (file.exists())
|
||||
throw new FileAlreadyExistsException(file.toString());
|
||||
}
|
||||
else if (!options.contains(StandardOpenOption.CREATE)) {
|
||||
if (!file.exists())
|
||||
throw new NoSuchFileException(file.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
private RandomAccessFile newRandomAccessFile (File file, Set<? extends OpenOption> options) throws IOException {
|
||||
try {
|
||||
if (!options.contains(StandardOpenOption.WRITE))
|
||||
return new RandomAccessFile(file, toModeString(options));
|
||||
final RandomAccessFile raf = new RandomAccessFile(file, toModeString(options));
|
||||
if (options.contains(StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||
try {
|
||||
raf.setLength(0);
|
||||
}
|
||||
catch (IOException e) {
|
||||
Utils.closeQuietly(raf);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return raf;
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, file.toString(), null);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
@Override
|
||||
public void createSymbolicLink (Path link, Path target) throws IOException {
|
||||
checkPaths(link, target);
|
||||
createSymbolicLink(link.toFile(), target.toFile());
|
||||
}
|
||||
|
||||
protected void createSymbolicLink (File link, File target) throws IOException {
|
||||
final Process process = Runtime.getRuntime().exec(new String[] { "ln", "-s", target.toString(), link.toString() });
|
||||
try {
|
||||
if (0 != process.waitFor()) {
|
||||
if (target.exists())
|
||||
throw new FileAlreadyExistsException(target.toString());
|
||||
else
|
||||
throw new IOException("ln command failure");
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw (IOException)new InterruptedIOException().initCause(e);
|
||||
}
|
||||
finally {
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Path readSymbolicLink (Path link) throws IOException {
|
||||
final Path parent = link.getParent();
|
||||
if (parent != null)
|
||||
link = parent.toRealPath().resolve(link.getFileName());
|
||||
final Path real = link.toRealPath();
|
||||
if (real.equals(link.toAbsolutePath()))
|
||||
throw new NotLinkException(link.toString());
|
||||
return real;
|
||||
}
|
||||
|
||||
/**
|
||||
* May be overridden with a faster implementation.
|
||||
*/
|
||||
protected boolean isSymbolicLink (Path path) {
|
||||
return isSymbolicLink(path.toFile());
|
||||
}
|
||||
|
||||
private boolean isSymbolicLink (File file) {
|
||||
try {
|
||||
final File parent = file.getParentFile();
|
||||
if (parent != null)
|
||||
file = new File(parent.getCanonicalPath(), file.getName());
|
||||
return !file.getCanonicalPath().equals(file.getAbsolutePath());
|
||||
}
|
||||
catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public <A extends BasicFileAttributes> A readAttributes (Path path, Class<A> type, LinkOption... options) throws IOException {
|
||||
checkPath(path);
|
||||
if (BasicFileAttributes.class != type)
|
||||
throw new UnsupportedOperationException("Unsupported type: "+type);
|
||||
return (A)readBasicFileAttributes(path.toFile(), options);
|
||||
}
|
||||
|
||||
protected BasicFileAttributes readBasicFileAttributes (File file, LinkOption... options) throws IOException {
|
||||
for (final LinkOption option : options) {
|
||||
if (LinkOption.NOFOLLOW_LINKS == option) {
|
||||
if (!isSymbolicLink(file))
|
||||
break;
|
||||
return new CompleteBasicFileAttributes(null, FileType.SYMBOLIC_LINK, 0, Utils.ZERO_TIME, Utils.ZERO_TIME, Utils.ZERO_TIME);
|
||||
}
|
||||
}
|
||||
if (!file.exists())
|
||||
throw new NoSuchFileException(file.toString());
|
||||
final FileType fileType;
|
||||
if (file.isDirectory())
|
||||
fileType = FileType.DIRECTORY;
|
||||
else if (file.isFile())
|
||||
fileType = FileType.REGULAR_FILE;
|
||||
else
|
||||
fileType = FileType.OTHER;
|
||||
return new CompleteBasicFileAttributes(
|
||||
null,
|
||||
fileType,
|
||||
file.length(),
|
||||
Utils.ZERO_TIME,
|
||||
FileTime.fromMillis(file.lastModified()),
|
||||
Utils.ZERO_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setAttributes (Path path, Set<? extends FileAttribute<?>> attrs, LinkOption... options) throws IOException {
|
||||
checkPath(path);
|
||||
for (final FileAttribute<?> attr : attrs) {
|
||||
if (attr instanceof BasicFileAttributeValue) {
|
||||
switch (((BasicFileAttributeValue)attr).type()) {
|
||||
case lastModifiedTime:
|
||||
setLastModifiedTime(path.toFile(), (FileTime)attr.value());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
throw new UnsupportedOperationException("Attribute: "+attr.name());
|
||||
}
|
||||
}
|
||||
|
||||
protected void setLastModifiedTime (File file, FileTime value) throws IOException {
|
||||
if (!file.setLastModified(value.toMillis()))
|
||||
throw new FileSystemException(file.toString(), null, "Failed to set last modified time");
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> newDirectoryStream (final Path dir, final DirectoryStream.Filter<? super Path> filter) throws IOException {
|
||||
checkPath(dir);
|
||||
if (filter == null)
|
||||
throw new NullPointerException("filter");
|
||||
try {
|
||||
final File dirFile = dir.toFile();
|
||||
final String[] files = dirFile.list();
|
||||
if (files == null) {
|
||||
if (dirFile.exists() && !dirFile.canRead())
|
||||
throw new AccessDeniedException(dir.toString());
|
||||
throw new NotDirectoryException(dir.toString());
|
||||
}
|
||||
return new AbstractDirectoryStream<Path>() {
|
||||
private int index;
|
||||
@Override
|
||||
protected Path advance () throws IOException {
|
||||
while (index < files.length) {
|
||||
final Path entry = dir.resolve(files[index++]);
|
||||
if (filter.accept(entry))
|
||||
return entry;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, dir.toString(), null);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void createDirectory (File dir, FileAttribute<?>... attrs) throws IOException {
|
||||
if (!dir.mkdir()) {
|
||||
if (dir.exists()) {
|
||||
if (!dir.canWrite())
|
||||
throw new AccessDeniedException(dir.toString());
|
||||
throw new FileAlreadyExistsException(dir.toString());
|
||||
}
|
||||
throw new FileSystemException(dir.toString(), null, "Failed to create directory");
|
||||
}
|
||||
}
|
||||
|
||||
protected final void delete (File file, boolean ifExists) throws IOException {
|
||||
if (!file.delete()) {
|
||||
if (isNonEmptyDirectory(file))
|
||||
throw new DirectoryNotEmptyException(file.toString());
|
||||
if (file.exists()) {
|
||||
if (!file.canWrite())
|
||||
throw new AccessDeniedException(file.toString());
|
||||
throw new FileSystemException(file.toString(), null, "Failed to delete file");
|
||||
}
|
||||
else if (!ifExists)
|
||||
throw new NoSuchFileException(file.toString());
|
||||
}
|
||||
}
|
||||
|
||||
protected final void copyFile (File source, File target) throws IOException {
|
||||
final FileChannel in;
|
||||
try {
|
||||
in = new FileInputStream(source).getChannel();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, source.toString(), null);
|
||||
}
|
||||
try {
|
||||
final FileChannel out;
|
||||
try {
|
||||
out = new FileOutputStream(target).getChannel();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, target.toString(), null);
|
||||
}
|
||||
try {
|
||||
Utils.transfer(in, out);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw toProperException(e, source.toString(), target.toString());
|
||||
}
|
||||
finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
protected final boolean isSameFile (File file1, File file2) throws IOException {
|
||||
return file1.getCanonicalPath().equals(file2.getCanonicalPath());
|
||||
}
|
||||
|
||||
protected final boolean isNonEmptyDirectory (File dir) {
|
||||
final String[] names = dir.list(new FilenameFilter() {
|
||||
private boolean found;
|
||||
@Override
|
||||
public boolean accept (File dir, String filename) {
|
||||
if (found)
|
||||
return false;
|
||||
return found = true;
|
||||
}
|
||||
});
|
||||
return names != null && names.length != 0;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.java;
|
||||
|
||||
import com.llamalab.safs.channels.SeekableByteChannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
final class SeekableByteChannelWrapper implements SeekableByteChannel {
|
||||
|
||||
private final FileChannel fc;
|
||||
private final boolean append;
|
||||
|
||||
public SeekableByteChannelWrapper (FileChannel fc, boolean append) {
|
||||
this.fc = fc;
|
||||
this.append = append;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close () throws IOException {
|
||||
fc.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpen () {
|
||||
return fc.isOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read (ByteBuffer dst) throws IOException {
|
||||
return fc.read(dst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write (ByteBuffer src) throws IOException {
|
||||
if (append)
|
||||
fc.position(fc.size());
|
||||
return fc.write(src);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long position () throws IOException {
|
||||
return fc.position();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel position (long newPosition) throws IOException {
|
||||
fc.position(newPosition);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long size () throws IOException {
|
||||
return fc.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeekableByteChannel truncate (long size) throws IOException {
|
||||
fc.truncate(size);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package com.llamalab.safs.kotlin.io.path
|
||||
import com.llamalab.safs.LinkOption
|
||||
import com.llamalab.safs.Path
|
||||
import com.llamalab.safs.Files
|
||||
import com.llamalab.safs.OpenOption
|
||||
import com.llamalab.safs.attribute.FileAttribute
|
||||
import java.nio.charset.Charset
|
||||
|
||||
public inline fun Path.isRegularFile(vararg options: LinkOption): Boolean = Files.isRegularFile(this, *options)
|
||||
public inline fun Path.readBytes(): ByteArray {
|
||||
return Files.readAllBytes(this)
|
||||
}
|
||||
public inline fun Path.writeBytes(array: ByteArray, vararg options: OpenOption): Unit {
|
||||
Files.write(this, array, *options)
|
||||
}
|
||||
public inline fun Path.createDirectories(vararg attributes: FileAttribute<*>): Path =
|
||||
Files.createDirectories(this, *attributes)
|
||||
|
||||
public fun createTempDirectory(directory: Path?, prefix: String? = null, vararg attributes: FileAttribute<*>): Path =
|
||||
if (directory != null)
|
||||
Files.createTempDirectory(directory, prefix, *attributes)
|
||||
else
|
||||
Files.createTempDirectory(prefix, *attributes)
|
||||
public inline fun createTempDirectory(prefix: String? = null, vararg attributes: FileAttribute<*>): Path = Files.createTempDirectory(prefix, *attributes)
|
||||
|
||||
public fun Path.writeText(text: CharSequence, charset: Charset = Charsets.UTF_8, vararg options: OpenOption) {
|
||||
Files.newOutputStream(this, *options).writer(charset).use { it.append(text) }
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.spi;
|
||||
|
||||
import com.llamalab.safs.CopyOption;
|
||||
import com.llamalab.safs.DirectoryStream;
|
||||
import com.llamalab.safs.FileStore;
|
||||
import com.llamalab.safs.FileSystem;
|
||||
import com.llamalab.safs.FileSystems;
|
||||
import com.llamalab.safs.LinkOption;
|
||||
import com.llamalab.safs.NoSuchFileException;
|
||||
import com.llamalab.safs.OpenOption;
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.attribute.BasicFileAttributes;
|
||||
import com.llamalab.safs.attribute.FileAttribute;
|
||||
import com.llamalab.safs.attribute.FileAttributeView;
|
||||
import com.llamalab.safs.channels.SeekableByteChannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class FileSystemProvider {
|
||||
|
||||
protected FileSystemProvider () {}
|
||||
|
||||
public static List<FileSystemProvider> installedProviders () {
|
||||
return InstalledFileSystemProvidersHolder.providers;
|
||||
}
|
||||
|
||||
public abstract String getScheme ();
|
||||
public abstract Path getPath (URI uri);
|
||||
public abstract FileSystem getFileSystem (URI uri);
|
||||
public abstract FileSystem newFileSystem (Path path, Map<String,?> env) throws IOException;
|
||||
public abstract FileSystem newFileSystem (URI uri, Map<String,?> env) throws IOException;
|
||||
public abstract FileStore getFileStore (Path path) throws IOException;
|
||||
public abstract boolean isSameFile (Path path1, Path path2) throws IOException;
|
||||
public abstract boolean isHidden (Path path) throws IOException;
|
||||
public abstract void createDirectory (Path dir, FileAttribute<?>... attrs) throws IOException;
|
||||
public abstract void delete (Path path) throws IOException;
|
||||
public boolean deleteIfExists (Path path) throws IOException {
|
||||
try {
|
||||
delete(path);
|
||||
return true;
|
||||
}
|
||||
catch (NoSuchFileException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public abstract void copy (Path source, Path target, CopyOption... options) throws IOException;
|
||||
public abstract void move (Path source,Path target, CopyOption... options) throws IOException;
|
||||
public abstract InputStream newInputStream (Path path, OpenOption... options) throws IOException;
|
||||
public abstract OutputStream newOutputStream (Path path, OpenOption... options) throws IOException;
|
||||
public abstract SeekableByteChannel newByteChannel (Path path, Set<? extends OpenOption> options, FileAttribute<?>... attrs) throws IOException;
|
||||
/*
|
||||
public void createSymbolicLink (Path link, Path target) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
*/
|
||||
public Path readSymbolicLink (Path link) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public abstract <A extends BasicFileAttributes> A readAttributes (Path path, Class<A> type, LinkOption... options) throws IOException;
|
||||
public abstract Map<String,Object> readAttributes (Path path, String attributes, LinkOption... options) throws IOException;
|
||||
public abstract void setAttribute (Path path, String attribute, Object value, LinkOption... options) throws IOException;
|
||||
public abstract <V extends FileAttributeView> V getFileAttributeView (Path path, Class<V> type, LinkOption... options);
|
||||
|
||||
public abstract DirectoryStream<Path> newDirectoryStream (Path dir, DirectoryStream.Filter<? super Path> filter) throws IOException;
|
||||
|
||||
private static final class InstalledFileSystemProvidersHolder {
|
||||
|
||||
static final List<FileSystemProvider> providers = loadInstalledProviders();
|
||||
|
||||
private static List<FileSystemProvider> loadInstalledProviders () {
|
||||
final List<FileSystemProvider> providers = new ArrayList<FileSystemProvider>();
|
||||
providers.add(FileSystems.getDefault().provider());
|
||||
for (final FileSystemProvider provider : ServiceLoader.load(FileSystemProvider.class, FileSystemProvider.class.getClassLoader())) {
|
||||
if (!"file".equalsIgnoreCase(provider.getScheme()))
|
||||
providers.add(provider);
|
||||
}
|
||||
return Collections.unmodifiableList(providers);
|
||||
}
|
||||
|
||||
} // class InstalledFileSystemProvidersHolder
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.spi;
|
||||
|
||||
import com.llamalab.safs.Path;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* System.setProperty("content.types.user.table", "/path/to/property/file");
|
||||
*/
|
||||
public abstract class FileTypeDetector {
|
||||
|
||||
protected FileTypeDetector () {}
|
||||
|
||||
public abstract String probeContentType (Path path) throws IOException;
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.unix;
|
||||
|
||||
import com.llamalab.safs.FileStore;
|
||||
import com.llamalab.safs.FileSystem;
|
||||
import com.llamalab.safs.LinkOption;
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.PathMatcher;
|
||||
import com.llamalab.safs.WatchService;
|
||||
import com.llamalab.safs.attribute.UserPrincipalLookupService;
|
||||
import com.llamalab.safs.internal.BasicFileAttribute;
|
||||
import com.llamalab.safs.internal.Utils;
|
||||
import com.llamalab.safs.spi.FileSystemProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public abstract class AbstractUnixFileSystem extends FileSystem {
|
||||
|
||||
protected final FileSystemProvider provider;
|
||||
private volatile Path emptyDirectory;
|
||||
private volatile Path rootDirectory;
|
||||
|
||||
public AbstractUnixFileSystem (FileSystemProvider provider) {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final FileSystemProvider provider () {
|
||||
return provider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Path getPath (String first, String... more) {
|
||||
return getPathSanitized(UnixPath.sanitize(first, more));
|
||||
}
|
||||
|
||||
public final Path getPath (String first) {
|
||||
return getPathSanitized(UnixPath.sanitize(first, Utils.EMPTY_STRING_ARRAY));
|
||||
}
|
||||
|
||||
protected Path getPathSanitized (String path) {
|
||||
return new UnixPath(this, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMatcher getPathMatcher (String syntaxAndPattern) {
|
||||
final Pattern pattern;
|
||||
if (syntaxAndPattern.startsWith("regex:"))
|
||||
pattern = Pattern.compile(syntaxAndPattern.substring(6));
|
||||
else if (syntaxAndPattern.startsWith("glob:"))
|
||||
pattern = Pattern.compile(Utils.globToRegex(syntaxAndPattern, 5, syntaxAndPattern.length()));
|
||||
else
|
||||
throw new UnsupportedOperationException(syntaxAndPattern);
|
||||
return new PathMatcher() {
|
||||
@Override
|
||||
public boolean matches (Path path) {
|
||||
return pattern.matcher(path.toString()).matches();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected abstract Path toRealPath (Path path, LinkOption... options) throws IOException;
|
||||
|
||||
@Override
|
||||
public final String getSeparator() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> supportedFileAttributeViews() {
|
||||
return Collections.singleton(BasicFileAttribute.VIEW_NAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<FileStore> getFileStores () {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterable<Path> getRootDirectories () {
|
||||
return Collections.singleton(getRootDirectory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipalLookupService getUserPrincipalLookupService () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchService newWatchService () throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public abstract Path getCurrentDirectory ();
|
||||
|
||||
public final Path getEmptyDirectory () {
|
||||
if (emptyDirectory == null)
|
||||
emptyDirectory = getPathSanitized("");
|
||||
return emptyDirectory;
|
||||
}
|
||||
|
||||
public final Path getRootDirectory () {
|
||||
if (rootDirectory == null)
|
||||
rootDirectory = getPathSanitized("/");
|
||||
return rootDirectory;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.unix;
|
||||
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.internal.AbstractFileSystemProvider;
|
||||
import com.llamalab.safs.internal.Utils;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
public abstract class AbstractUnixFileSystemProvider extends AbstractFileSystemProvider {
|
||||
|
||||
@Override
|
||||
protected Class<? extends UnixPath> getPathType () {
|
||||
return UnixPath.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath (URI uri) {
|
||||
if (!uri.isAbsolute() || uri.isOpaque() || uri.getAuthority() != null || uri.getFragment() != null || uri.getQuery() != null)
|
||||
throw new IllegalArgumentException();
|
||||
return new UnixPath((AbstractUnixFileSystem)getFileSystem(uri), UnixPath.sanitize(uri.getPath(), Utils.EMPTY_STRING_ARRAY));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,551 @@
|
||||
/*
|
||||
* Copyright (C) 2019 Henrik Lindqvist
|
||||
*
|
||||
* 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.llamalab.safs.unix;
|
||||
|
||||
import com.llamalab.safs.LinkOption;
|
||||
import com.llamalab.safs.Path;
|
||||
import com.llamalab.safs.ProviderMismatchException;
|
||||
import com.llamalab.safs.WatchEvent;
|
||||
import com.llamalab.safs.WatchKey;
|
||||
import com.llamalab.safs.WatchService;
|
||||
import com.llamalab.safs.internal.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
public class UnixPath implements Path {
|
||||
|
||||
private final AbstractUnixFileSystem fs;
|
||||
private final String path;
|
||||
private volatile short[] nameOffsets; // lazy
|
||||
|
||||
protected UnixPath (AbstractUnixFileSystem fs, String path) {
|
||||
this.fs = fs;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
protected UnixPath (UnixPath other) {
|
||||
this(other.fs, other.path);
|
||||
this.nameOffsets = other.nameOffsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final AbstractUnixFileSystem getFileSystem () {
|
||||
return fs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString () {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int hashCode () {
|
||||
int hc = 17;
|
||||
hc = 37*hc + fs.hashCode();
|
||||
hc = 37*hc + path.hashCode();
|
||||
return hc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals (Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!(obj instanceof UnixPath))
|
||||
return false;
|
||||
final UnixPath other = (UnixPath)obj;
|
||||
return fs.equals(other.fs)
|
||||
&& path.equals(other.path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public int compareTo (Path other) {
|
||||
return path.compareTo(other.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isAbsolute () {
|
||||
return path.startsWith("/");
|
||||
}
|
||||
|
||||
public final boolean isHidden () {
|
||||
return path.startsWith(".");
|
||||
}
|
||||
|
||||
public final boolean isEmpty () {
|
||||
return path.isEmpty();
|
||||
}
|
||||
|
||||
public final boolean isRoot () {
|
||||
return path.equals("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if . or ..
|
||||
*/
|
||||
public final boolean isDots () {
|
||||
return ".".equals(path) || "..".equals(path);
|
||||
}
|
||||
|
||||
private static final short[] ZERO_NAME_OFFSETS = new short[] { 0 };
|
||||
private static final short[] EMPTY_NAME_OFFSETS = new short[0];
|
||||
|
||||
private short[] getNameOffsets () {
|
||||
if (nameOffsets != null)
|
||||
return nameOffsets;
|
||||
final String path = this.path;
|
||||
final int length = path.length();
|
||||
if (length == 0)
|
||||
return nameOffsets = ZERO_NAME_OFFSETS;
|
||||
int count = 0;
|
||||
int index = 0;
|
||||
while (index < length) {
|
||||
if ('/' != path.charAt(index)) {
|
||||
if (index++ > 0xFFFF)
|
||||
throw new ArrayStoreException();
|
||||
++count;
|
||||
while (index < length && '/' != path.charAt(index))
|
||||
++index;
|
||||
}
|
||||
else
|
||||
++index;
|
||||
}
|
||||
if (count == 0)
|
||||
return nameOffsets = EMPTY_NAME_OFFSETS;
|
||||
final short[] offsets = new short[count];
|
||||
index = 0;
|
||||
count = 0;
|
||||
while (index < length) {
|
||||
if ('/' != path.charAt(index)) {
|
||||
offsets[count++] = (short)index++;
|
||||
while (index < length && '/' != path.charAt(index))
|
||||
++index;
|
||||
}
|
||||
else
|
||||
++index;
|
||||
}
|
||||
return nameOffsets = offsets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getRoot () {
|
||||
return isAbsolute() ? fs.getRootDirectory() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getParent () {
|
||||
final short[] offsets = getNameOffsets();
|
||||
final int count = offsets.length;
|
||||
if (count == 0)
|
||||
return null;
|
||||
final int end = (offsets[count - 1] & 0xFFFF) - 1;
|
||||
if (end <= 0)
|
||||
return getRoot();
|
||||
return fs.getPathSanitized(path.substring(0, end));
|
||||
}
|
||||
|
||||
/*
|
||||
public final Path getAncestor (int endIndex) {
|
||||
final short[] offsets = getNameOffsets();
|
||||
final int count = offsets.length;
|
||||
if (endIndex < 0 || count < endIndex)
|
||||
throw new IllegalArgumentException();
|
||||
if (count == 0)
|
||||
return null;
|
||||
if (endIndex == count)
|
||||
return this;
|
||||
final int end = (offsets[endIndex] & 0xFFFF) - 1;
|
||||
if (end <= 0)
|
||||
return getRoot();
|
||||
return fs.getPathSanitized(path.substring(0, end));
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public Path getFileName () {
|
||||
final short[] offsets = getNameOffsets();
|
||||
final int count = offsets.length;
|
||||
if (count == 0)
|
||||
return null;
|
||||
if (count == 1 && path.length() != 0 && '/' != path.charAt(0))
|
||||
return this;
|
||||
return fs.getPathSanitized(path.substring(offsets[count - 1] & 0xFFFF));
|
||||
}
|
||||
|
||||
public final int getNameCount () {
|
||||
return getNameOffsets().length;
|
||||
}
|
||||
|
||||
public Path getName (int index) {
|
||||
return subpath(index, index + 1);
|
||||
}
|
||||
|
||||
public Path subpath (int beginIndex, int endIndex) {
|
||||
final short[] offsets = getNameOffsets();
|
||||
final int count = offsets.length;
|
||||
if (beginIndex < 0 || count < endIndex || endIndex <= beginIndex)
|
||||
throw new IllegalArgumentException();
|
||||
if (endIndex < count)
|
||||
return fs.getPathSanitized(path.substring(offsets[beginIndex] & 0xFFFF, (offsets[endIndex] & 0xFFFF) - 1));
|
||||
else
|
||||
return fs.getPathSanitized(path.substring(offsets[beginIndex] & 0xFFFF));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean startsWith (Path other) {
|
||||
return fs.equals(other.getFileSystem())
|
||||
&& startsWithSanitized(((UnixPath)other).path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean startsWith (String other) {
|
||||
return startsWithSanitized(sanitize(other, Utils.EMPTY_STRING_ARRAY));
|
||||
}
|
||||
|
||||
private boolean startsWithSanitized (String other) {
|
||||
if (!path.startsWith(other))
|
||||
return false;
|
||||
final int end = other.length();
|
||||
return end == path.length() || '/' == path.charAt(end) || "/".equals(other);
|
||||
}
|
||||
|
||||
public final boolean endsWith (Path other) {
|
||||
return fs.equals(other.getFileSystem())
|
||||
&& endsWithSanitized(((UnixPath)other).path);
|
||||
}
|
||||
|
||||
public final boolean endsWith (String other) {
|
||||
return endsWithSanitized(sanitize(other, Utils.EMPTY_STRING_ARRAY));
|
||||
}
|
||||
|
||||
private boolean endsWithSanitized (String other) {
|
||||
if (!path.endsWith(other))
|
||||
return false;
|
||||
final int start = path.length() - other.length();
|
||||
return start == 0 || '/' == path.charAt(start - 1);
|
||||
}
|
||||
|
||||
@SuppressWarnings("NullableProblems")
|
||||
@Override
|
||||
public Iterator<Path> iterator () {
|
||||
final short[] offsets = getNameOffsets();
|
||||
final int count = offsets.length;
|
||||
if (count == 0)
|
||||
return Utils.emptyIterator();
|
||||
return new NameIterator<Path>(path, offsets, count) {
|
||||
@Override
|
||||
protected Path next (String path, int start, int end) {
|
||||
return fs.getPathSanitized(path.substring(start, end));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final Iterator<CharSequence> charSequenceIterator () {
|
||||
final short[] offsets = getNameOffsets();
|
||||
final int count = offsets.length;
|
||||
if (count == 0)
|
||||
return Utils.emptyIterator();
|
||||
return new NameIterator<CharSequence>(path, offsets, count) {
|
||||
@Override
|
||||
protected CharSequence next (String path, int start, int end) {
|
||||
return path.subSequence(start, end);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public final Iterator<String> stringIterator () {
|
||||
final short[] offsets = getNameOffsets();
|
||||
final int count = offsets.length;
|
||||
if (count == 0)
|
||||
return Utils.emptyIterator();
|
||||
return new NameIterator<String>(path, offsets, count) {
|
||||
@Override
|
||||
protected String next (String path, int start, int end) {
|
||||
return path.substring(start, end);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME: horrid
|
||||
@Override
|
||||
public Path normalize () {
|
||||
CharSequence[] names = Utils.EMPTY_CHAR_SEQUENCE_ARRAY;
|
||||
int end = 0;
|
||||
for (Iterator<CharSequence> i = charSequenceIterator(); i.hasNext();) {
|
||||
final CharSequence n = i.next();
|
||||
if ("..".contentEquals(n) && end > 0)
|
||||
--end;
|
||||
else if (!".".contentEquals(n)){
|
||||
if (names.length == end)
|
||||
names = Arrays.copyOf(names, Math.max(end*2, 8));
|
||||
names[end++] = n;
|
||||
}
|
||||
}
|
||||
if (end == 0)
|
||||
return isAbsolute() ? fs.getRootDirectory() : fs.getEmptyDirectory();
|
||||
return fs.getPath(join(names, 0, end, isAbsolute() ? "/" : ""));
|
||||
}
|
||||
|
||||
// FIXME: horrid
|
||||
@Override
|
||||
public Path relativize (Path other) {
|
||||
checkPath(other);
|
||||
if (isAbsolute() != other.isAbsolute())
|
||||
throw new IllegalArgumentException("Absolute vs relative");
|
||||
//if (equals(that))
|
||||
// return fs.getEmptyDirectory();
|
||||
final Iterator<CharSequence> bi = charSequenceIterator();
|
||||
final Iterator<CharSequence> ci = ((UnixPath)other).charSequenceIterator();
|
||||
final RelativizeHelper h = new RelativizeHelper();
|
||||
CharSequence bn, cn = null;
|
||||
out: while (bi.hasNext() && ci.hasNext()) {
|
||||
while (".".contentEquals(bn = bi.next()))
|
||||
if (!bi.hasNext()) break out;
|
||||
while (".".contentEquals(cn = ci.next()))
|
||||
if (!ci.hasNext()) break out;
|
||||
if (!Utils.contentEquals(bn, cn)) {
|
||||
h.base(bn);
|
||||
break;
|
||||
}
|
||||
h.add(bn);
|
||||
++h.start;
|
||||
cn = null;
|
||||
}
|
||||
while (bi.hasNext())
|
||||
if (!".".contentEquals(bn = bi.next())) h.base(bn);
|
||||
if (cn != null && !".".contentEquals(cn))
|
||||
h.child(cn);
|
||||
while (ci.hasNext())
|
||||
if (!".".contentEquals(cn = ci.next())) h.child(cn);
|
||||
return fs.getPathSanitized(join(h.names, h.start, h.end, ""));
|
||||
}
|
||||
|
||||
private static final class RelativizeHelper {
|
||||
public CharSequence[] names = new CharSequence[8];
|
||||
public int start = 0, end = 0;
|
||||
public void base (CharSequence value) {
|
||||
if ("..".contentEquals(value) && start > 0)
|
||||
--start;
|
||||
else
|
||||
add("..");
|
||||
}
|
||||
public void child (CharSequence value) {
|
||||
if ("..".contentEquals(value) && start < end && !"..".contentEquals(names[end - 1]))
|
||||
--end;
|
||||
else
|
||||
add(value);
|
||||
}
|
||||
public void add (CharSequence value) {
|
||||
if (names.length == end)
|
||||
names = Arrays.copyOf(names, end*2);
|
||||
names[end++] = value;
|
||||
}
|
||||
} // class RelativizeHelper
|
||||
|
||||
@Override
|
||||
public Path resolve (Path other) {
|
||||
checkPath(other);
|
||||
if (other.isAbsolute())
|
||||
return other;
|
||||
if (((UnixPath)other).isEmpty())
|
||||
return this;
|
||||
return fs.getPath(path, ((UnixPath)other).path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path resolve (String other) {
|
||||
if (other.startsWith("/"))
|
||||
return fs.getPath(other);
|
||||
if (other.isEmpty())
|
||||
return this;
|
||||
return fs.getPath(path, other);
|
||||
}
|
||||
|
||||
public Path resolveSibling (Path other) {
|
||||
checkPath(other);
|
||||
final Path parent = getParent();
|
||||
return (parent != null) ? parent.resolve(other) : other;
|
||||
}
|
||||
|
||||
public Path resolveSibling (String other) {
|
||||
final Path parent = getParent();
|
||||
return (parent != null) ? parent.resolve(other) : fs.getPath(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path toAbsolutePath () {
|
||||
return isAbsolute() ? this : fs.getCurrentDirectory().resolve(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path toRealPath (LinkOption... options) throws IOException {
|
||||
return fs.toRealPath(this, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public File toFile () {
|
||||
return new File(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI toUri () {
|
||||
try {
|
||||
return new URI(fs.provider().getScheme(), toAbsolutePath().toString(), null);
|
||||
}
|
||||
catch (Throwable t) {
|
||||
throw new IllegalStateException(t); // shouldn't happen
|
||||
}
|
||||
}
|
||||
|
||||
private static final WatchEvent.Modifier[] EMPTY_MODIFIER_ARRAY = new WatchEvent.Modifier[0];
|
||||
|
||||
@Override
|
||||
public final WatchKey register (WatchService service, WatchEvent.Kind<?>... kinds) throws IOException {
|
||||
return register(service, kinds, EMPTY_MODIFIER_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchKey register (WatchService service, WatchEvent.Kind<?>[] kinds, WatchEvent.Modifier... modifiers) throws IOException {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
private static void checkPath (Path path) {
|
||||
if (!(path instanceof UnixPath))
|
||||
throw (path == null) ? new NullPointerException() : new ProviderMismatchException();
|
||||
}
|
||||
|
||||
/*
|
||||
private static String sanitize (String path) {
|
||||
return sanitize(path, Utils.EMPTY_STRING_ARRAY);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Removes double and tail slash
|
||||
*/
|
||||
static String sanitize (String first, String[] more) {
|
||||
CharSequence parent = "";
|
||||
for (int i = -1, count = more.length;;) {
|
||||
parent = sanitizeChunk(parent, first);
|
||||
if (++i == count)
|
||||
break;
|
||||
first = more[i];
|
||||
}
|
||||
return parent.toString();
|
||||
}
|
||||
|
||||
private static CharSequence sanitizeChunk (CharSequence parent, String path) {
|
||||
int end = path.length();
|
||||
if (end == 0)
|
||||
return parent; // empty
|
||||
while (end > 0 && '/' == path.charAt(end - 1)) --end;
|
||||
if (end == 0)
|
||||
return (parent.length() == 0) ? "/" : parent; // root
|
||||
// At this point it's neither empty nor root.
|
||||
int start = 0;
|
||||
while (start < end && '/' == path.charAt(start)) ++start;
|
||||
final int parentLength = parent.length();
|
||||
int index = path.indexOf("//", start + 1);
|
||||
if (index == -1 || index >= end) {
|
||||
// no //
|
||||
if (parentLength == 0) {
|
||||
if (start > 0)
|
||||
--start; // keep initial /
|
||||
return path.substring(start, end);
|
||||
}
|
||||
if (parentLength == 1 && '/' == parent.charAt(0)) {
|
||||
// root parent
|
||||
if (start > 0)
|
||||
return path.substring(start - 1, end);
|
||||
else
|
||||
return new StringBuilder("/").append(path, start, end);
|
||||
}
|
||||
final StringBuilder sb = (parent instanceof StringBuilder) ? (StringBuilder)parent : new StringBuilder(parent);
|
||||
return sb.append('/').append(path, start, end);
|
||||
}
|
||||
// found //
|
||||
final StringBuilder sb = (parent instanceof StringBuilder) ? (StringBuilder)parent : new StringBuilder(parent);
|
||||
if (parentLength == 0) {
|
||||
if (start > 0)
|
||||
--start; // keep initial /
|
||||
}
|
||||
else if (parentLength != 1 || '/' != parent.charAt(0)) {
|
||||
// not root parent
|
||||
sb.append('/');
|
||||
}
|
||||
while (++index < end) {
|
||||
sb.append(path, start, index);
|
||||
while (index < end && '/' == path.charAt(index)) ++index;
|
||||
start = index;
|
||||
}
|
||||
return sb.append(path, start, index);
|
||||
}
|
||||
|
||||
private static String join (CharSequence[] cs, int start, int end, String prefix) {
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
while (start < end) {
|
||||
sb.append(prefix).append(cs[start++]);
|
||||
prefix = "/";
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static abstract class NameIterator<T> implements Iterator<T> {
|
||||
|
||||
private final String path;
|
||||
private final short[] offsets;
|
||||
private final int end;
|
||||
private int index;
|
||||
|
||||
public NameIterator (String path, short[] offsets, int count) {
|
||||
this.path = path;
|
||||
this.offsets = offsets;
|
||||
this.end = count - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean hasNext () {
|
||||
return index <= end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final T next () {
|
||||
final int i = index;
|
||||
if (i > end)
|
||||
throw new NoSuchElementException();
|
||||
++index;
|
||||
return next(
|
||||
path,
|
||||
offsets[i] & 0xFFFF,
|
||||
(i < end) ? (offsets[i + 1] & 0xFFFF) - 1 : path.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void remove () {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
protected abstract T next (String path, int start, int end);
|
||||
|
||||
} // class NameIterator
|
||||
|
||||
}
|
@ -17,7 +17,7 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPluginLoader
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
|
||||
/**
|
||||
* [数据对象][PluginData] 存储仓库.
|
||||
|
@ -13,12 +13,13 @@ import net.mamoe.mirai.utils.SecretsProtection
|
||||
import net.mamoe.mirai.utils.lateinitMutableProperty
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.DataOutputStream
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
import java.util.*
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.isRegularFile
|
||||
import kotlin.io.path.readBytes
|
||||
import kotlin.io.path.writeBytes
|
||||
import com.llamalab.safs.kotlin.io.path.createDirectories
|
||||
import com.llamalab.safs.kotlin.io.path.readBytes
|
||||
import com.llamalab.safs.kotlin.io.path.writeBytes
|
||||
|
||||
import com.llamalab.safs.kotlin.io.path.isRegularFile
|
||||
|
||||
internal class ConsoleSecretsCalculator(
|
||||
private val file: Path,
|
||||
|
@ -24,7 +24,7 @@ import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.currentTimeMillis
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
|
||||
@Suppress("RedundantVisibilityModifier") // might be public in the future
|
||||
internal open class MultiFilePluginDataStorageImpl(
|
||||
|
@ -31,7 +31,7 @@ import net.mamoe.mirai.console.util.ConsoleExperimentalApi
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.yamlkt.Yaml
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
@ -41,7 +41,7 @@ import net.mamoe.mirai.utils.MiraiLogger
|
||||
import net.mamoe.mirai.utils.safeCast
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
import java.util.*
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
@ -19,7 +19,7 @@ import net.mamoe.mirai.console.plugin.jvm.JvmPluginDescription
|
||||
import net.mamoe.mirai.utils.MiraiLogger
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
internal abstract class NotYetLoadedJvmPlugin(
|
||||
|
@ -30,7 +30,7 @@ import net.mamoe.mirai.console.plugin.name
|
||||
import net.mamoe.mirai.console.util.SemVersion
|
||||
import net.mamoe.mirai.utils.*
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
import java.util.concurrent.CopyOnWriteArrayList
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
|
@ -24,7 +24,7 @@ import java.io.FileOutputStream
|
||||
import java.io.PrintStream
|
||||
import java.lang.management.ManagementFactory
|
||||
import java.lang.reflect.Method
|
||||
import java.nio.file.Paths
|
||||
import com.llamalab.safs.Paths
|
||||
import java.time.Instant
|
||||
import java.time.ZoneOffset
|
||||
import java.util.*
|
||||
@ -33,7 +33,7 @@ import java.util.concurrent.Executors
|
||||
import java.util.concurrent.ThreadFactory
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
import kotlin.io.path.writeText
|
||||
import com.llamalab.safs.kotlin.io.path.writeText
|
||||
|
||||
internal object ShutdownDaemon {
|
||||
@Suppress("RemoveRedundantQualifierName")
|
||||
@ -110,6 +110,7 @@ internal object ShutdownDaemon {
|
||||
bridge.mainLogger.debug { "SHUTDOWN DAEMON STARTED........." }
|
||||
}
|
||||
|
||||
@OptIn(ConsoleInternalApi::class)
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
fun dumpCrashReport(saveError: Boolean) {
|
||||
val isAndroidSystem = kotlin.runCatching { Class.forName("android.util.Log") }.isSuccess
|
||||
|
@ -13,7 +13,7 @@ import net.mamoe.mirai.console.data.PluginConfig
|
||||
import net.mamoe.mirai.console.data.PluginData
|
||||
import net.mamoe.mirai.console.plugin.jvm.JvmPlugin
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
|
||||
|
||||
/**
|
||||
|
@ -17,7 +17,7 @@ import net.mamoe.mirai.console.plugin.description.PluginDescription
|
||||
import net.mamoe.mirai.console.plugin.loader.PluginLoader
|
||||
import net.mamoe.mirai.utils.NotStableForInheritance
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import com.llamalab.safs.Path
|
||||
|
||||
/**
|
||||
* 插件管理器.
|
||||
|
Loading…
Reference in New Issue
Block a user