diff --git a/src/main/java/com/twcable/jackalope/impl/common/Paths.java b/src/main/java/com/twcable/jackalope/impl/common/Paths.java index 70c4f2b..43ee1a4 100644 --- a/src/main/java/com/twcable/jackalope/impl/common/Paths.java +++ b/src/main/java/com/twcable/jackalope/impl/common/Paths.java @@ -160,4 +160,24 @@ public static String resolve(String first, String second) { private static String stripRoot(String path) { return isAbsolute(path) ? path.substring(1) : path; } + + /** + * Strip a trailing '/' on a path. + * + * Items saved in the JCR can be accessed as both /some/path and + * /some/path/ but the standard output is w/o the trailing '/'. + * + * Note that the root path '/' is preserved as '/'. + * + * @param path The path to be modified + * @return The path without a trailing '/' if one is found. + */ + public static String stripTrailingSeparator(String path) { + if (path != null && !path.equals(SEPARATOR)) { + if (path.lastIndexOf(SEPARATOR) == (path.length() - 1)) { + return path.substring(0, path.length() - 1); + } + } + return path; + } } diff --git a/src/main/java/com/twcable/jackalope/impl/jcr/ItemImpl.java b/src/main/java/com/twcable/jackalope/impl/jcr/ItemImpl.java index d74d6c4..1a47312 100644 --- a/src/main/java/com/twcable/jackalope/impl/jcr/ItemImpl.java +++ b/src/main/java/com/twcable/jackalope/impl/jcr/ItemImpl.java @@ -54,7 +54,7 @@ public abstract class ItemImpl implements Item { */ ItemImpl(@Nonnull SessionImpl session, @Nonnull String path) throws ItemNotFoundException, ItemExistsException { this.session = session; - this.path = path; + this.path = Paths.stripTrailingSeparator(path); session.addItem(this); } @@ -67,7 +67,7 @@ public String getPath() { void setPath(String path) { - this.path = path; + this.path = Paths.stripTrailingSeparator(path); } diff --git a/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java b/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java index 7235e61..22c0884 100644 --- a/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java +++ b/src/main/java/com/twcable/jackalope/impl/jcr/SessionImpl.java @@ -149,19 +149,19 @@ public Property getProperty(String absPath) throws PathNotFoundException { @Override public boolean itemExists(String absPath) { - return itemStore.containsKey(absPath); + return itemStore.containsKey(Paths.stripTrailingSeparator(absPath)); } @Override public boolean nodeExists(String absPath) { - return itemExists(absPath) && itemStore.get(absPath).isNode(); + return itemExists(absPath) && itemStore.get(Paths.stripTrailingSeparator(absPath)).isNode(); } @Override public boolean propertyExists(String absPath) { - return itemExists(absPath) && !itemStore.get(absPath).isNode(); + return itemExists(absPath) && !itemStore.get(Paths.stripTrailingSeparator(absPath)).isNode(); } @@ -353,7 +353,7 @@ Item removeItem(@Nonnull ItemImpl item) { private ItemImpl getItemImpl(String absPath) { - return itemStore.get(absPath); + return itemStore.get(Paths.stripTrailingSeparator(absPath)); } @@ -364,6 +364,9 @@ private Item storeItem(@Nonnull ItemImpl item) throws ItemNotFoundException { private void moveItem(String src, String dest) { + src = Paths.stripTrailingSeparator(src); + dest = Paths.stripTrailingSeparator(dest); + ItemImpl item = itemStore.get(src); item.setPath(dest); itemStore.put(dest, item); diff --git a/src/test/groovy/com/twcable/jackalope/impl/common/PathsSpec.groovy b/src/test/groovy/com/twcable/jackalope/impl/common/PathsSpec.groovy index 86c3d96..c17976a 100644 --- a/src/test/groovy/com/twcable/jackalope/impl/common/PathsSpec.groovy +++ b/src/test/groovy/com/twcable/jackalope/impl/common/PathsSpec.groovy @@ -169,4 +169,20 @@ class PathsSpec extends Specification { "/segment" | "next" | "/segment/next" "/segment" | "/next" | "/next" } + + def "strip trailing separator"() { + expect: + Paths.stripTrailingSeparator(path) == expected + + where: + path | expected + null | null + "/" | "/" + "/segment" | "/segment" + "/segment/" | "/segment" + "/segment/next" | "/segment/next" + "/segment/next/" | "/segment/next" + + + } }