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