X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms.jcr%2Fsrc%2Forg%2Fargeo%2Fcms%2Fjcr%2Facr%2FJcrContent.java;h=6a0cccb84fb8e9d0ae976a163e6f983c12e566bb;hb=8f85fc13d0d2cd5ce8c97ba8e4d0838e74928d08;hp=30590640e05cc6a29b43f5b22eeeb90863a2b7a5;hpb=ceee6c4ebe8e30e19cf3a1cdae3267cbd8840d62;p=gpl%2Fargeo-jcr.git diff --git a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java index 3059064..6a0cccb 100644 --- a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java +++ b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java @@ -3,63 +3,94 @@ package org.argeo.cms.jcr.acr; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; +import java.time.Instant; import java.util.ArrayList; +import java.util.Arrays; import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.TreeSet; +import java.util.UUID; import java.util.concurrent.ForkJoinPool; +import javax.jcr.Binary; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.PropertyType; import javax.jcr.RepositoryException; +import javax.jcr.Session; import javax.jcr.Value; +import javax.jcr.ValueFactory; import javax.jcr.nodetype.NodeType; +import javax.jcr.nodetype.NodeTypeManager; import javax.xml.namespace.QName; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import org.argeo.api.acr.Content; +import org.argeo.api.acr.CrAttributeType; +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.ProvidedSession; import org.argeo.api.cms.CmsConstants; import org.argeo.cms.acr.AbstractContent; -import org.argeo.cms.acr.ContentUtils; +import org.argeo.cms.acr.CmsContent; +import org.argeo.cms.util.AsyncPipedOutputStream; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.JcrxApi; /** A JCR {@link Node} accessed as {@link Content}. */ public class JcrContent extends AbstractContent { -// private Node jcrNode; - private JcrContentProvider provider; private String jcrWorkspace; private String jcrPath; + private final boolean isMountBase; + + /* OPTIMISATIONS */ + /** + * While we want to support thread-safe access, it is very likely that only one + * thread and only one session will be used (typically from a single-threaded + * UI). We therefore cache was long as the same thread is calling. + */ + private Thread lastRetrievingThread = null; + private Node cachedNode = null; + private boolean caching = true; + protected JcrContent(ProvidedSession session, JcrContentProvider provider, String jcrWorkspace, String jcrPath) { super(session); this.provider = provider; this.jcrWorkspace = jcrWorkspace; this.jcrPath = jcrPath; + + this.isMountBase = "/".equals(jcrPath); } + /* + * READ + */ + @Override public QName getName() { String name = Jcr.getName(getJcrNode()); if (name.equals("")) {// root String mountPath = provider.getMountPath(); - name = ContentUtils.getParentPath(mountPath)[1]; + name = CmsContent.getParentPath(mountPath)[1]; // name = Jcr.getWorkspaceName(getJcrNode()); } return NamespaceUtils.parsePrefixedName(provider, name); @@ -68,10 +99,52 @@ public class JcrContent extends AbstractContent { @SuppressWarnings("unchecked") @Override public Optional get(QName key, Class clss) { - if (isDefaultAttrTypeRequested(clss)) { - return Optional.of((A) get(getJcrNode(), key.toString())); + try { + Node node = getJcrNode(); + if (DName.creationdate.equals(key)) + key = JcrName.created.qName(); + else if (DName.getlastmodified.equals(key)) + key = JcrName.lastModified.qName(); + else if (DName.getcontenttype.equals(key)) { + String contentType = null; + if (node.isNodeType(NodeType.NT_FILE)) { + Node content = node.getNode(Node.JCR_CONTENT); + if (content.isNodeType(NodeType.MIX_MIMETYPE)) { + contentType = content.getProperty(Property.JCR_MIMETYPE).getString(); + if (content.hasProperty(Property.JCR_ENCODING)) + contentType = contentType + ";encoding=" + + content.getProperty(Property.JCR_ENCODING).getString(); + } + } + if (contentType == null) + contentType = "application/octet-stream"; + return CrAttributeType.cast(clss, contentType); + } else if (DName.checkedOut.equals(key)) { + if (!node.hasProperty(Property.JCR_IS_CHECKED_OUT)) + return Optional.empty(); + boolean isCheckedOut = node.getProperty(Property.JCR_IS_CHECKED_OUT).getBoolean(); + if (!isCheckedOut) + return Optional.empty(); + // FIXME return URI + return (Optional) Optional.of(new Object()); + } else if (DName.checkedIn.equals(key)) { + if (!node.hasProperty(Property.JCR_IS_CHECKED_OUT)) + return Optional.empty(); + boolean isCheckedOut = node.getProperty(Property.JCR_IS_CHECKED_OUT).getBoolean(); + if (isCheckedOut) + return Optional.empty(); + // FIXME return URI + return (Optional) Optional.of(new Object()); + } + + Object value = get(node, key.toString()); + if (value instanceof List lst) + return Optional.of((A) lst); + // TODO check other collections? + return CrAttributeType.cast(clss, value); + } catch (RepositoryException e) { + throw new JcrException(e); } - return Optional.of((A) Jcr.get(getJcrNode(), key.toString())); } @Override @@ -86,12 +159,25 @@ public class JcrContent extends AbstractContent { @Override protected Iterable keys() { try { + Node node = getJcrNode(); Set keys = new HashSet<>(); - for (PropertyIterator propertyIterator = getJcrNode().getProperties(); propertyIterator.hasNext();) { + for (PropertyIterator propertyIterator = node.getProperties(); propertyIterator.hasNext();) { Property property = propertyIterator.nextProperty(); + QName name = NamespaceUtils.parsePrefixedName(provider, property.getName()); + // TODO convert standard names + if (property.getName().equals(Property.JCR_CREATED)) + name = DName.creationdate.qName(); + else if (property.getName().equals(Property.JCR_LAST_MODIFIED)) + name = DName.getlastmodified.qName(); + else if (property.getName().equals(Property.JCR_MIMETYPE)) + name = DName.getcontenttype.qName(); + else if (property.getName().equals(Property.JCR_IS_CHECKED_OUT)) { + boolean isCheckedOut = node.getProperty(Property.JCR_IS_CHECKED_OUT).getBoolean(); + name = isCheckedOut ? DName.checkedOut.qName() : DName.checkedIn.qName(); + } + // TODO skip technical properties - QName name = NamespaceUtils.parsePrefixedName(provider, property.getName()); keys.add(name); } return keys; @@ -100,18 +186,11 @@ public class JcrContent extends AbstractContent { } } - public Node getJcrNode() { - try { - // TODO caching? - return provider.getJcrSession(getSession(), jcrWorkspace).getNode(jcrPath); - } catch (RepositoryException e) { - throw new JcrException("Cannot retrieve " + jcrPath + " from workspace " + jcrWorkspace, e); - } - } - /** Cast to a standard Java object. */ static Object get(Node node, String property) { try { + if (!node.hasProperty(property)) + return null; Property p = node.getProperty(property); if (p.isMultiple()) { Value[] values = p.getValues(); @@ -125,57 +204,25 @@ public class JcrContent extends AbstractContent { return convertSingleValue(value); } } catch (RepositoryException e) { - throw new JcrException("Cannot cast value from " + property + " of node " + node, e); - } - } - - static Object convertSingleValue(Value value) throws RepositoryException { - switch (value.getType()) { - case PropertyType.STRING: - return value.getString(); - case PropertyType.DOUBLE: - return (Double) value.getDouble(); - case PropertyType.LONG: - return (Long) value.getLong(); - case PropertyType.BOOLEAN: - return (Boolean) value.getBoolean(); - case PropertyType.DATE: - Calendar calendar = value.getDate(); - return calendar.toInstant(); - case PropertyType.BINARY: - throw new IllegalArgumentException("Binary is not supported as an attribute"); - default: - return value.getString(); + throw new JcrException("Cannot cast value from " + property + " of " + node, e); } } - class JcrContentIterator implements Iterator { - private final NodeIterator nodeIterator; - // we keep track in order to be able to delete it - private JcrContent current = null; - - protected JcrContentIterator(NodeIterator nodeIterator) { - this.nodeIterator = nodeIterator; - } - - @Override - public boolean hasNext() { - return nodeIterator.hasNext(); - } - - @Override - public Content next() { - current = new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getPath(nodeIterator.nextNode())); - return current; - } - - @Override - public void remove() { - if (current != null) { - Jcr.remove(current.getJcrNode()); + @Override + public boolean isMultiple(QName key) { + Node node = getJcrNode(); + String p = NamespaceUtils.toFullyQualified(key); + try { + if (node.hasProperty(p)) { + Property property = node.getProperty(p); + return property.isMultiple(); + } else { + return false; } + } catch (RepositoryException e) { + throw new JcrException( + "Cannot check multiplicity of property " + p + " of " + jcrPath + " in " + jcrWorkspace, e); } - } @Override @@ -183,7 +230,7 @@ public class JcrContent extends AbstractContent { try { // Note: it is important to to use the default way (recursing through parents), // since the session may not have access to parent nodes - return ContentUtils.ROOT_SLASH + jcrWorkspace + getJcrNode().getPath(); + return Content.ROOT_PATH + jcrWorkspace + getJcrNode().getPath(); } catch (RepositoryException e) { throw new JcrException("Cannot get depth of " + getJcrNode(), e); } @@ -200,38 +247,114 @@ public class JcrContent extends AbstractContent { @Override public Content getParent() { - if (Jcr.isRoot(getJcrNode())) // root - return null; + if (isMountBase) { + String mountPath = provider.getMountPath(); + if (mountPath == null || mountPath.equals("/")) + return null; + String[] parent = CmsContent.getParentPath(mountPath); + return getSession().get(parent[0]); + } +// if (Jcr.isRoot(getJcrNode())) // root +// return null; return new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getParentPath(getJcrNode())); } + @Override + public int getSiblingIndex() { + return Jcr.getIndex(getJcrNode()); + } + + @Override + public String getText() { + return JcrxApi.getXmlValue(getJcrNode()); + } + + /* + * MAP OPTIMISATIONS + */ + @Override + public boolean containsKey(Object key) { + return Jcr.hasProperty(getJcrNode(), key.toString()); + } + + /* + * WRITE + */ + + protected Node openForEdit() { + Node node = getProvider().openForEdit(getSession(), jcrWorkspace, jcrPath); + getSession().notifyModification(this); + return node; + } + @Override public Content add(QName name, QName... classes) { - if (classes.length > 0) { - QName primaryType = classes[0]; - Node child = Jcr.addNode(getJcrNode(), name.toString(), primaryType.toString()); - for (int i = 1; i < classes.length; i++) { - try { + try { + Node node = openForEdit(); + Node child; + if (classes.length > 0) { + classes: for (int i = 0; i < classes.length; i++) { + if (classes[i].equals(DName.collection.qName())) { + List lst = new ArrayList<>(Arrays.asList(classes)); + lst.add(0, NtType.folder.qName()); + lst.remove(DName.collection.qName()); + classes = lst.toArray(new QName[lst.size()]); + break classes; + } + } + QName primaryType = classes[0]; + child = Jcr.addNode(node, name.toString(), primaryType.toString()); + + for (int i = 1; i < classes.length; i++) child.addMixin(classes[i].toString()); - } catch (RepositoryException e) { - throw new JcrException("Cannot add child to " + getJcrNode(), e); + + if (NtType.file.qName().equals(primaryType)) { + // TODO optimise when we have a proper save mechanism + child.addNode(Node.JCR_CONTENT, NodeType.NT_UNSTRUCTURED); } + } else { + child = Jcr.addNode(node, name.toString(), NodeType.NT_UNSTRUCTURED); } + saveEditedNode(node); + return new JcrContent(getSession(), provider, jcrWorkspace, child.getPath()); + } catch (RepositoryException e) { + throw new JcrException("Cannot add child to " + jcrPath + " in " + jcrWorkspace, e); + } + } - } else { - Jcr.addNode(getJcrNode(), name.toString(), NodeType.NT_UNSTRUCTURED); + @Override + public Content add(QName name, Map attrs, QName... classes) { + if (attrs.containsKey(DName.getcontenttype.qName())) { + List lst = new ArrayList<>(Arrays.asList(classes)); + lst.add(0, NtType.file.qName()); + classes = lst.toArray(new QName[lst.size()]); } - return null; + + Content child = add(name, classes); + child.putAll(attrs); + return child; } @Override public void remove() { - Jcr.remove(getJcrNode()); + Node node = openForEdit(); + Jcr.remove(node); + saveEditedNode(node); + } + + private void saveEditedNode(Node node) { + try { + node.getSession().save(); + getJcrSession().refresh(true); + } catch (RepositoryException e) { + throw new JcrException("Cannot persist " + jcrPath + " in " + jcrWorkspace, e); + } } @Override protected void removeAttr(QName key) { - Property property = Jcr.getProperty(getJcrNode(), key.toString()); + Node node = openForEdit(); + Property property = Jcr.getProperty(node, key.toString()); if (property != null) { try { property.remove(); @@ -239,23 +362,117 @@ public class JcrContent extends AbstractContent { throw new JcrException("Cannot remove property " + key + " from " + getJcrNode(), e); } } + saveEditedNode(node); + } + @Override + public Object put(QName key, Object value) { + Objects.requireNonNull(value, "Value cannot be null"); + Node node = openForEdit(); + try { + if (DName.checkedIn.equals(key) || DName.checkedOut.equals(key)) + throw new IllegalArgumentException( + key + " cannot be set, use the openForEdit/freeze methods of the related content provider."); + + Object old = null; + String property; + if (DName.creationdate.equals(key)) + property = Property.JCR_CREATED; + else if (DName.getlastmodified.equals(key)) + property = Property.JCR_LAST_MODIFIED; + else if (DName.getcontenttype.equals(key)) { + if (!node.isNodeType(NodeType.NT_FILE)) + throw new IllegalStateException(DName.getcontenttype + " can only be set on a file"); + Node content = node.getNode(Node.JCR_CONTENT); + old = Jcr.get(content, Property.JCR_MIMETYPE); + if (old != null && Jcr.hasProperty(content, Property.JCR_ENCODING)) + old = old + ";encoding=" + Jcr.get(content, Property.JCR_ENCODING); + String[] str = value.toString().split(";"); + String mimeType = str[0].trim(); + String encoding = null; + if (str.length > 1) { + value = str[0].trim(); + String[] eq = str[1].split("="); + assert eq.length == 2; + if ("encoding".equals(eq[0].trim())) + encoding = eq[1]; + } + content.setProperty(Property.JCR_MIMETYPE, mimeType); + if (encoding != null) + content.setProperty(Property.JCR_ENCODING, encoding); + property = null; + } else + property = NamespaceUtils.toFullyQualified(key); + + if (property != null) { + if (node.hasProperty(property)) { + old = convertSingleValue(node.getProperty(property).getValue()); + } + Value newValue = convertSingleObject(node.getSession().getValueFactory(), value); + node.setProperty(property, newValue); + } + // FIXME proper edition + saveEditedNode(node); + return old; + } catch (RepositoryException e) { + throw new JcrException("Cannot set property " + key + " on " + jcrPath + " in " + jcrWorkspace, e); + } } - boolean exists() { + @Override + public void addContentClasses(QName... contentClass) throws IllegalArgumentException, JcrException { try { - return provider.getJcrSession(getSession(), jcrWorkspace).itemExists(jcrPath); + Node node = openForEdit(); + NodeTypeManager ntm = node.getSession().getWorkspace().getNodeTypeManager(); + List nodeTypes = new ArrayList<>(); + for (QName clss : contentClass) { + NodeType nodeType = ntm.getNodeType(NamespaceUtils.toFullyQualified(clss)); + if (!nodeType.isMixin()) + throw new IllegalArgumentException(clss + " is not a mixin"); + nodeTypes.add(nodeType); + } + for (NodeType nodeType : nodeTypes) { + node.addMixin(nodeType.getName()); + } + // FIXME proper edition + saveEditedNode(node); + } catch (RepositoryException e) { + throw new JcrException( + "Cannot add content classes " + contentClass + " to " + jcrPath + " in " + jcrWorkspace, e); + } + } + + /* + * ACCESS + */ + protected boolean exists() { + try { + return getJcrSession().itemExists(jcrPath); } catch (RepositoryException e) { throw new JcrException("Cannot check whether " + jcrPath + " exists", e); } } + @Override + public boolean isParentAccessible() { + String jcrParentPath = CmsContent.getParentPath(jcrPath)[0]; + if ("".equals(jcrParentPath)) // JCR root node + jcrParentPath = "/"; + try { + return getJcrSession().hasPermission(jcrParentPath, Session.ACTION_READ); + } catch (RepositoryException e) { + throw new JcrException("Cannot check whether parent " + jcrParentPath + " is accessible", e); + } + } + /* * ADAPTERS */ @SuppressWarnings("unchecked") public A adapt(Class clss) { - if (Source.class.isAssignableFrom(clss)) { + if (Node.class.isAssignableFrom(clss)) { + return (A) getJcrNode(); + } else if (Source.class.isAssignableFrom(clss)) { // try { PipedOutputStream out = new PipedOutputStream(); PipedInputStream in; @@ -268,7 +485,7 @@ public class JcrContent extends AbstractContent { ForkJoinPool.commonPool().execute(() -> { // try (PipedOutputStream out = new PipedOutputStream(in)) { try { - provider.getJcrSession(getSession(), jcrWorkspace).exportDocumentView(jcrPath, out, true, false); + getJcrSession().exportDocumentView(jcrPath, out, true, false); out.flush(); out.close(); } catch (IOException | RepositoryException e) { @@ -280,29 +497,50 @@ public class JcrContent extends AbstractContent { // } catch (IOException e) { // throw new RuntimeException("Cannot adapt " + JcrContent.this + " to " + clss, e); // } - } else - + } else { return super.adapt(clss); + } } @SuppressWarnings("unchecked") @Override public C open(Class clss) throws IOException, IllegalArgumentException { - if (InputStream.class.isAssignableFrom(clss)) { - Node node = getJcrNode(); - if (Jcr.isNodeType(node, NodeType.NT_FILE)) { - try { + try { + if (InputStream.class.isAssignableFrom(clss)) { + Node node = getJcrNode(); +// System.out.println(node.getSession()); + if (Jcr.isNodeType(node, NodeType.NT_FILE)) { return (C) JcrUtils.getFileAsStream(node); - } catch (RepositoryException e) { - throw new JcrException("Cannot open " + jcrPath + " in workspace " + jcrWorkspace, e); + } + } else if (OutputStream.class.isAssignableFrom(clss)) { + Node node = openForEdit(); +// System.out.println(node.getSession()); + if (Jcr.isNodeType(node, NodeType.NT_FILE)) { + Node content = node.getNode(Node.JCR_CONTENT); + AsyncPipedOutputStream out = new AsyncPipedOutputStream(); + + ValueFactory valueFactory = getJcrSession().getValueFactory(); + out.asyncRead((in) -> { + try { + Binary binary = valueFactory.createBinary(in); + content.setProperty(Property.JCR_DATA, binary); + saveEditedNode(node); + } catch (RepositoryException e) { + throw new JcrException( + "Cannot create binary in " + jcrPath + " in workspace " + jcrWorkspace, e); + } + }); + return (C) out; } } + } catch (RepositoryException e) { + throw new JcrException("Cannot open " + jcrPath + " in workspace " + jcrWorkspace, e); } return super.open(clss); } @Override - public ContentProvider getProvider() { + public JcrContentProvider getProvider() { return provider; } @@ -318,22 +556,101 @@ public class JcrContent extends AbstractContent { /* * TYPING */ + + static Object convertSingleValue(Value value) throws JcrException, IllegalArgumentException { + try { + switch (value.getType()) { + case PropertyType.STRING: + return value.getString(); + case PropertyType.DOUBLE: + return (Double) value.getDouble(); + case PropertyType.LONG: + return (Long) value.getLong(); + case PropertyType.BOOLEAN: + return (Boolean) value.getBoolean(); + case PropertyType.DATE: + Calendar calendar = value.getDate(); + return calendar.toInstant(); + case PropertyType.BINARY: + throw new IllegalArgumentException("Binary is not supported as an attribute"); + default: + return value.getString(); + } + } catch (RepositoryException e) { + throw new JcrException("Cannot convert " + value + " to an object.", e); + } + } + + static Value convertSingleObject(ValueFactory factory, Object value) { + if (value instanceof String string) { + return factory.createValue(string); + } else if (value instanceof Double dbl) { + return factory.createValue(dbl); + } else if (value instanceof Float flt) { + return factory.createValue(flt); + } else if (value instanceof Long lng) { + return factory.createValue(lng); + } else if (value instanceof Integer intg) { + return factory.createValue(intg); + } else if (value instanceof Boolean bool) { + return factory.createValue(bool); + } else if (value instanceof Instant instant) { + GregorianCalendar calendar = new GregorianCalendar(); + calendar.setTime(Date.from(instant)); + return factory.createValue(calendar); + } else { + // TODO or use String by default? + throw new IllegalArgumentException("Unsupported value " + value.getClass()); + } + } + + @Override + public Class getType(QName key) { + Node node = getJcrNode(); + String p = NamespaceUtils.toFullyQualified(key); + try { + if (node.hasProperty(p)) { + Property property = node.getProperty(p); + return switch (property.getType()) { + case PropertyType.STRING: + case PropertyType.NAME: + case PropertyType.PATH: + case PropertyType.DECIMAL: + yield String.class; + case PropertyType.LONG: + yield Long.class; + case PropertyType.DOUBLE: + yield Double.class; + case PropertyType.BOOLEAN: + yield Boolean.class; + case PropertyType.DATE: + yield Instant.class; + case PropertyType.WEAKREFERENCE: + case PropertyType.REFERENCE: + yield UUID.class; + default: + yield Object.class; + }; + } else { + // TODO does it make sense? + return Object.class; + } + } catch (RepositoryException e) { + throw new JcrException("Cannot get type of property " + p + " of " + jcrPath + " in " + jcrWorkspace, e); + } + } + @Override public List getContentClasses() { try { -// Node node = getJcrNode(); -// List res = new ArrayList<>(); -// res.add(nodeTypeToQName(node.getPrimaryNodeType())); -// for (NodeType mixin : node.getMixinNodeTypes()) { -// res.add(nodeTypeToQName(mixin)); -// } -// return res; Node context = getJcrNode(); List res = new ArrayList<>(); // primary node type NodeType primaryType = context.getPrimaryNodeType(); res.add(nodeTypeToQName(primaryType)); + if (primaryType.getName().equals(NodeType.NT_FOLDER)) + res.add(DName.collection.qName()); Set secondaryTypes = new TreeSet<>(NamespaceUtils.QNAME_COMPARATOR); for (NodeType mixinType : context.getMixinNodeTypes()) { @@ -348,15 +665,6 @@ public class JcrContent extends AbstractContent { secondaryTypes.add(nodeTypeToQName(superType)); } } -// // entity type -// if (context.isNodeType(EntityType.entity.get())) { -// if (context.hasProperty(EntityNames.ENTITY_TYPE)) { -// String entityTypeName = context.getProperty(EntityNames.ENTITY_TYPE).getString(); -// if (byType.containsKey(entityTypeName)) { -// types.add(entityTypeName); -// } -// } -// } res.addAll(secondaryTypes); return res; } catch (RepositoryException e) { @@ -366,12 +674,40 @@ public class JcrContent extends AbstractContent { private QName nodeTypeToQName(NodeType nodeType) { String name = nodeType.getName(); - return QName.valueOf(name); + return NamespaceUtils.parsePrefixedName(provider, name); + // return QName.valueOf(name); } - @Override - public int getSiblingIndex() { - return Jcr.getIndex(getJcrNode()); + /* + * COMMON UTILITIES + */ + protected Session getJcrSession() { + Session s = provider.getJcrSession(getSession(), jcrWorkspace); +// if (getSession().isEditing()) +// try { +// s.refresh(false); +// } catch (RepositoryException e) { +// throw new JcrException("Cannot refresh session", e); +// } + return s; + } + + protected Node getJcrNode() { + try { + if (caching) { + synchronized (this) { + if (lastRetrievingThread != Thread.currentThread()) { + cachedNode = getJcrSession().getNode(jcrPath); + lastRetrievingThread = Thread.currentThread(); + } + return cachedNode; + } + } else { + return getJcrSession().getNode(jcrPath); + } + } catch (RepositoryException e) { + throw new JcrException("Cannot retrieve " + jcrPath + " from workspace " + jcrWorkspace, e); + } } /* @@ -386,10 +722,43 @@ public class JcrContent extends AbstractContent { if (contentSession == null) throw new IllegalArgumentException( "Cannot adapt " + node + " to content, because it was not loaded from a content session"); - return contentSession.get(ContentUtils.SLASH + CmsConstants.SYS_WORKSPACE + node.getPath()); + return contentSession.get(Content.ROOT_PATH + CmsConstants.SYS_WORKSPACE + node.getPath()); } catch (RepositoryException e) { throw new JcrException("Cannot adapt " + node + " to a content", e); } } + /* + * CONTENT ITERATOR + */ + + class JcrContentIterator implements Iterator { + private final NodeIterator nodeIterator; + // we keep track in order to be able to delete it + private JcrContent current = null; + + protected JcrContentIterator(NodeIterator nodeIterator) { + this.nodeIterator = nodeIterator; + } + + @Override + public boolean hasNext() { + return nodeIterator.hasNext(); + } + + @Override + public Content next() { + current = new JcrContent(getSession(), provider, jcrWorkspace, Jcr.getPath(nodeIterator.nextNode())); + return current; + } + + @Override + public void remove() { + if (current != null) { + Jcr.remove(current.getJcrNode()); + } + } + + } + }