/*
* Copyright 2015 Red Hat, Inc. and/or its affiliates.
*
* 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 org.uberfire.io.impl;
import java.lang.reflect.Constructor;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.uberfire.io.IOService;
import org.uberfire.io.IOWatchService;
import org.uberfire.java.nio.IOException;
import org.uberfire.java.nio.base.AbstractBasicFileAttributeView;
import org.uberfire.java.nio.base.AttrHolder;
import org.uberfire.java.nio.base.NeedsPreloadedAttrs;
import org.uberfire.java.nio.base.Properties;
import org.uberfire.java.nio.base.dotfiles.DotFileOption;
import org.uberfire.java.nio.channels.SeekableByteChannel;
import org.uberfire.java.nio.file.AtomicMoveNotSupportedException;
import org.uberfire.java.nio.file.CopyOption;
import org.uberfire.java.nio.file.DeleteOption;
import org.uberfire.java.nio.file.DirectoryNotEmptyException;
import org.uberfire.java.nio.file.FileAlreadyExistsException;
import org.uberfire.java.nio.file.Files;
import org.uberfire.java.nio.file.NoSuchFileException;
import org.uberfire.java.nio.file.OpenOption;
import org.uberfire.java.nio.file.Path;
import org.uberfire.java.nio.file.attribute.FileAttribute;
import org.uberfire.java.nio.file.attribute.FileAttributeView;
import static org.uberfire.commons.validation.PortablePreconditions.checkNotEmpty;
import static org.uberfire.commons.validation.PortablePreconditions.checkNotNull;
import static org.uberfire.java.nio.base.dotfiles.DotFileUtils.buildDotFile;
import static org.uberfire.java.nio.base.dotfiles.DotFileUtils.consolidate;
import static org.uberfire.java.nio.base.dotfiles.DotFileUtils.dot;
import static org.uberfire.java.nio.file.StandardCopyOption.REPLACE_EXISTING;
public class IOServiceDotFileImpl
extends AbstractIOService
implements IOService {
public IOServiceDotFileImpl() {
super();
}
public IOServiceDotFileImpl(final String serviceId) {
super(serviceId);
}
public IOServiceDotFileImpl(final IOWatchService watchService) {
super(watchService);
}
public IOServiceDotFileImpl(final String serviceId,
final IOWatchService watchService) {
super(serviceId,
watchService);
}
@Override
public void delete(final Path path,
final DeleteOption... options)
throws IllegalArgumentException, NoSuchFileException, DirectoryNotEmptyException,
IOException, SecurityException {
Files.delete(path,
options);
try {
Files.deleteIfExists(dot(path),
options);
} catch (Exception ex) {
}
if (path instanceof AttrHolder) {
((AttrHolder) path).getAttrStorage().clear();
}
}
@Override
public boolean deleteIfExists(final Path path,
final DeleteOption... options)
throws IllegalArgumentException, DirectoryNotEmptyException, IOException, SecurityException {
final boolean result = Files.deleteIfExists(path,
options);
try {
Files.deleteIfExists(dot(path),
options);
} catch (Exception ex) {
}
if (path instanceof AttrHolder) {
((AttrHolder) path).getAttrStorage().clear();
}
return result;
}
@Override
public SeekableByteChannel newByteChannel(final Path path,
final Set<? extends OpenOption> options,
final FileAttribute<?>... attrs)
throws IllegalArgumentException, UnsupportedOperationException,
FileAlreadyExistsException, IOException, SecurityException {
checkNotNull("path",
path);
final Properties properties = new Properties();
if (exists(dot(path))) {
properties.load(newInputStream(dot(path)));
}
final FileAttribute<?>[] allAttrs = consolidate(properties,
attrs);
final SeekableByteChannel result = Files.newByteChannel(path,
buildOptions(options),
allAttrs);
if (isFileScheme(path)) {
buildDotFile(path,
newOutputStream(dot(path)),
allAttrs);
}
return result;
}
@Override
public Path createDirectory(final Path dir,
final FileAttribute<?>... attrs)
throws IllegalArgumentException, UnsupportedOperationException, FileAlreadyExistsException,
IOException, SecurityException {
return internalCreateDirectory(dir,
false,
attrs);
}
@Override
public Path createDirectories(final Path dir,
final FileAttribute<?>... attrs)
throws UnsupportedOperationException, FileAlreadyExistsException,
IOException, SecurityException {
final Path result = Files.createDirectories(dir,
attrs);
buildDotFile(dir,
newOutputStream(dot(dir)),
attrs);
return result;
}
@Override
public Path copy(final Path source,
final Path target,
final CopyOption... options)
throws UnsupportedOperationException, FileAlreadyExistsException,
DirectoryNotEmptyException, IOException, SecurityException {
if (Files.exists(dot(source))) {
Files.copy(dot(source),
dot(target),
forceBuildOptions(options));
} else if (Files.exists(dot(target))) {
Files.delete(dot(target));
}
final Path result = Files.copy(source,
target,
options);
return result;
}
@Override
public Path move(final Path source,
final Path target,
final CopyOption... options)
throws UnsupportedOperationException, FileAlreadyExistsException,
DirectoryNotEmptyException, AtomicMoveNotSupportedException, IOException, SecurityException {
if (Files.exists(dot(source))) {
Files.move(dot(source),
dot(target),
forceBuildOptions(options));
} else if (Files.exists(dot(target))) {
Files.delete(dot(target));
}
final Path result = Files.move(source,
target,
options);
return result;
}
@Override
public <V extends FileAttributeView> V getFileAttributeView(final Path path,
final Class<V> type)
throws IllegalArgumentException {
final V value = Files.getFileAttributeView(path,
type);
if (value == null && path instanceof AttrHolder) {
final AttrHolder holder = ((AttrHolder) path);
final V holderView = holder.getAttrView(type);
if (holderView == null && AbstractBasicFileAttributeView.class.isAssignableFrom(type)) {
return (V) newView(holder,
(Class<? extends AbstractBasicFileAttributeView>) type);
}
return holderView;
}
return value;
}
@Override
public Map<String, Object> readAttributes(final Path path,
final String attributes)
throws UnsupportedOperationException, NoSuchFileException, IllegalArgumentException,
IOException, SecurityException {
checkNotNull("path",
path);
checkNotEmpty("attributes",
attributes);
final Properties original = new Properties(Files.readAttributes(path,
attributes));
if (attributes.equals("*") && exists(dot(path))) {
boolean isAttrHolder = path instanceof AttrHolder;
if (isAttrHolder && ((AttrHolder) path).getAttrStorage().getContent().size() > 0) {
return ((AttrHolder) path).getAttrStorage().getAllContent();
}
final Properties content = new Properties();
content.load(newInputStream(dot(path)));
content.putAll(original);
if (isAttrHolder) {
((AttrHolder) path).getAttrStorage().loadContent(content);
}
return content;
}
return original;
}
@Override
public Path setAttributes(final Path path,
final FileAttribute<?>... attrs)
throws UnsupportedOperationException, IllegalArgumentException, ClassCastException, IOException, SecurityException {
checkNotNull("path",
path);
if (Files.isDirectory(path)) {
return internalCreateDirectory(path,
true,
attrs);
}
return write(path,
readAllBytes(path),
Collections.<OpenOption>emptySet(),
attrs);
}
@Override
public Object getAttribute(final Path path,
final String attribute)
throws UnsupportedOperationException, IllegalArgumentException, IOException, SecurityException {
checkNotNull("path",
path);
Object value;
try {
value = Files.getAttribute(path,
attribute);
} catch (UnsupportedOperationException ex) {
value = null;
}
if (value == null && path instanceof AttrHolder) {
final AttrHolder holder = ((AttrHolder) path);
final String[] attr = split(attribute);
if (holder.getAttrStorage().getContent().isEmpty()) {
loadDotFile(path);
}
return holder.getAttrStorage().getAllContent().get(attr[1]);
}
return value;
}
@Override
protected Set<? extends OpenOption> buildOptions(final Set<? extends OpenOption> options,
final OpenOption... others) {
return new HashSet<OpenOption>(options) {{
add(new DotFileOption());
if (others != null) {
for (final OpenOption other : others) {
add(other);
}
}
}};
}
protected CopyOption[] buildOptions(final CopyOption... options) {
final CopyOption[] result = new CopyOption[options.length + 1];
System.arraycopy(options,
0,
result,
0,
options.length);
result[result.length - 1] = new DotFileOption();
return result;
}
protected CopyOption[] forceBuildOptions(final CopyOption[] options) {
final CopyOption[] result = new CopyOption[options.length + 1];
System.arraycopy(options,
0,
result,
0,
options.length);
result[result.length - 1] = REPLACE_EXISTING;
return result;
}
protected boolean isFileScheme(final Path path) {
if (path == null || path.getFileSystem() == null || path.getFileSystem().provider() == null) {
return false;
}
return path.getFileSystem().provider().getScheme().equals("file");
}
protected void loadDotFile(final Path path) {
final Properties content = new Properties();
content.load(newInputStream(dot(path)));
if (path instanceof AttrHolder) {
((AttrHolder) path).getAttrStorage().loadContent(content);
}
}
protected <V extends AbstractBasicFileAttributeView> V newView(final AttrHolder holder,
final Class<V> type) {
if (NeedsPreloadedAttrs.class.isAssignableFrom(type) && holder.getAttrStorage().getContent().size() == 0) {
readAttributes((Path) holder);
}
try {
final Constructor<V> constructor = (Constructor<V>) type.getConstructors()[0];
final V view = constructor.newInstance(holder);
holder.addAttrView(view);
return view;
} catch (final Exception e) {
}
return null;
}
protected Path internalCreateDirectory(final Path dir,
final boolean skipAlreadyExistsException,
final FileAttribute<?>... attrs)
throws IllegalArgumentException, UnsupportedOperationException, FileAlreadyExistsException,
IOException, SecurityException {
checkNotNull("dir",
dir);
FileAttribute<?>[] allAttrs = attrs;
try {
Files.createDirectory(dir,
attrs);
} catch (final FileAlreadyExistsException ex) {
final Properties properties = new Properties();
if (exists(dot(dir))) {
properties.load(newInputStream(dot(dir)));
}
allAttrs = consolidate(properties,
attrs);
if (!skipAlreadyExistsException) {
throw ex;
}
}
buildDotFile(dir,
newOutputStream(dot(dir)),
allAttrs);
return dir;
}
protected String[] split(final String attribute) {
final String[] s = new String[2];
final int pos = attribute.indexOf(':');
if (pos == -1) {
s[0] = "basic";
s[1] = attribute;
} else {
s[0] = attribute.substring(0,
pos);
s[1] = (pos == attribute.length()) ? "" : attribute.substring(pos + 1);
}
return s;
}
@Override
public int priority() {
return 10;
}
}