]> git.argeo.org Git - lgpl/argeo-commons.git/blob - FsContent.java
5efa65959b50a86affbf5e0b3e0b5bcc246c339d
[lgpl/argeo-commons.git] / FsContent.java
1 package org.argeo.cms.gcr.fs;
2
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;
15 import java.util.Map;
16 import java.util.Set;
17
18 import javax.xml.namespace.QName;
19
20 import org.argeo.api.gcr.Content;
21 import org.argeo.api.gcr.ContentName;
22 import org.argeo.api.gcr.ContentResourceException;
23 import org.argeo.api.gcr.CrName;
24 import org.argeo.api.gcr.spi.AbstractContent;
25 import org.argeo.api.gcr.spi.ProvidedContent;
26 import org.argeo.api.gcr.spi.ProvidedSession;
27 import org.argeo.util.FsUtils;
28
29 public class FsContent extends AbstractContent implements ProvidedContent {
30 private final static String USER_ = "user:";
31
32 private static final Map<QName, String> BASIC_KEYS;
33 private static final Map<QName, String> POSIX_KEYS;
34 static {
35 BASIC_KEYS = new HashMap<>();
36 BASIC_KEYS.put(CrName.CREATION_TIME.get(), "basic:creationTime");
37 BASIC_KEYS.put(CrName.LAST_MODIFIED_TIME.get(), "basic:lastModifiedTime");
38 BASIC_KEYS.put(CrName.SIZE.get(), "basic:size");
39 BASIC_KEYS.put(CrName.FILE_KEY.get(), "basic:fileKey");
40
41 POSIX_KEYS = new HashMap<>(BASIC_KEYS);
42 POSIX_KEYS.put(CrName.OWNER.get(), "owner:owner");
43 POSIX_KEYS.put(CrName.GROUP.get(), "posix:group");
44 POSIX_KEYS.put(CrName.PERMISSIONS.get(), "posix:permissions");
45 }
46
47 private final ProvidedSession session;
48 private final FsContentProvider provider;
49 private final Path path;
50 private final boolean isRoot;
51 private final QName name;
52
53 protected FsContent(ProvidedSession session, FsContentProvider contentProvider, Path path) {
54 this.session = session;
55 this.provider = contentProvider;
56 this.path = path;
57 this.isRoot = contentProvider.isRoot(path);
58 // TODO check file names with ':' ?
59 if (isRoot)
60 this.name = CrName.ROOT.get();
61 else
62 this.name = session.parsePrefixedName(path.getFileName().toString());
63 }
64
65 protected FsContent(FsContent context, Path path) {
66 this(context.getSession(), context.getProvider(), path);
67 }
68
69 private boolean isPosix() {
70 return path.getFileSystem().supportedFileAttributeViews().contains("posix");
71 }
72
73 @Override
74 public QName getName() {
75 return name;
76 }
77
78 /*
79 * ATTRIBUTES
80 */
81
82 @Override
83 public <A> A get(QName key, Class<A> clss) {
84 Object value;
85 try {
86 // We need to add user: when accessing via Files#getAttribute
87 value = Files.getAttribute(path, toFsAttributeKey(key));
88 } catch (IOException e) {
89 throw new ContentResourceException("Cannot retrieve attribute " + key + " for " + path, e);
90 }
91 if (value instanceof FileTime) {
92 if (clss.isAssignableFrom(FileTime.class))
93 return (A) value;
94 Instant instant = ((FileTime) value).toInstant();
95 if (Object.class.isAssignableFrom(clss)) {// plain object requested
96 return (A) instant;
97 }
98 // TODO perform trivial file conversion to other formats
99 }
100 if (value instanceof byte[]) {
101 return (A) new String((byte[]) value, StandardCharsets.UTF_8);
102 }
103 return (A) value;
104 }
105
106 @Override
107 protected Iterable<QName> keys() {
108 Set<QName> result = new HashSet<>(isPosix() ? POSIX_KEYS.keySet() : BASIC_KEYS.keySet());
109 UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
110 if (udfav != null) {
111 try {
112 for (String name : udfav.list()) {
113 result.add(session.parsePrefixedName(name));
114 }
115 } catch (IOException e) {
116 throw new ContentResourceException("Cannot list attributes for " + path, e);
117 }
118 }
119 return result;
120 }
121
122 @Override
123 protected void removeAttr(QName key) {
124 UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
125 try {
126 udfav.delete(session.toPrefixedName(key));
127 } catch (IOException e) {
128 throw new ContentResourceException("Cannot delete attribute " + key + " for " + path, e);
129 }
130 }
131
132 @Override
133 public Object put(QName key, Object value) {
134 Object previous = get(key);
135 UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
136 ByteBuffer bb = ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
137 try {
138 int size = udfav.write(session.toPrefixedName(key), bb);
139 } catch (IOException e) {
140 throw new ContentResourceException("Cannot delete attribute " + key + " for " + path, e);
141 }
142 return previous;
143 }
144
145 protected String toFsAttributeKey(QName key) {
146 if (POSIX_KEYS.containsKey(key))
147 return POSIX_KEYS.get(key);
148 else
149 return USER_ + session.toPrefixedName(key);
150 }
151
152 /*
153 * CONTENT OPERATIONS
154 */
155 @Override
156 public Iterator<Content> iterator() {
157 if (Files.isDirectory(path)) {
158 try {
159 return Files.list(path).map((p) -> (Content) new FsContent(this, p)).iterator();
160 } catch (IOException e) {
161 throw new ContentResourceException("Cannot list " + path, e);
162 }
163 } else {
164 return Collections.emptyIterator();
165 }
166 }
167
168 @Override
169 public Content add(QName name, QName... classes) {
170 try {
171 Path newPath = path.resolve(session.toPrefixedName(name));
172 if (ContentName.contains(classes, CrName.COLLECTION.get()))
173 Files.createDirectory(newPath);
174 else
175 Files.createFile(newPath);
176
177 // for(ContentClass clss:classes) {
178 // Files.setAttribute(newPath, name, newPath, null)
179 // }
180 return new FsContent(this, newPath);
181 } catch (IOException e) {
182 throw new ContentResourceException("Cannot create new content", e);
183 }
184 }
185
186 @Override
187 public void remove() {
188 FsUtils.delete(path);
189 }
190
191 @Override
192 public Content getParent() {
193 if (isRoot)
194 return null;// TODO deal with mounts
195 return new FsContent(this, path.getParent());
196 }
197
198 /*
199 * ACCESSORS
200 */
201 @Override
202 public ProvidedSession getSession() {
203 return session;
204 }
205
206 @Override
207 public FsContentProvider getProvider() {
208 return provider;
209 }
210
211 }