return QName.valueOf(name);
}
+ @Override
+ public int getSiblingIndex() {
+ return Jcr.getIndex(getJcrNode());
+ }
+
/*
* STATIC UTLITIES
*/
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
+import java.util.ArrayList;
import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
import javax.jcr.RepositoryException;
+import javax.jcr.Value;
import javax.jcr.nodetype.NodeType;
import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public static void copyFiles(Node folder, Content collection, String... additionalCollectionTypes) {
try {
log.debug("Copy collection " + collection);
+
+ NamespaceContext jcrNamespaceContext = new JcrSessionNamespaceContext(folder.getSession());
+
nodes: for (NodeIterator it = folder.getNodes(); it.hasNext();) {
Node node = it.nextNode();
String name = node.getName();
Content subCol = collection.add(name, CrName.collection.qName());
copyFiles(node, subCol, additionalCollectionTypes);
} else {
+ List<QName> contentClasses = typesAsContentClasses(node, jcrNamespaceContext);
for (String collectionType : additionalCollectionTypes) {
if (node.isNodeType(collectionType)) {
- Content subCol = collection.add(name, CrName.collection.qName());
+ contentClasses.add(CrName.collection.qName());
+ Content subCol = collection.add(name,
+ contentClasses.toArray(new QName[contentClasses.size()]));
+ setAttributes(node, subCol, jcrNamespaceContext);
+// setContentClasses(node, subCol, jcrNamespaceContext);
copyFiles(node, subCol, additionalCollectionTypes);
continue nodes;
}
log.warn("Same name siblings not supported, skipping " + node);
continue nodes;
}
- Content content = collection.add(qName, qName);
+ Content content = collection.add(qName, contentClasses.toArray(new QName[contentClasses.size()]));
Source source = toSource(node);
((ProvidedContent) content).getSession().edit((s) -> {
((ProvidedSession) s).notifyModification((ProvidedContent) content);
content.write(Source.class).complete(source);
+// try {
+// //setContentClasses(node, content, jcrNamespaceContext);
+// } catch (RepositoryException e) {
+// // TODO Auto-generated catch block
+// e.printStackTrace();
+// }
}).toCompletableFuture().join();
+ setAttributes(node, content, jcrNamespaceContext);
// } else {
// // ignore
// try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
// node.getSession().exportDocumentView(node.getPath(), out, true, false);
-// DocumentBuilder documentBuilder = DocumentBuilderFactory.newNSInstance().newDocumentBuilder();
-// Document document;
-// try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray())) {
-// document = documentBuilder.parse(in);
-// }
-// cleanJcrDom(document);
-// return new DOMSource(document);
-// } catch (IOException | SAXException | ParserConfigurationException e) {
+// System.out.println(new String(out.toByteArray(), StandardCharsets.UTF_8));
+//// DocumentBuilder documentBuilder = DocumentBuilderFactory.newNSInstance().newDocumentBuilder();
+//// Document document;
+//// try (ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray())) {
+//// document = documentBuilder.parse(in);
+//// }
+//// cleanJcrDom(document);
+//// return new DOMSource(document);
+// } catch (IOException e) {
// throw new RuntimeException(e);
// }
}
+ public static void setAttributes(Node source, Content target, NamespaceContext jcrNamespaceContext)
+ throws RepositoryException {
+ properties: for (PropertyIterator pit = source.getProperties(); pit.hasNext();) {
+ Property p = pit.nextProperty();
+ // TODO migrate JCR title, last modified, etc. ?
+ if (p.getName().startsWith("jcr:"))
+ continue properties;
+ if (p.isMultiple()) {
+ List<String> attr = new ArrayList<>();
+ for (Value value : p.getValues()) {
+ attr.add(value.getString());
+ }
+ target.put(NamespaceUtils.parsePrefixedName(jcrNamespaceContext, p.getName()), attr);
+ } else {
+ target.put(NamespaceUtils.parsePrefixedName(jcrNamespaceContext, p.getName()), p.getString());
+ }
+ }
+ }
+
+ public static List<QName> typesAsContentClasses(Node source, NamespaceContext jcrNamespaceContext)
+ throws RepositoryException {
+ // TODO super types?
+ List<QName> contentClasses = new ArrayList<>();
+ contentClasses
+ .add(NamespaceUtils.parsePrefixedName(jcrNamespaceContext, source.getPrimaryNodeType().getName()));
+ for (NodeType nodeType : source.getMixinNodeTypes()) {
+ contentClasses.add(NamespaceUtils.parsePrefixedName(jcrNamespaceContext, nodeType.getName()));
+ }
+ // filter out JCR types
+ for (Iterator<QName> it = contentClasses.iterator(); it.hasNext();) {
+ QName type = it.next();
+ if (type.getNamespaceURI().equals(JCR_NT_NAMESPACE_URI)
+ || type.getNamespaceURI().equals(JCR_MIX_NAMESPACE_URI)) {
+ it.remove();
+ }
+ }
+ // target.addContentClasses(contentClasses.toArray(new
+ // QName[contentClasses.size()]));
+ return contentClasses;
+ }
+
static final String JCR_NAMESPACE_URI = "http://www.jcp.org/jcr/1.0";
+ static final String JCR_NT_NAMESPACE_URI = "http://www.jcp.org/jcr/nt/1.0";
+ static final String JCR_MIX_NAMESPACE_URI = "http://www.jcp.org/jcr/mix/1.0";
public static void cleanJcrDom(Document document) {
Element documentElement = document.getDocumentElement();
if (namespaceUri == null)
continue attributes;
if (JCR_NAMESPACE_URI.equals(namespaceUri)) {
+ // FIXME probably wrong to change attributes length
element.removeAttributeNode(attr);
continue attributes;
}
--- /dev/null
+package org.argeo.cms.jcr.acr;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.xml.namespace.NamespaceContext;
+
+import org.argeo.jcr.JcrException;
+
+/** A {@link NamespaceContext} based on a JCR {@link Session}. */
+public class JcrSessionNamespaceContext implements NamespaceContext {
+ private final Session session;
+
+ public JcrSessionNamespaceContext(Session session) {
+ this.session = session;
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) {
+ try {
+ return session.getNamespaceURI(prefix);
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ @Override
+ public String getPrefix(String namespaceURI) {
+ try {
+ return session.getNamespacePrefix(namespaceURI);
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+
+ @Override
+ public Iterator<String> getPrefixes(String namespaceURI) {
+ try {
+ return Arrays.asList(session.getNamespacePrefix(namespaceURI)).iterator();
+ } catch (RepositoryException e) {
+ throw new JcrException(e);
+ }
+ }
+}
*/
List<QName> getContentClasses();
+ default void addContentClasses(QName... contentClass) {
+ throw new UnsupportedOperationException("Adding content classes to " + getPath() + " is not supported");
+ }
+
/** AND */
default boolean isContentClass(QName... contentClass) {
List<QName> contentClasses = getContentClasses();
return false;
}
+ /*
+ * SIBLINGS
+ */
+
+ default int getSiblingIndex() {
+ return 1;
+ }
+
/*
* DEFAULT METHODS
*/
*/
uuid, // the UUID of a content
mount,
+ cc, // content class
/*
* ATTRIBUTES FROM FILE SEMANTICS
String GUESTS_WORKSPACE = "guests";
String PUBLIC_WORKSPACE = "public";
String SECURITY_WORKSPACE = "security";
+ String MIGRATION_WORKSPACE = "migration";
+
+ /*
+ * ACR CONVENTIONS
+ */
+ String SRV_BASE = "/srv";
+
/*
* BASE DNs
@Override
public List<Content> getChildren(Content content) {
List<Content> res = new ArrayList<>();
+ if (isLeaf(content))
+ return res;
if (content == null)
return res;
for (Iterator<Content> it = content.iterator(); it.hasNext();) {
return res;
}
+ protected boolean isLeaf(Content content) {
+ return false;
+ }
+
@Override
public String getText(Content model) {
try {
import org.argeo.api.acr.Content;
import org.argeo.api.acr.CrName;
+import org.argeo.api.acr.NamespaceUtils;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.util.LangUtils;
private final ProvidedSession session;
// cache
- private String _path = null;
+// private String _path = null;
public AbstractContent(ProvidedSession session) {
this.session = session;
public <A> Optional<List<A>> getMultiple(QName key, Class<A> clss) {
Object value = get(key);
if (value == null)
- return null;
+ return Optional.empty();
if (value instanceof List) {
try {
List<A> res = (List<A>) value;
@Override
public String getPath() {
- if (_path != null)
- return _path;
+// if (_path != null)
+// return _path;
List<Content> ancestors = new ArrayList<>();
collectAncestors(ancestors, this);
StringBuilder path = new StringBuilder();
- for (Content c : ancestors) {
+ ancestors: for (Content c : ancestors) {
QName name = c.getName();
- // FIXME
- if (!CrName.root.qName().equals(name))
- path.append('/').append(name);
+ if (CrName.root.qName().equals(name))
+ continue ancestors;
+
+ path.append('/');
+ path.append(NamespaceUtils.toPrefixedName(name));
+ int siblingIndex = c.getSiblingIndex();
+ if (siblingIndex != 1)
+ path.append('[').append(siblingIndex).append(']');
}
- _path = path.toString();
- return _path;
+// _path = path.toString();
+// return _path;
+ return path.toString();
}
private void collectAncestors(List<Content> ancestors, Content content) {
public static final char SLASH = '/';
public static final String ROOT_SLASH = "" + SLASH;
+ public static final String EMPTY = "";
/**
* Split a path (with '/' separator) in an array of length 2, the first part
}
if (parentIndex == -1) // no '/'
- return new String[] { "", path };
+ return new String[] { EMPTY, path };
return new String[] { parentIndex != 0 ? path.substring(0, parentIndex) : "" + SLASH,
path.substring(parentIndex + 1) };
public static List<String> toPathSegments(String path) {
List<String> res = new ArrayList<>();
- if ("".equals(path) || ROOT_SLASH.equals(path))
+ if (EMPTY.equals(path) || ROOT_SLASH.equals(path))
return res;
collectPathSegments(path, res);
return res;
private static void collectPathSegments(String path, List<String> segments) {
String[] parent = getParentPath(path);
- if ("".equals(parent[1])) // root
+ if (EMPTY.equals(parent[1])) // root
return;
segments.add(0, parent[1]);
- if ("".equals(parent[0])) // end
+ if (EMPTY.equals(parent[0])) // end
return;
collectPathSegments(parent[0], segments);
}
}
synchronized ContentProvider findContentProvider(String path) {
+// if (ContentUtils.EMPTY.equals(path))
+// return partitions.firstEntry().getValue();
Map.Entry<String, ContentProvider> entry = partitions.floorEntry(path);
if (entry == null)
- throw new IllegalArgumentException("No entry provider found for " + path);
+ throw new IllegalArgumentException("No entry provider found for path '" + path + "'");
String mountPath = entry.getKey();
if (!path.startsWith(mountPath)) {
// FIXME make it more robust and find when there is no content provider
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.StringJoiner;
import java.util.concurrent.CompletableFuture;
import javax.xml.namespace.QName;
// 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 {
} 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);
}
// TODO perform trivial file conversion to other formats
}
if (value instanceof byte[]) {
- res = (A) new String((byte[]) value, StandardCharsets.UTF_8);
+ String str = new String((byte[]) value, StandardCharsets.UTF_8);
+ String[] arr = str.split("\n");
+ if (arr.length == 1) {
+ res = (A) arr[0];
+ } else {
+ List<String> lst = new ArrayList<>();
+ for (String s : arr) {
+ lst.add(s);
+ }
+ res = (A) lst;
+ }
}
if (res == null)
try {
@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) {
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);
@Override
public List<QName> getContentClasses() {
List<QName> res = new ArrayList<>();
+ Optional<List<String>> value = getMultiple(CrName.cc.qName(), String.class);
+ if (!value.isEmpty()) {
+ for (String s : value.get()) {
+ 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<String> 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
*/
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentName;
+import org.argeo.api.acr.CrName;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.cms.acr.AbstractContent;
@Override
public QName getName() {
- if (element.getParentNode() == null) {// root
+ if (isLocalRoot()) {// root
String mountPath = provider.getMountPath();
if (mountPath != null) {
+ if (ContentUtils.ROOT_SLASH.equals(mountPath)) {
+ return CrName.root.qName();
+ }
Content mountPoint = getSession().getMountPoint(mountPath);
- return mountPoint.getName();
+ QName mountPointName = mountPoint.getName();
+ return mountPointName;
}
}
return toQName(this.element);
}
+ protected boolean isLocalRoot() {
+ return element.getParentNode() == null || element.getParentNode() instanceof Document;
+ }
+
protected QName toQName(Node node) {
String prefix = node.getPrefix();
if (prefix == null) {
@Override
public Content getParent() {
Node parentNode = element.getParentNode();
- if (parentNode == null) {
+ if (isLocalRoot()) {
String mountPath = provider.getMountPath();
if (mountPath == null)
return null;
+ if (ContentUtils.ROOT_SLASH.equals(mountPath)) {
+ return null;
+ }
String[] parent = ContentUtils.getParentPath(mountPath);
+ if (ContentUtils.EMPTY.equals(parent[0]))
+ return null;
return getSession().get(parent[0]);
}
- if (parentNode instanceof Document)
- return null;
if (!(parentNode instanceof Element))
throw new IllegalStateException("Parent is not an element");
return new DomContent(this, (Element) parentNode);
}
// parentNode.replaceChild(resultNode, element);
// element = (Element)resultNode;
-
+
} catch (DOMException | TransformerException e) {
throw new RuntimeException("Cannot write to element", e);
}
return super.write(clss);
}
+ @Override
+ public int getSiblingIndex() {
+ Node curr = element.getPreviousSibling();
+ int count = 1;
+ while (curr != null) {
+ if (curr instanceof Element) {
+ if (Objects.equals(curr.getNamespaceURI(), element.getNamespaceURI())
+ && Objects.equals(curr.getLocalName(), element.getLocalName())) {
+ count++;
+ }
+ }
+ curr = curr.getPreviousSibling();
+ }
+ return count;
+ }
+
/*
* TYPING
*/
@Override
public List<QName> getContentClasses() {
List<QName> res = new ArrayList<>();
- res.add(getName());
+ if (isLocalRoot()) {
+ String mountPath = provider.getMountPath();
+ if (mountPath != null) {
+ Content mountPoint = getSession().getMountPoint(mountPath);
+ res.addAll(mountPoint.getContentClasses());
+ }
+ } else {
+ res.add(getName());
+ }
return res;
}
+ @Override
+ public void addContentClasses(QName... contentClass) {
+ if (isLocalRoot()) {
+ String mountPath = provider.getMountPath();
+ if (mountPath != null) {
+ Content mountPoint = getSession().getMountPoint(mountPath);
+ mountPoint.addContentClasses(contentClass);
+ }
+ } else {
+ super.addContentClasses(contentClass);
+ }
+ }
+
/*
* MOUNT MANAGEMENT
*/