1 package org
.argeo
.cms
.acr
.fs
;
3 import java
.io
.IOException
;
4 import java
.nio
.ByteBuffer
;
5 import java
.nio
.charset
.StandardCharsets
;
6 import java
.nio
.file
.Files
;
7 import java
.nio
.file
.Path
;
8 import java
.nio
.file
.attribute
.FileTime
;
9 import java
.nio
.file
.attribute
.UserDefinedFileAttributeView
;
10 import java
.time
.Instant
;
11 import java
.util
.Collections
;
12 import java
.util
.HashMap
;
13 import java
.util
.HashSet
;
14 import java
.util
.Iterator
;
16 import java
.util
.Optional
;
19 import javax
.xml
.namespace
.QName
;
21 import org
.argeo
.api
.acr
.Content
;
22 import org
.argeo
.api
.acr
.ContentName
;
23 import org
.argeo
.api
.acr
.ContentResourceException
;
24 import org
.argeo
.api
.acr
.CrName
;
25 import org
.argeo
.api
.acr
.NamespaceUtils
;
26 import org
.argeo
.api
.acr
.spi
.AbstractContent
;
27 import org
.argeo
.api
.acr
.spi
.ProvidedContent
;
28 import org
.argeo
.api
.acr
.spi
.ProvidedSession
;
29 import org
.argeo
.util
.FsUtils
;
31 public class FsContent
extends AbstractContent
implements ProvidedContent
{
32 final static String USER_
= "user:";
34 private static final Map
<QName
, String
> BASIC_KEYS
;
35 private static final Map
<QName
, String
> POSIX_KEYS
;
37 BASIC_KEYS
= new HashMap
<>();
38 BASIC_KEYS
.put(CrName
.CREATION_TIME
.get(), "basic:creationTime");
39 BASIC_KEYS
.put(CrName
.LAST_MODIFIED_TIME
.get(), "basic:lastModifiedTime");
40 BASIC_KEYS
.put(CrName
.SIZE
.get(), "basic:size");
41 BASIC_KEYS
.put(CrName
.FILE_KEY
.get(), "basic:fileKey");
43 POSIX_KEYS
= new HashMap
<>(BASIC_KEYS
);
44 POSIX_KEYS
.put(CrName
.OWNER
.get(), "owner:owner");
45 POSIX_KEYS
.put(CrName
.GROUP
.get(), "posix:group");
46 POSIX_KEYS
.put(CrName
.PERMISSIONS
.get(), "posix:permissions");
49 private final ProvidedSession session
;
50 private final FsContentProvider provider
;
51 private final Path path
;
52 private final boolean isRoot
;
53 private final QName name
;
55 protected FsContent(ProvidedSession session
, FsContentProvider contentProvider
, Path path
) {
56 this.session
= session
;
57 this.provider
= contentProvider
;
59 this.isRoot
= contentProvider
.isRoot(path
);
60 // TODO check file names with ':' ?
62 this.name
= CrName
.ROOT
.get();
64 QName providerName
= NamespaceUtils
.parsePrefixedName(provider
, path
.getFileName().toString());
65 this.name
= new ContentName(providerName
, session
);
69 protected FsContent(FsContent context
, Path path
) {
70 this(context
.getSession(), context
.getProvider(), path
);
73 private boolean isPosix() {
74 return path
.getFileSystem().supportedFileAttributeViews().contains("posix");
78 public QName
getName() {
87 public <A
> Optional
<A
> get(QName key
, Class
<A
> clss
) {
90 // We need to add user: when accessing via Files#getAttribute
91 value
= Files
.getAttribute(path
, toFsAttributeKey(key
));
92 } catch (IOException e
) {
93 throw new ContentResourceException("Cannot retrieve attribute " + key
+ " for " + path
, e
);
96 if (value
instanceof FileTime
) {
97 if (clss
.isAssignableFrom(FileTime
.class))
99 Instant instant
= ((FileTime
) value
).toInstant();
100 if (Object
.class.isAssignableFrom(clss
)) {// plain object requested
103 // TODO perform trivial file conversion to other formats
105 if (value
instanceof byte[]) {
106 res
= (A
) new String((byte[]) value
, StandardCharsets
.UTF_8
);
111 } catch (ClassCastException e
) {
112 return Optional
.empty();
114 return Optional
.of(res
);
118 protected Iterable
<QName
> keys() {
119 Set
<QName
> result
= new HashSet
<>(isPosix() ? POSIX_KEYS
.keySet() : BASIC_KEYS
.keySet());
120 UserDefinedFileAttributeView udfav
= Files
.getFileAttributeView(path
, UserDefinedFileAttributeView
.class);
123 for (String name
: udfav
.list()) {
124 QName providerName
= NamespaceUtils
.parsePrefixedName(provider
, name
);
125 QName sessionName
= new ContentName(providerName
, session
);
126 result
.add(sessionName
);
128 } catch (IOException e
) {
129 throw new ContentResourceException("Cannot list attributes for " + path
, e
);
136 protected void removeAttr(QName key
) {
137 UserDefinedFileAttributeView udfav
= Files
.getFileAttributeView(path
, UserDefinedFileAttributeView
.class);
139 udfav
.delete(NamespaceUtils
.toPrefixedName(provider
, key
));
140 } catch (IOException e
) {
141 throw new ContentResourceException("Cannot delete attribute " + key
+ " for " + path
, e
);
146 public Object
put(QName key
, Object value
) {
147 Object previous
= get(key
);
148 UserDefinedFileAttributeView udfav
= Files
.getFileAttributeView(path
, UserDefinedFileAttributeView
.class);
149 ByteBuffer bb
= ByteBuffer
.wrap(value
.toString().getBytes(StandardCharsets
.UTF_8
));
151 int size
= udfav
.write(NamespaceUtils
.toPrefixedName(provider
, key
), bb
);
152 } catch (IOException e
) {
153 throw new ContentResourceException("Cannot delete attribute " + key
+ " for " + path
, e
);
158 protected String
toFsAttributeKey(QName key
) {
159 if (POSIX_KEYS
.containsKey(key
))
160 return POSIX_KEYS
.get(key
);
162 return USER_
+ NamespaceUtils
.toPrefixedName(provider
, key
);
169 public Iterator
<Content
> iterator() {
170 if (Files
.isDirectory(path
)) {
172 return Files
.list(path
).map((p
) -> (Content
) new FsContent(this, p
)).iterator();
173 } catch (IOException e
) {
174 throw new ContentResourceException("Cannot list " + path
, e
);
177 return Collections
.emptyIterator();
182 public Content
add(QName name
, QName
... classes
) {
184 Path newPath
= path
.resolve(NamespaceUtils
.toPrefixedName(provider
, name
));
185 if (ContentName
.contains(classes
, CrName
.COLLECTION
.get()))
186 Files
.createDirectory(newPath
);
188 Files
.createFile(newPath
);
190 // for(ContentClass clss:classes) {
191 // Files.setAttribute(newPath, name, newPath, null)
193 return new FsContent(this, newPath
);
194 } catch (IOException e
) {
195 throw new ContentResourceException("Cannot create new content", e
);
200 public void remove() {
201 FsUtils
.delete(path
);
205 public Content
getParent() {
207 return null;// TODO deal with mounts
208 return new FsContent(this, path
.getParent());
215 public ProvidedSession
getSession() {
220 public FsContentProvider
getProvider() {