]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java
Introduce CMS JShell
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / fs / FsContentProvider.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.UserDefinedFileAttributeView;
9 import java.util.Iterator;
10 import java.util.Map;
11 import java.util.NavigableMap;
12 import java.util.Objects;
13 import java.util.TreeMap;
14 import java.util.stream.Collectors;
15
16 import javax.xml.namespace.QName;
17
18 import org.argeo.api.acr.ArgeoNamespace;
19 import org.argeo.api.acr.ContentResourceException;
20 import org.argeo.api.acr.NamespaceUtils;
21 import org.argeo.api.acr.RuntimeNamespaceContext;
22 import org.argeo.api.acr.spi.ContentProvider;
23 import org.argeo.api.acr.spi.ProvidedContent;
24 import org.argeo.api.acr.spi.ProvidedSession;
25 import org.argeo.cms.util.OS;
26
27 /** Access a file system as a {@link ContentProvider}. */
28 public class FsContentProvider implements ContentProvider {
29
30 protected String mountPath;
31 protected Path rootPath;
32
33 private NavigableMap<String, String> prefixes = new TreeMap<>();
34
35 private final boolean isNtfs;
36 private final String XMLNS_;
37
38 public FsContentProvider(String mountPath, Path rootPath) {
39 Objects.requireNonNull(mountPath);
40 Objects.requireNonNull(rootPath);
41
42 this.mountPath = mountPath;
43 this.rootPath = rootPath;
44
45 this.isNtfs = OS.LOCAL.isMSWindows();
46 this.XMLNS_ = isNtfs ? "xmlns%3A" : "xmlns:";
47
48 // FIXME make it more robust
49 initNamespaces();
50 }
51
52 protected FsContentProvider() {
53 this.isNtfs = OS.LOCAL.isMSWindows();
54 this.XMLNS_ = isNtfs ? "xmlns%3A" : "xmlns:";
55 }
56
57 protected void initNamespaces() {
58 try {
59 UserDefinedFileAttributeView udfav = Files.getFileAttributeView(rootPath,
60 UserDefinedFileAttributeView.class);
61 if (udfav == null)
62 return;
63 for (String name : udfav.list()) {
64 if (name.startsWith(XMLNS_)) {
65 ByteBuffer buf = ByteBuffer.allocate(udfav.size(name));
66 udfav.read(name, buf);
67 buf.flip();
68 String namespace = StandardCharsets.UTF_8.decode(buf).toString();
69 String prefix = name.substring(XMLNS_.length());
70 prefixes.put(prefix, namespace);
71 }
72 }
73
74 // defaults
75 addDefaultNamespace(udfav, ArgeoNamespace.CR_DEFAULT_PREFIX, ArgeoNamespace.CR_NAMESPACE_URI);
76 addDefaultNamespace(udfav, "basic", ArgeoNamespace.CR_NAMESPACE_URI);
77 addDefaultNamespace(udfav, "owner", ArgeoNamespace.CR_NAMESPACE_URI);
78 addDefaultNamespace(udfav, "posix", ArgeoNamespace.CR_NAMESPACE_URI);
79 } catch (IOException e) {
80 throw new RuntimeException("Cannot read namespaces from " + rootPath, e);
81 }
82
83 }
84
85 protected void addDefaultNamespace(UserDefinedFileAttributeView udfav, String prefix, String namespace)
86 throws IOException {
87 if (!prefixes.containsKey(prefix)) {
88 ByteBuffer bb = ByteBuffer.wrap(namespace.getBytes(StandardCharsets.UTF_8));
89 udfav.write(XMLNS_ + prefix, bb);
90 prefixes.put(prefix, namespace);
91 }
92 }
93
94 public void registerPrefix(String prefix, String namespace) {
95 if (prefixes.containsKey(prefix))
96 prefixes.remove(prefix);
97 try {
98 UserDefinedFileAttributeView udfav = Files.getFileAttributeView(rootPath,
99 UserDefinedFileAttributeView.class);
100 addDefaultNamespace(udfav, prefix, namespace);
101 } catch (IOException e) {
102 throw new RuntimeException("Cannot register namespace " + prefix + " " + namespace + " on " + rootPath, e);
103 }
104
105 }
106
107 @Override
108 public String getMountPath() {
109 return mountPath;
110 }
111
112 boolean isMountBase(Path path) {
113 try {
114 return Files.isSameFile(rootPath, path);
115 } catch (IOException e) {
116 throw new ContentResourceException(e);
117 }
118 }
119
120 @Override
121 public ProvidedContent get(ProvidedSession session, String relativePath) {
122 return new FsContent(session, this, rootPath.resolve(relativePath));
123 }
124
125 @Override
126 public boolean exists(ProvidedSession session, String relativePath) {
127 return Files.exists(rootPath.resolve(relativePath));
128 }
129
130 /*
131 * ATTRIBUTE NAMES
132 */
133 /**
134 * Make sure that the prefixed name is compatible with the underlying file
135 * system for file names/attributes (NTFS does not accept :)
136 */
137 String toFsPrefixedName(QName key) {
138 return isNtfs ? NamespaceUtils.toPrefixedName(this, key).replace(":", "%3A")
139 : NamespaceUtils.toPrefixedName(this, key);
140 }
141
142 /**
143 * PArse a prefixed name which is compatible with the underlying file system for
144 * file names/attributes (NTFS does not accept :)
145 */
146 QName fromFsPrefixedName(String name) {
147 return isNtfs ? NamespaceUtils.parsePrefixedName(this, name.replace("%3A", ":"))
148 : NamespaceUtils.parsePrefixedName(this, name);
149 }
150
151 /*
152 * NAMESPACE CONTEXT
153 */
154
155 @Override
156 public String getNamespaceURI(String prefix) {
157 return NamespaceUtils.getNamespaceURI((p) -> prefixes.get(p), prefix);
158 }
159
160 @Override
161 public Iterator<String> getPrefixes(String namespaceURI) {
162 Iterator<String> res = NamespaceUtils.getPrefixes((ns) -> prefixes.entrySet().stream()
163 .filter(e -> e.getValue().equals(ns)).map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()),
164 namespaceURI);
165 if (!res.hasNext()) {
166 String prefix = RuntimeNamespaceContext.getNamespaceContext().getPrefix(namespaceURI);
167 if (prefix != null) {
168 registerPrefix(prefix, namespaceURI);
169 return getPrefixes(namespaceURI);
170 } else {
171 throw new IllegalArgumentException("Unknown namespace " + namespaceURI);
172 }
173 }
174 return res;
175 }
176
177 }