Massive package refactoring
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / acr / fs / FsContent.java
index 55ef6ec46d89e7f4dbc01ef0b6ac2352c8901cf9..43cae85721bf6b96c3f577f157f64b56845c11b2 100644 (file)
@@ -21,8 +21,10 @@ import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.concurrent.CompletableFuture;
 
+import javax.xml.XMLConstants;
 import javax.xml.namespace.QName;
 import javax.xml.transform.Source;
 import javax.xml.transform.TransformerException;
@@ -32,31 +34,37 @@ import javax.xml.transform.stream.StreamResult;
 import org.argeo.api.acr.Content;
 import org.argeo.api.acr.ContentName;
 import org.argeo.api.acr.ContentResourceException;
+import org.argeo.api.acr.CrAttributeType;
 import org.argeo.api.acr.CrName;
+import org.argeo.api.acr.DName;
 import org.argeo.api.acr.NamespaceUtils;
 import org.argeo.api.acr.spi.ContentProvider;
 import org.argeo.api.acr.spi.ProvidedContent;
 import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.api.cms.CmsLog;
 import org.argeo.cms.acr.AbstractContent;
 import org.argeo.cms.acr.ContentUtils;
-import org.argeo.util.FsUtils;
+import org.argeo.cms.util.FsUtils;
 
 /** Content persisted as a filesystem {@link Path}. */
 public class FsContent extends AbstractContent implements ProvidedContent {
+       private CmsLog log = CmsLog.getLog(FsContent.class);
+
        final static String USER_ = "user:";
 
        private static final Map<QName, String> BASIC_KEYS;
        private static final Map<QName, String> POSIX_KEYS;
        static {
                BASIC_KEYS = new HashMap<>();
-               BASIC_KEYS.put(CrName.creationTime.qName(), "basic:creationTime");
-               BASIC_KEYS.put(CrName.lastModifiedTime.qName(), "basic:lastModifiedTime");
-               BASIC_KEYS.put(CrName.size.qName(), "basic:size");
+               BASIC_KEYS.put(DName.creationdate.qName(), "basic:creationTime");
+               BASIC_KEYS.put(DName.getlastmodified.qName(), "basic:lastModifiedTime");
+               BASIC_KEYS.put(DName.getcontentlength.qName(), "basic:size");
+
                BASIC_KEYS.put(CrName.fileKey.qName(), "basic:fileKey");
 
                POSIX_KEYS = new HashMap<>(BASIC_KEYS);
-               POSIX_KEYS.put(CrName.owner.qName(), "owner:owner");
-               POSIX_KEYS.put(CrName.group.qName(), "posix:group");
+               POSIX_KEYS.put(DName.owner.qName(), "owner:owner");
+               POSIX_KEYS.put(DName.group.qName(), "posix:group");
                POSIX_KEYS.put(CrName.permissions.qName(), "posix:permissions");
        }
 
@@ -73,7 +81,7 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                // TODO check file names with ':' ?
                if (isMountBase) {
                        String mountPath = provider.getMountPath();
-                       if (mountPath != null && !mountPath.equals("/")) {
+                       if (mountPath != null && !mountPath.equals(ContentUtils.ROOT_SLASH)) {
                                Content mountPoint = session.getMountPoint(mountPath);
                                this.name = mountPoint.getName();
                        } else {
@@ -82,9 +90,8 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                } else {
 
                        // TODO should we support prefixed name for known types?
-                       // QName providerName = NamespaceUtils.parsePrefixedName(provider,
-                       // path.getFileName().toString());
-                       QName providerName = new QName(path.getFileName().toString());
+                       QName providerName = NamespaceUtils.parsePrefixedName(provider, path.getFileName().toString());
+//                     QName providerName = new QName(path.getFileName().toString());
                        // TODO remove extension if mounted?
                        this.name = new ContentName(providerName, session);
                }
@@ -146,15 +153,41 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                        }
                        // TODO perform trivial file conversion to other formats
                }
+
+               // TODO better deal with multiple types
                if (value instanceof byte[]) {
-                       res = (A) new String((byte[]) value, StandardCharsets.UTF_8);
-               }
-               if (res == null)
-                       try {
-                               res = (A) value;
-                       } catch (ClassCastException e) {
-                               return Optional.empty();
+                       String str = new String((byte[]) value, StandardCharsets.UTF_8);
+                       String[] arr = str.split("\n");
+
+                       if (arr.length == 1) {
+                               if (clss.isAssignableFrom(String.class)) {
+                                       res = (A) arr[0];
+                               } else {
+                                       res = (A) CrAttributeType.parse(arr[0]);
+                               }
+                       } else {
+                               List<Object> lst = new ArrayList<>();
+                               for (String s : arr) {
+                                       lst.add(CrAttributeType.parse(s));
+                               }
+                               res = (A) lst;
                        }
+               }
+               if (res == null) {
+                       if (isDefaultAttrTypeRequested(clss))
+                               return Optional.of((A) CrAttributeType.parse(value.toString()));
+                       if (clss.isAssignableFrom(value.getClass()))
+                               return Optional.of((A) value);
+                       if (clss.isAssignableFrom(String.class))
+                               return Optional.of((A) value.toString());
+                       log.warn("Cannot interpret " + key + " in " + this);
+                       return Optional.empty();
+//                     try {
+//                             res = (A) value;
+//                     } catch (ClassCastException e) {
+//                             return Optional.empty();
+//                     }
+               }
                return Optional.of(res);
        }
 
