Adapt to MS Windows
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / fs / FsContentProvider.java
index 65bdd343ef72b213289e982008f917940877c8c2..47cd64af343dc2aec976cb554b82be229c6547ad 100644 (file)
@@ -9,35 +9,57 @@ import java.nio.file.attribute.UserDefinedFileAttributeView;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.NavigableMap;
+import java.util.Objects;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
 
-import org.argeo.api.acr.Content;
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.ArgeoNamespace;
 import org.argeo.api.acr.ContentResourceException;
-import org.argeo.api.acr.CrName;
 import org.argeo.api.acr.NamespaceUtils;
+import org.argeo.api.acr.RuntimeNamespaceContext;
 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.util.OS;
 
 /** Access a file system as a {@link ContentProvider}. */
 public class FsContentProvider implements ContentProvider {
-       final static String XMLNS_ = "xmlns:";
 
-       private final Path rootPath;
-       private final boolean isRoot;
+       protected String mountPath;
+       protected Path rootPath;
 
        private NavigableMap<String, String> prefixes = new TreeMap<>();
 
-       public FsContentProvider(Path rootPath, boolean isRoot) {
+       private final boolean isNtfs;
+       private final String XMLNS_;
+
+       public FsContentProvider(String mountPath, Path rootPath) {
+               Objects.requireNonNull(mountPath);
+               Objects.requireNonNull(rootPath);
+
+               this.mountPath = mountPath;
                this.rootPath = rootPath;
-               this.isRoot = isRoot;
+
+               this.isNtfs = OS.LOCAL.isMSWindows();
+               this.XMLNS_ = isNtfs ? "xmlns%3A" : "xmlns:";
+
+               // FIXME make it more robust
                initNamespaces();
        }
 
+       protected FsContentProvider() {
+               this.isNtfs = OS.LOCAL.isMSWindows();
+               this.XMLNS_ = isNtfs ? "xmlns%3A" : "xmlns:";
+       }
+
        protected void initNamespaces() {
                try {
                        UserDefinedFileAttributeView udfav = Files.getFileAttributeView(rootPath,
                                        UserDefinedFileAttributeView.class);
+                       if (udfav == null)
+                               return;
                        for (String name : udfav.list()) {
                                if (name.startsWith(XMLNS_)) {
                                        ByteBuffer buf = ByteBuffer.allocate(udfav.size(name));
@@ -50,10 +72,10 @@ public class FsContentProvider implements ContentProvider {
                        }
 
                        // defaults
-                       addDefaultNamespace(udfav, CrName.CR_DEFAULT_PREFIX, CrName.CR_NAMESPACE_URI);
-                       addDefaultNamespace(udfav, "basic", CrName.CR_NAMESPACE_URI);
-                       addDefaultNamespace(udfav, "owner", CrName.CR_NAMESPACE_URI);
-                       addDefaultNamespace(udfav, "posix", CrName.CR_NAMESPACE_URI);
+                       addDefaultNamespace(udfav, ArgeoNamespace.CR_DEFAULT_PREFIX, ArgeoNamespace.CR_NAMESPACE_URI);
+                       addDefaultNamespace(udfav, "basic", ArgeoNamespace.CR_NAMESPACE_URI);
+                       addDefaultNamespace(udfav, "owner", ArgeoNamespace.CR_NAMESPACE_URI);
+                       addDefaultNamespace(udfav, "posix", ArgeoNamespace.CR_NAMESPACE_URI);
                } catch (IOException e) {
                        throw new RuntimeException("Cannot read namespaces from " + rootPath, e);
                }
@@ -64,24 +86,68 @@ public class FsContentProvider implements ContentProvider {
                        throws IOException {
                if (!prefixes.containsKey(prefix)) {
                        ByteBuffer bb = ByteBuffer.wrap(namespace.getBytes(StandardCharsets.UTF_8));
-                       int size = udfav.write(XMLNS_ + prefix, bb);
+                       udfav.write(XMLNS_ + prefix, bb);
                        prefixes.put(prefix, namespace);
                }
        }
 
-       boolean isRoot(Path path) {
+       public void registerPrefix(String prefix, String namespace) {
+               if (prefixes.containsKey(prefix))
+                       prefixes.remove(prefix);
                try {
-                       return isRoot && Files.isSameFile(rootPath, path);
+                       UserDefinedFileAttributeView udfav = Files.getFileAttributeView(rootPath,
+                                       UserDefinedFileAttributeView.class);
+                       addDefaultNamespace(udfav, prefix, namespace);
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot register namespace " + prefix + " " + namespace + " on " + rootPath, e);
+               }
+
+       }
+
+       @Override
+       public String getMountPath() {
+               return mountPath;
+       }
+
+       boolean isMountBase(Path path) {
+               try {
+                       return Files.isSameFile(rootPath, path);
                } catch (IOException e) {
                        throw new ContentResourceException(e);
                }
        }
 
        @Override
-       public Content get(ProvidedSession session, String mountPath, String relativePath) {
+       public ProvidedContent get(ProvidedSession session, String relativePath) {
                return new FsContent(session, this, rootPath.resolve(relativePath));
        }
 
+       @Override
+       public boolean exists(ProvidedSession session, String relativePath) {
+               return Files.exists(rootPath.resolve(relativePath));
+       }
+
+       /*
+        * ATTRIBUTE NAMES
+        */
+       /**
+        * Make sure that the prefixed name is compatible with the underlying file
+        * system for file names/attributes (NTFS does not accept :)
+        */
+       String toFsPrefixedName(QName key) {
+               return isNtfs ? NamespaceUtils.toPrefixedName(this, key).replace(":", "%3A")
+                               : NamespaceUtils.toPrefixedName(this, key);
+       }
+
+       /**
+        * PArse a prefixed name which is compatible with the underlying file system for
+        * file names/attributes (NTFS does not accept :)
+        */
+       QName fromFsPrefixedName(String name) {
+               return isNtfs ? NamespaceUtils.parsePrefixedName(this, name.replace("%3A", ":"))
+                               : NamespaceUtils.parsePrefixedName(this, name);
+       }
+
        /*
         * NAMESPACE CONTEXT
         */
@@ -93,8 +159,19 @@ public class FsContentProvider implements ContentProvider {
 
        @Override
        public Iterator<String> getPrefixes(String namespaceURI) {
-               return NamespaceUtils.getPrefixes((ns) -> prefixes.entrySet().stream().filter(e -> e.getValue().equals(ns))
-                               .map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()), namespaceURI);
+               Iterator<String> res = NamespaceUtils.getPrefixes((ns) -> prefixes.entrySet().stream()
+                               .filter(e -> e.getValue().equals(ns)).map(Map.Entry::getKey).collect(Collectors.toUnmodifiableSet()),
+                               namespaceURI);
+               if (!res.hasNext()) {
+                       String prefix = RuntimeNamespaceContext.getNamespaceContext().getPrefix(namespaceURI);
+                       if (prefix != null) {
+                               registerPrefix(prefix, namespaceURI);
+                               return getPrefixes(namespaceURI);
+                       } else {
+                               throw new IllegalArgumentException("Unknown namespace " + namespaceURI);
+                       }
+               }
+               return res;
        }
 
 }