X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Facr%2Ffs%2FFsContent.java;h=f0c7338579c5f67f319c5122c06a2cebc072948e;hb=b8f50d6d8e7b9c9215d156ba33f9dedfcee913a7;hp=15917c50359a003affc0b2d853e444a483526019;hpb=7e464c3cedfa41ece64811fb55ddc9ce740a1050;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java index 15917c503..f0c733857 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java @@ -8,6 +8,7 @@ import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import java.nio.file.attribute.FileTime; import java.nio.file.attribute.UserDefinedFileAttributeView; import java.time.Instant; @@ -20,23 +21,34 @@ 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; +import javax.xml.transform.TransformerFactory; +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.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; /** 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 BASIC_KEYS; @@ -56,18 +68,18 @@ public class FsContent extends AbstractContent implements ProvidedContent { private final FsContentProvider provider; private final Path path; - private final boolean isRoot; + private final boolean isMountBase; private final QName name; protected FsContent(ProvidedSession session, FsContentProvider contentProvider, Path path) { super(session); this.provider = contentProvider; this.path = path; - this.isRoot = contentProvider.isMountRoot(path); + this.isMountBase = contentProvider.isMountBase(path); // TODO check file names with ':' ? - if (isRoot) { + 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 { @@ -76,9 +88,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); } @@ -140,15 +151,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 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); } @@ -160,6 +197,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); } @@ -183,8 +222,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) { @@ -246,6 +297,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); @@ -265,7 +318,7 @@ public class FsContent extends AbstractContent implements ProvidedContent { @Override public Content getParent() { - if (isRoot) { + if (isMountBase) { String mountPath = provider.getMountPath(); if (mountPath == null || mountPath.equals("/")) return null; @@ -307,12 +360,30 @@ public class FsContent extends AbstractContent implements ProvidedContent { @Override public List getContentClasses() { List res = new ArrayList<>(); + List value = getMultiple(CrName.cc.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 return res; } + @Override + public void addContentClasses(QName... contentClass) { + List toWrite = new ArrayList<>(); + for (QName cc : getContentClasses()) { + if (cc.equals(CrName.collection.qName())) + continue; // skip + toWrite.add(NamespaceUtils.toPrefixedName(provider, cc)); + } + for (QName cc : contentClass) { + toWrite.add(NamespaceUtils.toPrefixedName(provider, cc)); + } + put(CrName.cc.qName(), toWrite); + } + /* * ACCESSORS */ @@ -322,4 +393,39 @@ public class FsContent extends AbstractContent implements ProvidedContent { return provider; } + /* + * READ / WRITE + */ + @SuppressWarnings("unchecked") + public CompletableFuture write(Class clss) { + if (isContentClass(CrName.collection.qName())) { + throw new IllegalStateException("Cannot directly write to a collection"); + } + if (InputStream.class.isAssignableFrom(clss)) { + CompletableFuture res = new CompletableFuture<>(); + res.thenAccept((in) -> { + try { + Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException("Cannot write to " + path, e); + } + }); + return (CompletableFuture) res; + } else if (Source.class.isAssignableFrom(clss)) { + CompletableFuture res = new CompletableFuture(); + res.thenAccept((source) -> { +// Path targetPath = path.getParent().resolve(path.getFileName()+".xml"); + Path targetPath = path; + try (OutputStream out = Files.newOutputStream(targetPath)) { + StreamResult result = new StreamResult(out); + TransformerFactory.newDefaultInstance().newTransformer().transform(source, result); + } catch (IOException | TransformerException e) { + throw new RuntimeException("Cannot write to " + path, e); + } + }); + return (CompletableFuture) res; + } else { + return super.write(clss); + } + } }