@@ -166,6 +199,8 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                        try {
                                for (String name : udfav.list()) {
                                        QName providerName = NamespaceUtils.parsePrefixedName(provider, name);
+                                       if (providerName.getNamespaceURI().equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI))
+                                               continue; // skip prefix mapping
                                        QName sessionName = new ContentName(providerName, getSession());
                                        result.add(sessionName);
                                }
@@ -189,8 +224,20 @@ public class FsContent extends AbstractContent implements ProvidedContent {
        @Override
        public Object put(QName key, Object value) {
                Object previous = get(key);
+
+               String toWrite;
+               if (value instanceof List) {
+                       StringJoiner sj = new StringJoiner("\n");
+                       for (Object obj : (List<?>) value) {
+                               sj.add(obj.toString());
+                       }
+                       toWrite = sj.toString();
+               } else {
+                       toWrite = value.toString();
+               }
+
                UserDefinedFileAttributeView udfav = Files.getFileAttributeView(path, UserDefinedFileAttributeView.class);
-               ByteBuffer bb = ByteBuffer.wrap(value.toString().getBytes(StandardCharsets.UTF_8));
+               ByteBuffer bb = ByteBuffer.wrap(toWrite.getBytes(StandardCharsets.UTF_8));
                try {
                        udfav.write(NamespaceUtils.toPrefixedName(provider, key), bb);
                } catch (IOException e) {
@@ -239,7 +286,7 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                FsContent fsContent;
                try {
                        Path newPath = path.resolve(NamespaceUtils.toPrefixedName(provider, name));
-                       if (ContentName.contains(classes, CrName.collection.qName()))
+                       if (ContentName.contains(classes, DName.collection.qName()))
                                Files.createDirectory(newPath);
                        else
                                Files.createFile(newPath);
@@ -252,6 +299,8 @@ public class FsContent extends AbstractContent implements ProvidedContent {
                        throw new ContentResourceException("Cannot create new content", e);
                }
 
+               if (classes.length > 0)
+                       fsContent.addContentClasses(classes);
                if (getSession().getRepository().shouldMount(classes)) {
                        ContentProvider contentProvider = getSession().getRepository().getMountContentProvider(fsContent, true,
                                        classes);
@@ -313,12 +362,30 @@ public class FsContent extends AbstractContent implements ProvidedContent {
        @Override
        public List<QName> getContentClasses() {
                List<QName> res = new ArrayList<>();
+               List<String> value = getMultiple(DName.resourcetype.qName(), String.class);
+               for (String s : value) {
+                       QName name = NamespaceUtils.parsePrefixedName(provider, s);
+                       res.add(name);
+               }
                if (Files.isDirectory(path))
-                       res.add(CrName.collection.qName());
-               // TODO add other types
+                       res.add(DName.collection.qName());
                return res;
        }
 
+       @Override
+       public void addContentClasses(QName... contentClass) {
+               List<String> toWrite = new ArrayList<>();
+               for (QName cc : getContentClasses()) {
+                       if (cc.equals(DName.collection.qName()))
+                               continue; // skip
+                       toWrite.add(NamespaceUtils.toPrefixedName(provider, cc));
+               }
+               for (QName cc : contentClass) {
+                       toWrite.add(NamespaceUtils.toPrefixedName(provider, cc));
+               }
+               put(DName.resourcetype.qName(), toWrite);
+       }
+
        /*
         * ACCESSORS
         */
@@ -333,7 +400,7 @@ public class FsContent extends AbstractContent implements ProvidedContent {
         */
        @SuppressWarnings("unchecked")
        public <A> CompletableFuture<A> write(Class<A> clss) {
-               if (isContentClass(CrName.collection.qName())) {
+               if (isContentClass(DName.collection.qName())) {
                        throw new IllegalStateException("Cannot directly write to a collection");
                }
                if (InputStream.class.isAssignableFrom(clss)) {