X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Facr%2Ffs%2FFsContent.java;h=bbd77b2217c729ee5880d722a6dbf94804f60ae1;hb=55d1a78150b6be0004f6bcb28703bcdd6daf55a1;hp=b8bbddb5016d13c2b4136e8e525c9c6858958409;hpb=c615307d7b87bcb260d8a9f402c6e0a880862f38;p=lgpl%2Fargeo-commons.git
diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java
index b8bbddb50..bbd77b221 100644
--- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java
+++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java
@@ -1,6 +1,9 @@
package org.argeo.cms.acr.fs;
+import java.io.Closeable;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -8,10 +11,12 @@ import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.nio.file.attribute.UserDefinedFileAttributeView;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
@@ -23,11 +28,14 @@ import org.argeo.api.acr.ContentName;
import org.argeo.api.acr.ContentResourceException;
import org.argeo.api.acr.CrName;
import org.argeo.api.acr.NamespaceUtils;
-import org.argeo.api.acr.spi.AbstractContent;
+import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.cms.acr.AbstractContent;
+import org.argeo.cms.acr.ContentUtils;
import org.argeo.util.FsUtils;
+/** Content persisted as a filesystem {@link Path}. */
public class FsContent extends AbstractContent implements ProvidedContent {
final static String USER_ = "user:";
@@ -46,22 +54,32 @@ public class FsContent extends AbstractContent implements ProvidedContent {
POSIX_KEYS.put(CrName.PERMISSIONS.get(), "posix:permissions");
}
- private final ProvidedSession session;
private final FsContentProvider provider;
private final Path path;
private final boolean isRoot;
private final QName name;
protected FsContent(ProvidedSession session, FsContentProvider contentProvider, Path path) {
- this.session = session;
+ super(session);
this.provider = contentProvider;
this.path = path;
- this.isRoot = contentProvider.isRoot(path);
+ this.isRoot = contentProvider.isMountRoot(path);
// TODO check file names with ':' ?
- if (isRoot)
- this.name = CrName.ROOT.get();
- else {
- QName providerName = NamespaceUtils.parsePrefixedName(provider, path.getFileName().toString());
+ if (isRoot) {
+ String mountPath = provider.getMountPath();
+ if (mountPath != null && !mountPath.equals("/")) {
+ Content mountPoint = session.getMountPoint(mountPath);
+ this.name = mountPoint.getName();
+ } else {
+ this.name = CrName.ROOT.get();
+ }
+ } else {
+
+ // TODO should we support prefixed name for known types?
+ // QName providerName = NamespaceUtils.parsePrefixedName(provider,
+ // path.getFileName().toString());
+ QName providerName = new QName(path.getFileName().toString());
+ // TODO remove extension if mounted?
this.name = new ContentName(providerName, session);
}
}
@@ -83,12 +101,32 @@ public class FsContent extends AbstractContent implements ProvidedContent {
* ATTRIBUTES
*/
+ @SuppressWarnings("unchecked")
@Override
public Optional get(QName key, Class clss) {
Object value;
try {
// We need to add user: when accessing via Files#getAttribute
- value = Files.getAttribute(path, toFsAttributeKey(key));
+
+ if (POSIX_KEYS.containsKey(key)) {
+ value = Files.getAttribute(path, toFsAttributeKey(key));
+ } else {
+ UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path,
+ UserDefinedFileAttributeView.class);
+ String prefixedName = NamespaceUtils.toPrefixedName(provider, key);
+ if (!udfav.list().contains(prefixedName))
+ return Optional.empty();
+ ByteBuffer buf = ByteBuffer.allocate(udfav.size(prefixedName));
+ udfav.read(prefixedName, buf);
+ buf.flip();
+ if (buf.hasArray())
+ value = buf.array();
+ else {
+ byte[] arr = new byte[buf.remaining()];
+ buf.get(arr);
+ value = arr;
+ }
+ }
} catch (IOException e) {
throw new ContentResourceException("Cannot retrieve attribute " + key + " for " + path, e);
}
@@ -122,7 +160,7 @@ public class FsContent extends AbstractContent implements ProvidedContent {
try {
for (String name : udfav.list()) {
QName providerName = NamespaceUtils.parsePrefixedName(provider, name);
- QName sessionName = new ContentName(providerName, session);
+ QName sessionName = new ContentName(providerName, getSession());
result.add(sessionName);
}
} catch (IOException e) {
@@ -148,7 +186,7 @@ public class FsContent extends AbstractContent implements ProvidedContent {
UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
ByteBuffer bb = ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
try {
- int size = udfav.write(NamespaceUtils.toPrefixedName(provider, key), bb);
+ udfav.write(NamespaceUtils.toPrefixedName(provider, key), bb);
} catch (IOException e) {
throw new ContentResourceException("Cannot delete attribute " + key + " for " + path, e);
}
@@ -169,7 +207,19 @@ public class FsContent extends AbstractContent implements ProvidedContent {
public Iterator iterator() {
if (Files.isDirectory(path)) {
try {
- return Files.list(path).map((p) -> (Content) new FsContent(this, p)).iterator();
+ return Files.list(path).map((p) -> {
+ FsContent fsContent = new FsContent(this, p);
+ Optional isMount = fsContent.get(CrName.MOUNT.get(), String.class);
+ if (isMount.orElse("false").equals("true")) {
+ QName[] classes = null;
+ ContentProvider contentProvider = getSession().getRepository()
+ .getMountContentProvider(fsContent, false, classes);
+ Content mountedContent = contentProvider.get(getSession(), "");
+ return mountedContent;
+ } else {
+ return (Content) fsContent;
+ }
+ }).iterator();
} catch (IOException e) {
throw new ContentResourceException("Cannot list " + path, e);
}
@@ -180,6 +230,7 @@ public class FsContent extends AbstractContent implements ProvidedContent {
@Override
public Content add(QName name, QName... classes) {
+ FsContent fsContent;
try {
Path newPath = path.resolve(NamespaceUtils.toPrefixedName(provider, name));
if (ContentName.contains(classes, CrName.COLLECTION.get()))
@@ -190,10 +241,21 @@ public class FsContent extends AbstractContent implements ProvidedContent {
// for(ContentClass clss:classes) {
// Files.setAttribute(newPath, name, newPath, null)
// }
- return new FsContent(this, newPath);
+ fsContent = new FsContent(this, newPath);
} catch (IOException e) {
throw new ContentResourceException("Cannot create new content", e);
}
+
+ if (getSession().getRepository().shouldMount(classes)) {
+ ContentProvider contentProvider = getSession().getRepository().getMountContentProvider(fsContent, true,
+ classes);
+ Content mountedContent = contentProvider.get(getSession(), "");
+ fsContent.put(CrName.MOUNT.get(), "true");
+ return mountedContent;
+
+ } else {
+ return fsContent;
+ }
}
@Override
@@ -203,19 +265,58 @@ public class FsContent extends AbstractContent implements ProvidedContent {
@Override
public Content getParent() {
- if (isRoot)
- return null;// TODO deal with mounts
+ if (isRoot) {
+ String mountPath = provider.getMountPath();
+ if (mountPath == null || mountPath.equals("/"))
+ return null;
+ String[] parent = ContentUtils.getParentPath(mountPath);
+ return getSession().get(parent[0]);
+ }
return new FsContent(this, path.getParent());
}
+ @SuppressWarnings("unchecked")
+ @Override
+ public C open(Class clss) throws IOException, IllegalArgumentException {
+ if (InputStream.class.isAssignableFrom(clss)) {
+ if (Files.isDirectory(path))
+ throw new UnsupportedOperationException("Cannot open " + path + " as stream, since it is a directory");
+ return (C) Files.newInputStream(path);
+ } else if (OutputStream.class.isAssignableFrom(clss)) {
+ if (Files.isDirectory(path))
+ throw new UnsupportedOperationException("Cannot open " + path + " as stream, since it is a directory");
+ return (C) Files.newOutputStream(path);
+ }
+ return super.open(clss);
+ }
+
/*
- * ACCESSORS
+ * MOUNT MANAGEMENT
*/
@Override
- public ProvidedSession getSession() {
- return session;
+ public ProvidedContent getMountPoint(String relativePath) {
+ Path childPath = path.resolve(relativePath);
+ // TODO check that it is a mount
+ return new FsContent(this, childPath);
}
+ /*
+ * TYPING
+ */
+
+ @Override
+ public List getContentClasses() {
+ List res = new ArrayList<>();
+ if (Files.isDirectory(path))
+ res.add(CrName.COLLECTION.get());
+ // TODO add other types
+ return res;
+ }
+
+ /*
+ * ACCESSORS
+ */
+
@Override
public FsContentProvider getProvider() {
return provider;