You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2016/10/17 16:16:02 UTC

svn commit: r1765329 - in /sling/trunk/bundles/api/src: main/java/org/apache/sling/api/resource/path/Path.java test/java/org/apache/sling/api/resource/path/PathTest.java

Author: cziegeler
Date: Mon Oct 17 16:16:02 2016
New Revision: 1765329

URL: http://svn.apache.org/viewvc?rev=1765329&view=rev
Log:
SLING-6161 : Correctly implement Path.matches

Modified:
    sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/path/Path.java
    sling/trunk/bundles/api/src/test/java/org/apache/sling/api/resource/path/PathTest.java

Modified: sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/path/Path.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/path/Path.java?rev=1765329&r1=1765328&r2=1765329&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/path/Path.java (original)
+++ sling/trunk/bundles/api/src/main/java/org/apache/sling/api/resource/path/Path.java Mon Oct 17 16:16:02 2016
@@ -46,7 +46,9 @@ public class Path implements Comparable<
      *     <li>The {@code **} characters match zero or more characters crossing directory boundaries.</li>
      * </ul>
      *
-     * @param path the resource path or a glob pattern.
+     * @param path The resource path or a glob pattern.
+     * @throws NullPointerException If {@code otherPath} is {@code null}
+     * @throws IllegalArgumentException If the provided path is not absolute, or if the glob pattern does not start with a slash.
      */
     public Path(@Nonnull final String path) {
         if ( path.equals("/") ) {
@@ -56,23 +58,42 @@ public class Path implements Comparable<
         } else {
             this.path = path;
         }
-        if (path.startsWith(GLOB_PREFIX)) {
-            isPattern = true;
-            regexPattern = Pattern.compile(toRegexPattern(path.substring(5)));
-            String patternPath = path.substring(GLOB_PREFIX.length());
-            this.prefix = patternPath.equals("/") ? "/" : patternPath.concat("/");
+        if (this.path.startsWith(GLOB_PREFIX)) {
+            final String patternPath = path.substring(GLOB_PREFIX.length());
+            this.isPattern = true;
+            this.regexPattern = Pattern.compile(toRegexPattern(patternPath));
+            int lastSlash = 0;
+            int pos = 1;
+            while ( patternPath.length() > pos ) {
+                final char c = patternPath.charAt(pos);
+                if ( c == '/') {
+                    lastSlash = pos;
+                } else if ( c == '*') {
+                    break;
+                }
+                pos++;
+            }
+
+            this.prefix = (pos == patternPath.length() ? patternPath : patternPath.substring(0, lastSlash + 1));
         } else {
-            isPattern = false;
-            regexPattern = null;
+            this.isPattern = false;
+            this.regexPattern = null;
             this.prefix = this.path.equals("/") ? "/" : this.path.concat("/");
         }
+        if ( !this.prefix.startsWith("/") ) {
+            throw new IllegalArgumentException("Path must be absolute: " + path);
+        }
     }
 
     /**
      * If this {@code Path} object holds a path (and not a pattern), this method
-     * check whether the provided path is equal to this path or a sub path of it.
+     * checks whether the provided path is equal to this path or a sub path of it.
+     * If a glob pattern is provided as the argument, it performs the same check
+     * and respects the provided pattern.
      * If this {@code Path} object holds a pattern, it checks whether the
-     * provided path matches the pattern.
+     * provided path matches the pattern. If this path object holds a pattern
+     * and a pattern is provided as the argument, it returns only {@code true}
+     * if the pattern is the same.
      * If the provided argument is not an absolute path (e.g. if it is a relative
      * path or a pattern), this method returns {@code false}.
      *
@@ -80,11 +101,57 @@ public class Path implements Comparable<
      * @return {@code true} If other path is within the sub tree of this path
      *         or matches the pattern.
      * @see Path#isPattern()
+     * @throws NullPointerException If {@code otherPath} is {@code null}
+     * @throws IllegalArgumentException If the provided path is not absolute, or if the glob pattern does not start with a slash.
      */
     public boolean matches(final String otherPath) {
-        if (isPattern) {
+        if ( otherPath.startsWith(GLOB_PREFIX) ) {
+            if ( this.isPattern ) {
+                // both are patterns, then they must be equal
+                final Path oPath = new Path(otherPath);
+                return this.regexPattern.equals(oPath.regexPattern);
+            }
+
+            // this is path, provided argument is a pattern
+            // simplest case - the prefix of the glob pattern matches already
+            // for example: this path = /apps
+            //              glob      = /apps/**
             final Path oPath = new Path(otherPath);
-            return this.regexPattern.equals(oPath.regexPattern) || this.regexPattern.matcher(otherPath).matches();
+            if ( this.matches(oPath.prefix) ) {
+                return true;
+            }
+            // count slashes in path
+            int count = 0;
+            for (int i=0; i < this.path.length(); i++) {
+                if (this.path.charAt(i) == '/') {
+                     count++;
+                }
+            }
+            // now create the substring of the glob pattern with the same amount of slashes
+            int start = GLOB_PREFIX.length();
+            while ( start < otherPath.length() ) {
+                if ( otherPath.charAt(start) == '/') {
+                    if ( count == 0 ) {
+                        break;
+                    }
+                    count--;
+                }
+                start++;
+            }
+            if ( count > 0 ) {
+                return false;
+            }
+            final String globPattern = otherPath.substring(0, start);
+            final Path globPatternPath = new Path(globPattern);
+            return globPatternPath.matches(this.path);
+        }
+
+        // provided argument is a path
+        if ( !otherPath.startsWith("/") ) {
+            throw new IllegalArgumentException("Path must be absolute: " + otherPath);
+        }
+        if (isPattern) {
+            return this.regexPattern.matcher(otherPath).matches();
         }
         return this.path.equals(otherPath) || otherPath.startsWith(this.prefix);
     }
@@ -92,7 +159,7 @@ public class Path implements Comparable<
     /**
      * Return the path if this {@code Path} object holds a path,
      * returns the pattern otherwise.
-     * @return The path.
+     * @return The path or pattern.
      * @see #isPattern()
      */
     public String getPath() {

Modified: sling/trunk/bundles/api/src/test/java/org/apache/sling/api/resource/path/PathTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/api/src/test/java/org/apache/sling/api/resource/path/PathTest.java?rev=1765329&r1=1765328&r2=1765329&view=diff
==============================================================================
--- sling/trunk/bundles/api/src/test/java/org/apache/sling/api/resource/path/PathTest.java (original)
+++ sling/trunk/bundles/api/src/test/java/org/apache/sling/api/resource/path/PathTest.java Mon Oct 17 16:16:02 2016
@@ -20,6 +20,7 @@ package org.apache.sling.api.resource.pa
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 
 import org.junit.Test;
 
@@ -70,8 +71,33 @@ public class PathTest {
 
     @Test public void testPatternRootMatching() {
         final Path path = new Path("/");
-        assertFalse(path.matches("glob:/apps/myproject/components/**/*.html"));
-        assertFalse(path.matches("glob:/apps/**/*.html"));
+        assertTrue(path.matches("glob:/apps/myproject/components/**/*.html"));
+        assertTrue(path.matches("glob:/apps/**/*.html"));
+    }
+
+    @Test public void testMatchesWithGlobPattern() {
+        final Path path = new Path("/apps/myproject");
+        assertTrue(path.matches("glob:/apps/myproject/components/**/*.html"));
+        assertTrue(path.matches("glob:/apps/**/components/**/*.html"));
+        assertFalse(path.matches("glob:/*/foo"));
+        assertTrue(path.matches("glob:/*/myproject"));
+        assertTrue(path.matches("glob:/*/*project"));
+        assertTrue(path.matches("glob:/*/*project/**.html"));
+    }
+
+    @Test public void testIllegalArgumentException() {
+        try {
+            new Path("foo");
+            fail();
+        } catch ( final IllegalArgumentException iae) {
+            // this should happen!
+        }
+        try {
+            new Path("glob:foo");
+            fail();
+        } catch ( final IllegalArgumentException iae) {
+            // this should happen!
+        }
     }
 
     @Test public void testPathMatchingTrailingSlash() {