You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2008/07/08 16:37:23 UTC

svn commit: r674843 - in /jackrabbit/trunk/jackrabbit-spi-commons/src: main/java/org/apache/jackrabbit/spi/commons/name/ test/java/org/apache/jackrabbit/spi/commons/name/

Author: tripod
Date: Tue Jul  8 07:37:23 2008
New Revision: 674843

URL: http://svn.apache.org/viewvc?rev=674843&view=rev
Log:
JCR-1662 Add pattern matching for paths

Added:
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/MatchResult.java   (with props)
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Matcher.java   (with props)
    jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Pattern.java   (with props)
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/MatcherTest.java   (with props)
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PatternTest.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/MatchResult.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/MatchResult.java?rev=674843&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/MatchResult.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/MatchResult.java Tue Jul  8 07:37:23 2008
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi.commons.name;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Path;
+
+/**
+ * A MatchResult instance represents the result of matching a {@link Pattern} against
+ * a {@link Path}.
+ */
+public class MatchResult {
+    private final Path path;
+    private final int pathLength;
+    private int matchPos;
+    private final int matchLength;
+
+    MatchResult(Path path, int length) {
+        this(path, 0, length);
+    }
+
+    MatchResult(Path path, int pos, int length) {
+        super();
+        if (!path.isNormalized()) {
+            throw new IllegalArgumentException("Path not normalized");
+        }
+        this.path = path;
+        this.matchPos = pos;
+        this.matchLength = length;
+        this.pathLength = path.getLength();
+    }
+
+    /**
+     * Returns the remaining path after the matching part.
+     * @return  The remaining path after the matching part such that the path constructed from
+     *   {@link #getMatch()} followed by {@link #getRemainder()} is the original path or
+     *   <code>null</code> if {@link #isFullMatch()} is <code>true</code>.
+     */
+    public Path getRemainder() {
+        if (matchPos + matchLength >= pathLength) {
+            return null;
+        }
+        else {
+            try {
+                return path.subPath(matchPos + matchLength, pathLength);
+            }
+            catch (RepositoryException e) {
+                throw (IllegalStateException) new IllegalStateException("Path not normalized")
+                        .initCause(e);
+            }
+        }
+    }
+
+    /**
+     * Returns the path which was matched by the {@link Pattern}.
+     * @return The path which was matched such that the path constructed from
+     *   {@link #getMatch()} followed by {@link #getRemainder()} is the original path or
+     *   <code>null</code> if {@link #getMatchLength()} is <code>0</code>.
+     */
+    public Path getMatch() {
+        if (matchLength == 0) {
+            return null;
+        }
+        else {
+            try {
+                return path.subPath(matchPos, matchPos + matchLength);
+            }
+            catch (RepositoryException e) {
+                throw (IllegalStateException) new IllegalStateException("Path not normalized")
+                        .initCause(e);
+            }
+        }
+
+    }
+
+    /**
+     * Returns the position of the match
+     * @return
+     */
+    public int getMatchPos() {
+        return matchPos;
+    }
+
+    /**
+     * Returns the number of elements which where matched by the {@link Pattern}.
+     * @return
+     */
+    public int getMatchLength() {
+        return matchLength;
+    }
+
+    /**
+     * Returns true if the {@link Pattern} matched anything or false otherwise.
+     * @return
+     */
+    public boolean isMatch() {
+        return matchLength > 0;
+    }
+
+    /**
+     * Returns true if the {@link Pattern} matched the whole {@link Path}.
+     * @return
+     */
+    public boolean isFullMatch() {
+        return pathLength == matchLength;
+    }
+
+    MatchResult setPos(int matchPos) {
+        this.matchPos = matchPos;
+        return this;
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/MatchResult.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/MatchResult.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Matcher.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Matcher.java?rev=674843&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Matcher.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Matcher.java Tue Jul  8 07:37:23 2008
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi.commons.name;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Path;
+
+/**
+ * Utility class for matching {@link Pattern}s against {@link Path}es.
+ */
+public final class Matcher {
+
+    private Matcher() {
+        // Don't instantiate
+    }
+
+    /**
+     * Match a pattern against an input path and return the remaining path.
+     * @param pattern
+     * @param input
+     * @return The remaining path after the match or <code>null</code> if the whole path
+     *   was matched.
+     * @see MatchResult#getRemainder()
+     */
+    public static Path match(Pattern pattern, Path input) {
+        return pattern.match(input).getRemainder();
+    }
+
+    /**
+     * Checks whether a pattern matches an input path.
+     * @param pattern
+     * @param input
+     * @return <code>true</code> if <code>pattern</code> matches the whole <code>input</code>.
+     * @see MatchResult#isFullMatch()
+     */
+    public static boolean matches(Pattern pattern, Path input) {
+        return pattern.match(input).isFullMatch();
+    }
+
+    /**
+     * Find the first match of a pattern in a path.
+     * @param pattern
+     * @param input
+     * @return A {@link MatchResult} or null if the pattern does not occur in the
+     *   input.
+     * @throws IllegalArgumentException if <code>input</code> is not normalized.
+     */
+    public static MatchResult findMatch(Pattern pattern, Path input) {
+        return findMatch(pattern, input, 0);
+    }
+
+    /**
+     * Find the first match of a pattern in a path starting at a given position.
+     * @param pattern
+     * @param input
+     * @param pos
+     * @return A {@link MatchResult} or null if the pattern does not occur in the
+     *   input.
+     * @throws IllegalArgumentException if <code>input</code> is not normalized.
+     */
+    public static MatchResult findMatch(Pattern pattern, Path input, int pos) {
+        int length = input.getLength();
+        if (pos < 0 || pos >= length) {
+            throw new IllegalArgumentException("Index out of bounds");
+        }
+
+        try {
+            for (int k = pos; k < length; k++) {
+                Path path = input.subPath(k, length);
+                MatchResult result = pattern.match(path);
+                if (result.isMatch()) {
+                    return new MatchResult(input, k, result.getMatchLength());
+                }
+            }
+            return null;
+        }
+        catch (RepositoryException e) {
+            throw (IllegalArgumentException) new IllegalArgumentException("Path not normalizable")
+                    .initCause(e);
+        }
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Matcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Matcher.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Pattern.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Pattern.java?rev=674843&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Pattern.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Pattern.java Tue Jul  8 07:37:23 2008
@@ -0,0 +1,505 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi.commons.name;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.Path.Element;
+
+/**
+ * Pattern to match normalized {@link Path}s.
+ * A pattern matches either a constant path, a name of a path element, a selection of
+ * either of two patterns or a sequence of two patterns. The matching process is greedy.
+ * That is, whenever a match is not unique only the longest match is considered.
+ * Matching consumes as many elements from the beginning of an input path as possible and
+ * returns what's left as an instance of {@link MatchResult}.
+ * Use the {@link Matcher} class for matching a whole path or finding matches inside a path.
+ */
+public abstract class Pattern {
+
+    /**
+     * Matches this pattern against the input.
+     * @param input path to match with this pattern
+     * @return result from the matching <code>pattern</code> against <code>input</code>
+     * @throws IllegalArgumentException if <code>input</code> is not normalized
+     */
+    public MatchResult match(Path input) {
+        try {
+            return match(new Context(input)).getMatchResult();
+        }
+        catch (RepositoryException e) {
+            throw (IllegalArgumentException) new IllegalArgumentException("Path not normalized")
+                    .initCause(e);
+        }
+    }
+
+    protected abstract Context match(Context input) throws RepositoryException;
+
+    /**
+     * Construct a new pattern which matches an exact path
+     * @param path
+     * @return A pattern which matches <code>path</code> and nothing else
+     * @throws IllegalArgumentException if <code>path</code> is <code>null</code>
+     */
+    public static Pattern path(Path path) {
+        if (path == null) {
+            throw new IllegalArgumentException("path cannot be null");
+        }
+        return new PathPattern(path);
+    }
+
+    /**
+     * Construct a new pattern which matches a path element of a given name
+     * @param name
+     * @return A pattern which matches a path element with name <code>name</code>
+     * @throws IllegalArgumentException if <code>name</code> is <code>null</code>
+     */
+    public static Pattern name(Name name) {
+        if (name == null) {
+            throw new IllegalArgumentException("name cannot be null");
+        }
+        return new NamePattern(name);
+    }
+
+    /**
+     * Constructs a pattern which matches a path elements against regular expressions.
+     * @param namespaceUri A regular expression used for matching the name space URI of
+     *   a path element.
+     * @param localName A regular expression used for matching the local name of a path
+     *   element
+     * @return  A pattern which matches a path element if namespaceUri matches the
+     *   name space URI of the path element and localName matches the local name of the
+     *   path element.
+     * @throws IllegalArgumentException if either <code>namespaceUri</code> or
+     *   <code>localName</code> is <code>null</code>
+     *
+     * @see java.util.regex.Pattern
+     */
+    public static Pattern name(String namespaceUri, String localName) {
+        if (namespaceUri == null || localName == null) {
+            throw new IllegalArgumentException("neither namespaceUri nor localName can be null");
+        }
+        return new RegexPattern(namespaceUri, localName);
+    }
+
+    private static final Pattern ALL_PATTERN = new Pattern() {
+        protected Context match(Context input) {
+            return input.matchToEnd();
+        }
+
+        public String toString() {
+            return "[ALL]";
+        }
+
+    };
+
+    /**
+     * A pattern which matches all input.
+     * @return
+     */
+    public static Pattern all() {
+        return ALL_PATTERN;
+    }
+
+    private static final Pattern NOTHING_PATTERN = new Pattern() {
+        protected Context match(Context input) {
+            return input.match(0);
+        }
+
+        public String toString() {
+            return "[NOTHING]";
+        }
+    };
+
+    /**
+     * A pattern which matches nothing.
+     * @return
+     */
+    public static Pattern nothing() {
+        return NOTHING_PATTERN;
+    }
+
+    /**
+     * A pattern which matches <code>pattern1</code> followed by <code>pattern2</code> and
+     * returns the longer of the two matches.
+     * @param pattern1
+     * @param pattern2
+     * @return
+     * @throws IllegalArgumentException if either argument is <code>null</code>
+     */
+    public static Pattern selection(Pattern pattern1, Pattern pattern2) {
+        if (pattern1 == null || pattern2 == null) {
+            throw new IllegalArgumentException("Neither pattern can be null");
+        }
+        return new SelectPattern(pattern1, pattern2);
+    }
+
+    /**
+     * A pattern which matches <code>pattern1</code> followed by <code>pattern2</code>.
+     * @param pattern1
+     * @param pattern2
+     * @return
+     */
+    public static Pattern sequence(Pattern pattern1, Pattern pattern2) {
+        if (pattern1 == null || pattern2 == null) {
+            throw new IllegalArgumentException("Neither pattern can be null");
+        }
+        return new SequencePattern(pattern1, pattern2);
+    }
+
+    /**
+     * A pattern which matches <code>pattern</code> as many times as possible
+     * @param pattern
+     * @return
+     */
+    public static Pattern repeat(Pattern pattern) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("Pattern can not be null");
+        }
+        return new RepeatPattern(pattern);
+    }
+
+    /**
+     * A pattern which matches <code>pattern</code> as many times as possible
+     * but at least <code>min</code> times and at most <code>max</code> times.
+     * @param pattern
+     * @param min
+     * @param max
+     * @return
+     */
+    public static Pattern repeat(Pattern pattern, int min, int max) {
+        if (pattern == null) {
+            throw new IllegalArgumentException("Pattern can not be null");
+        }
+        return new RepeatPattern(pattern, min, max);
+    }
+
+    // -----------------------------------------------------< Context >---
+
+    private static class Context {
+        private final Path path;
+        private final int length;
+        private final int pos;
+        private final boolean isMatch;
+
+        public Context(Path path) {
+            super();
+            this.path = path;
+            length = path.getLength();
+            isMatch = false;
+            pos = 0;
+        }
+
+        public Context(Context context, int pos, boolean matched) {
+            path = context.path;
+            length = context.length;
+            this.pos = pos;
+            this.isMatch = matched;
+            if (pos > length) {
+                throw new IllegalArgumentException("Cannot match beyond end of input");
+            }
+        }
+
+        public Context matchToEnd() {
+            return new Context(this, length, true);
+        }
+
+        public Context match(int count) {
+            return new Context(this, pos + count, true);
+        }
+
+        public Context noMatch() {
+            return new Context(this, this.pos, false);
+        }
+
+        public boolean isMatch() {
+            return isMatch;
+        }
+
+        public Path getRemainder() throws RepositoryException {
+            if (pos >= length) {
+                return null;
+            }
+            else {
+                return path.subPath(pos, length);
+            }
+        }
+
+        public boolean isExhausted() {
+            return pos == length;
+        }
+
+        public MatchResult getMatchResult() {
+            return new MatchResult(path, isMatch? pos : 0);
+        }
+
+        public String toString() {
+            return pos + " @ " + path;
+        }
+
+    }
+
+    // -----------------------------------------------------< SelectPattern >---
+
+    private static class SelectPattern extends Pattern {
+        private final Pattern pattern1;
+        private final Pattern pattern2;
+
+        public SelectPattern(Pattern pattern1, Pattern pattern2) {
+            super();
+            this.pattern1 = pattern1;
+            this.pattern2 = pattern2;
+        }
+
+        protected Context match(Context input) throws RepositoryException {
+            Context remainder1 = pattern1.match(input);
+            Context remainder2 = pattern2.match(input);
+            return remainder1.pos > remainder2.pos ?
+                    remainder1 : remainder2;
+        }
+
+        public String toString() {
+            return new StringBuffer()
+                .append("(")
+                .append(pattern1)
+                .append("|")
+                .append(pattern2)
+                .append(")")
+            .toString();
+        }
+    }
+
+    // -----------------------------------------------------< SequencePattern >---
+
+    private static class SequencePattern extends Pattern {
+        private final Pattern pattern1;
+        private final Pattern pattern2;
+
+        public SequencePattern(Pattern pattern1, Pattern pattern2) {
+            super();
+            this.pattern1 = pattern1;
+            this.pattern2 = pattern2;
+        }
+
+        protected Context match(Context input) throws RepositoryException {
+            Context context1 = pattern1.match(input);
+            if (context1.isMatch()) {
+                return pattern2.match(context1);
+            }
+            else {
+                return input.noMatch();
+            }
+        }
+
+        public String toString() {
+            return new StringBuffer()
+                .append("(")
+                .append(pattern1)
+                .append(", ")
+                .append(pattern2)
+                .append(")")
+            .toString();
+        }
+    }
+
+    // -----------------------------------------------------< RepeatPattern >---
+
+    private static class RepeatPattern extends Pattern {
+        private final Pattern pattern;
+        private final int min;
+        private final int max;
+        private boolean hasBounds;
+
+        public RepeatPattern(Pattern pattern) {
+            this(pattern, 0, 0);
+            this.hasBounds = false;
+        }
+
+        public RepeatPattern(Pattern pattern, int min, int max) {
+            super();
+            this.pattern = pattern;
+            this.min = min;
+            this.max = max;
+            this.hasBounds = true;
+        }
+
+        protected Context match(Context input) throws RepositoryException {
+            Context nextInput;
+            Context output = input.match(0);
+            int matchCount = -1;
+            do {
+                nextInput = output;
+                output = pattern.match(nextInput);
+                matchCount++;
+            } while (output.isMatch() && (output.pos > nextInput.pos));
+
+            if (!hasBounds() || (min <= matchCount && matchCount <= max)) {
+                return nextInput;
+            }
+            else {
+                return input.noMatch();
+            }
+        }
+
+        private boolean hasBounds() {
+            return hasBounds;
+        }
+
+        public String toString() {
+            return new StringBuffer()
+                .append("(")
+                .append(pattern)
+                .append(")*")
+            .toString();
+        }
+
+    }
+
+    // -----------------------------------------------------< PathPattern >---
+
+    private static class PathPattern extends Pattern {
+        private final Path path;
+        private final Element[] patternElements;
+
+        public PathPattern(Path path) {
+            super();
+            this.path = path;
+            patternElements = path.getElements();
+        }
+
+        protected Context match(Context input) throws RepositoryException {
+            if (input.isExhausted()) {
+                return input;
+            }
+
+            Path inputPath = input.getRemainder();
+            if (!inputPath.isNormalized()) {
+                throw new IllegalArgumentException("Not normalized");
+            }
+
+            Element[] inputElements = inputPath.getElements();
+            int inputLength = inputElements.length;
+            int patternLength = patternElements.length;
+            if (patternLength > inputLength) {
+                return input.noMatch();
+            }
+
+            for (int k = 0; k < patternLength; k++) {
+                if (!patternElements[k].equals(inputElements[k])) {
+                    return input.noMatch();
+                }
+            }
+
+            return input.match(patternLength);
+        }
+
+        public String toString() {
+            return new StringBuffer()
+                .append("\"")
+                .append(path)
+                .append("\"")
+            .toString();
+        }
+    }
+
+    // -----------------------------------------------------< AbstractNamePattern >---
+
+    private static abstract class AbstractNamePattern extends Pattern {
+        protected abstract boolean matches(Element element);
+
+        protected Context match(Context input) throws RepositoryException {
+            if (input.isExhausted()) {
+                return input.noMatch();
+            }
+
+            Path inputPath = input.getRemainder();
+            if (!inputPath.isNormalized()) {
+                throw new IllegalArgumentException("Not normalized");
+            }
+
+            Element[] inputElements = inputPath.getElements();
+            if (inputElements.length < 1 || !matches(inputElements[0])) {
+                return input.noMatch();
+            }
+
+            return input.match(1);
+        }
+
+    }
+
+    // -----------------------------------------------------< NameNamePattern >---
+
+    private static class NamePattern extends AbstractNamePattern {
+        private final Name name;
+
+        public NamePattern(Name name) {
+            super();
+            this.name = name;
+        }
+
+        protected boolean matches(Element element) {
+            return name.equals(element.getName());
+        }
+
+        public String toString() {
+            return new StringBuffer()
+                .append("\"")
+                .append(name)
+                .append("\"")
+            .toString();
+        }
+    }
+
+    // -----------------------------------------------------< StringNamePattern >---
+
+    private static class RegexPattern extends AbstractNamePattern {
+        private final java.util.regex.Pattern namespaceUri;
+        private final java.util.regex.Pattern localName;
+        private final String localNameStr;
+        private final String namespaceUriStr;
+
+        public RegexPattern(String namespaceUri, String localName) {
+            super();
+
+            this.namespaceUri = java.util.regex.Pattern.compile(namespaceUri);
+            this.localName = java.util.regex.Pattern.compile(localName);
+            this.namespaceUriStr = namespaceUri;
+            this.localNameStr = localName;
+        }
+
+        protected boolean matches(Element element) {
+            Name name = element.getName();
+            boolean nsMatches = namespaceUri.matcher(name.getNamespaceURI()).matches();
+            boolean localMatches = localName.matcher(name.getLocalName()).matches();
+            return nsMatches && localMatches;
+        }
+
+        public String toString() {
+            return new StringBuffer()
+                .append("\"{")
+                .append(namespaceUriStr)
+                .append("}")
+                .append(localNameStr)
+                .append("\"")
+            .toString();
+        }
+    }
+
+}
+

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Pattern.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/main/java/org/apache/jackrabbit/spi/commons/name/Pattern.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/MatcherTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/MatcherTest.java?rev=674843&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/MatcherTest.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/MatcherTest.java Tue Jul  8 07:37:23 2008
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi.commons.name;
+
+import javax.jcr.RepositoryException;
+
+import junit.framework.TestCase;
+
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+
+public class MatcherTest extends TestCase {
+    private final static PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+    private final static Path PATH1 = PATH_FACTORY.create("{}\t{}a\t{}b\t{}c\t{}d\t{}e\t{}f\t{}g\t{}h\t{}i\t{}j\t{}k");
+    private static final Path PATH2 = PATH_FACTORY.create("{}a\t{}a\t{}a\t{}a\t{}a\t{}b");
+
+    public void testFindMatch() throws RepositoryException {
+        int k = 1;
+        for (char c = 'a'; c <= 'k'; c++) {
+            Pattern pattern = Pattern.name("", Character.toString(c));
+            MatchResult result = Matcher.findMatch(pattern, PATH1);
+            assertEquals("Match @ " + k, k, result.getMatchPos());
+            assertEquals("Match @ " + k, PATH1.subPath(k, k + 1), result.getMatch());
+            assertEquals("Match @ " + k, c == 'k'? null : PATH1.subPath(k + 1, PATH1.getLength()), result.getRemainder());
+            assertEquals("MatchLength == 1", 1, result.getMatchLength());
+            k++;
+        }
+    }
+
+    public void testFindMatchPos() throws RepositoryException {
+        Pattern pattern = Pattern.name("", ".");
+        int k = 1;
+        for (MatchResult result = Matcher.findMatch(pattern, PATH1);
+                result.getMatchPos() + 1 < PATH1.getLength();
+                result = Matcher.findMatch(pattern, PATH1, result.getMatchPos() + 1)) {
+
+            assertEquals("Match @ " + k, k, result.getMatchPos());
+            assertEquals("Match @ " + k, PATH1.subPath(k, k + 1), result.getMatch());
+            assertEquals("Match @ " + k, PATH1.subPath(k + 1, PATH1.getLength()), result.getRemainder());
+            assertEquals("MatchLength == 1", 1, result.getMatchLength());
+            k++;
+        }
+
+        pattern = Pattern.name("", "any");
+        assertEquals(pattern + " does not match " + PATH1, null, Matcher.findMatch(pattern, PATH1));
+    }
+
+    public void testGreedyRepeat() {
+        Pattern pattern = Pattern.sequence(
+                Pattern.repeat(
+                        Pattern.sequence(
+                                Pattern.name("", ".*"),
+                                Pattern.name("", "a"))),
+                Pattern.name("", "b"));
+
+        assertEquals(pattern + " matches " + PATH2 + " @1", 1, Matcher.findMatch(pattern, PATH2).getMatchPos());
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/MatcherTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/MatcherTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Added: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PatternTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PatternTest.java?rev=674843&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PatternTest.java (added)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PatternTest.java Tue Jul  8 07:37:23 2008
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.spi.commons.name;
+
+import javax.jcr.RepositoryException;
+
+import junit.framework.TestCase;
+
+import org.apache.jackrabbit.spi.NameFactory;
+import org.apache.jackrabbit.spi.Path;
+import org.apache.jackrabbit.spi.PathFactory;
+import org.apache.jackrabbit.spi.Path.Element;
+
+public class PatternTest extends TestCase {
+    private static final PathFactory PATH_FACTORY = PathFactoryImpl.getInstance();
+    private static final NameFactory NAME_FACTORY = NameFactoryImpl.getInstance();
+
+    private static final Path PATH1 = PATH_FACTORY.create("{}\t{}www\t{}day\t{}com\t{}jcr\t{}1\t{}0");
+    private static final Path PATH2 = PATH_FACTORY.create("{}\t{}jackrabbit\t{}apache\t{}org");
+    private static final Path PATH3 = PATH_FACTORY.create("{}a\t{}a\t{}a\t{}a\t{}a\t{}b");
+
+    public void testPathPattern() throws RepositoryException {
+        int len = PATH1.getLength();
+        for (int k = 1; k < len; k++) {
+            Pattern pattern = Pattern.path(PATH1.subPath(0, len - k));
+            MatchResult result = pattern.match(PATH1);
+            assertTrue(errMsg(pattern, PATH1), result.isMatch());
+            assertEquals(errMsg(pattern, PATH1), PATH1.subPath(0, len - k), result.getMatch());
+            assertEquals(errMsg(pattern, PATH1), PATH1.subPath(len - k, len), result.getRemainder());
+            assertEquals(errMsg(pattern, PATH1), PATH1.subPath(len - k, len), Matcher.match(pattern, PATH1));
+        }
+
+        Pattern pattern = Pattern.path(PATH1);
+        MatchResult result = pattern.match(PATH1);
+        assertTrue(errMsg(pattern, PATH1), result.isMatch());
+        assertTrue(errMsg(pattern, PATH1), result.isFullMatch());
+        assertTrue(errMsg(pattern, PATH1), Matcher.matches(pattern, PATH1));
+        assertEquals(errMsg(pattern, PATH1), PATH1, result.getMatch());
+        assertEquals(errMsg(pattern, PATH1), null, result.getRemainder());
+        assertEquals(errMsg(pattern, PATH1), null, Matcher.match(pattern, PATH1));
+    }
+
+    public void testNamePattern() throws RepositoryException {
+        Element[] elements = PATH1.getElements();
+        Path path = PATH1;
+        for (int k = 0; k < elements.length; k++) {
+            Pattern pattern = Pattern.name(elements[k].getName());
+            MatchResult result = pattern.match(path);
+            Path remainder = result.getRemainder();
+            assertTrue(errMsg(pattern, path), result.isMatch());
+            assertEquals(errMsg(pattern, path), path.subPath(0, 1), result.getMatch());
+            if (k + 1 == elements.length) {
+                assertTrue(errMsg(pattern, path), result.isFullMatch());
+                assertTrue(errMsg(pattern, path), Matcher.matches(pattern, path));
+                assertEquals(errMsg(pattern, path), null, remainder);
+                assertEquals(errMsg(pattern, path), null, Matcher.match(pattern, path));
+            }
+            else {
+                assertEquals(errMsg(pattern, path), path.subPath(1, path.getLength()), remainder);
+                assertEquals(errMsg(pattern, path), path.subPath(1, path.getLength()), Matcher.match(pattern, path));
+            }
+            path = remainder;
+        }
+    }
+
+    public void testRegexPattern() {
+        Path path = PATH_FACTORY.create("{}goodday");
+        Pattern pattern = Pattern.name(".*", "go*d+ay");
+        MatchResult result = pattern.match(path);
+        assertTrue(errMsg(pattern, path), result.isMatch());
+        assertTrue(errMsg(pattern, path), result.isFullMatch());
+        assertTrue(errMsg(pattern, path), Matcher.matches(pattern, path));
+        assertEquals(errMsg(pattern, path), path, result.getMatch());
+        assertEquals(errMsg(pattern, path), null, result.getRemainder());
+        assertEquals(errMsg(pattern, path), null, Matcher.match(pattern, path));
+
+        pattern = Pattern.name("", "goodday");
+        result = pattern.match(path);
+        assertTrue(errMsg(pattern, path), result.isMatch());
+        assertTrue(errMsg(pattern, path), result.isFullMatch());
+        assertTrue(errMsg(pattern, path), Matcher.matches(pattern, path));
+        assertEquals(errMsg(pattern, path), path, result.getMatch());
+        assertEquals(errMsg(pattern, path), null, result.getRemainder());
+        assertEquals(errMsg(pattern, path), null, Matcher.match(pattern, path));
+    }
+
+    public void testAllPattern() {
+        Pattern pattern = Pattern.all();
+        MatchResult result = pattern.match(PATH1);
+        assertTrue(errMsg(pattern, PATH1), result.isMatch());
+        assertTrue(errMsg(pattern, PATH1), result.isFullMatch());
+        assertTrue(errMsg(pattern, PATH1), Matcher.matches(pattern, PATH1));
+        assertEquals(errMsg(pattern, PATH1), PATH1, result.getMatch());
+        assertEquals(errMsg(pattern, PATH1), null, result.getRemainder());
+        assertEquals(errMsg(pattern, PATH1), null, Matcher.match(pattern, PATH1));
+    }
+
+    public void testNothingPattern() {
+        Pattern pattern = Pattern.nothing();
+        MatchResult result = pattern.match(PATH1);
+        assertFalse(errMsg(pattern, PATH1), result.isMatch());
+        assertFalse(errMsg(pattern, PATH1), result.isFullMatch());
+        assertFalse(errMsg(pattern, PATH1), Matcher.matches(pattern, PATH1));
+        assertEquals(errMsg(pattern, PATH1), null, result.getMatch());
+        assertEquals(errMsg(pattern, PATH1), PATH1, result.getRemainder());
+        assertEquals(errMsg(pattern, PATH1), PATH1, Matcher.match(pattern, PATH1));
+    }
+
+    public void testSelectPattern() {
+        Pattern pattern = Pattern.selection(
+                Pattern.path(PATH2),
+                Pattern.path(PATH1));
+
+        MatchResult result = pattern.match(PATH1);
+        assertTrue(errMsg(pattern, PATH1), result.isMatch());
+        assertTrue(errMsg(pattern, PATH1), result.isFullMatch());
+        assertTrue(errMsg(pattern, PATH1), Matcher.matches(pattern, PATH1));
+        assertEquals(errMsg(pattern, PATH1), PATH1, result.getMatch());
+        assertEquals(errMsg(pattern, PATH1), null, result.getRemainder());
+        assertEquals(errMsg(pattern, PATH1), null, Matcher.match(pattern, PATH1));
+    }
+
+    public void testOptionalPattern() {
+        Pattern pattern = Pattern.selection(
+                Pattern.nothing(),
+                Pattern.path(PATH1));
+
+        MatchResult result = pattern.match(PATH1);
+        assertTrue(errMsg(pattern, PATH1), result.isMatch());
+        assertTrue(errMsg(pattern, PATH1), result.isFullMatch());
+        assertTrue(errMsg(pattern, PATH1), Matcher.matches(pattern, PATH1));
+        assertEquals(errMsg(pattern, PATH1), PATH1, result.getMatch());
+        assertEquals(errMsg(pattern, PATH1), null, result.getRemainder());
+        assertEquals(errMsg(pattern, PATH1), null, Matcher.match(pattern, PATH1));
+    }
+
+    public void testGreedySelection() {
+        Path path = PATH_FACTORY.create("{}a\t{}a\t{}a\t{}a\t{}a");
+        Path expectedMatch = PATH_FACTORY.create("{}a\t{}a\t{}a");
+        Path expectedRemainder = PATH_FACTORY.create("{}a\t{}a");
+        Pattern pattern1 = Pattern.path(PATH_FACTORY.create("{}a\t{}a"));
+        Pattern pattern2 = Pattern.path(PATH_FACTORY.create("{}a\t{}a\t{}a"));
+
+        Pattern pattern = Pattern.selection(pattern1, pattern2);
+        MatchResult result = pattern.match(path);
+        assertTrue(errMsg(pattern, path), result.isMatch());
+        assertFalse(errMsg(pattern, path), result.isFullMatch());
+        assertFalse(errMsg(pattern, path), Matcher.matches(pattern, path));
+        assertEquals(errMsg(pattern, path), expectedMatch, result.getMatch());
+        assertEquals(errMsg(pattern, path), expectedRemainder, result.getRemainder());
+        assertEquals(errMsg(pattern, path), expectedRemainder, Matcher.match(pattern, path));
+
+        pattern = Pattern.selection(pattern2, pattern1);
+        result = pattern.match(path);
+        assertTrue(errMsg(pattern, path), result.isMatch());
+        assertFalse(errMsg(pattern, path), result.isFullMatch());
+        assertFalse(errMsg(pattern, path), Matcher.matches(pattern, path));
+        assertEquals(errMsg(pattern, path), expectedMatch, result.getMatch());
+        assertEquals(errMsg(pattern, path), expectedRemainder, result.getRemainder());
+        assertEquals(errMsg(pattern, path), expectedRemainder, Matcher.match(pattern, path));
+    }
+
+
+    public void testSequencePattern() throws RepositoryException {
+        int len = PATH1.getLength();
+        for (int k = 1; k < len; k++) {
+            Pattern pattern = Pattern.sequence(
+                    Pattern.path(PATH1.subPath(0, len - k)),
+                    Pattern.path(PATH1.subPath(len - k, len)));
+
+            MatchResult result = pattern.match(PATH1);
+            assertTrue(errMsg(pattern, PATH1), result.isMatch());
+            assertTrue(errMsg(pattern, PATH1), result.isFullMatch());
+            assertTrue(errMsg(pattern, PATH1), Matcher.matches(pattern, PATH1));
+            assertEquals(errMsg(pattern, PATH1), PATH1, result.getMatch());
+            assertEquals(errMsg(pattern, PATH1), null, result.getRemainder());
+            assertEquals(errMsg(pattern, PATH1), null, Matcher.match(pattern, PATH1));
+        }
+    }
+
+    public void testRepeatPattern() throws RepositoryException {
+        Pattern pattern = Pattern.repeat(
+                Pattern.name(".*", "a"));
+
+        MatchResult result = pattern.match(PATH3);
+        assertTrue(errMsg(pattern, PATH3), result.isMatch());
+        assertEquals(errMsg(pattern, PATH3), 5, result.getMatchLength());
+        assertFalse(errMsg(pattern, PATH3), result.isFullMatch());
+        assertEquals(errMsg(pattern, PATH3), PATH3.subPath(0, 5),result.getMatch());
+        assertEquals(errMsg(pattern, PATH3), PATH3.subPath(5, PATH3.getLength()), result.getRemainder());
+        assertEquals(errMsg(pattern, PATH3), PATH3.subPath(5, PATH3.getLength()), Matcher.match(pattern, PATH3));
+    }
+
+    public void testZeroLengthRepeatPattern() throws RepositoryException {
+        Pattern pattern = Pattern.sequence(
+                Pattern.repeat(
+                        Pattern.name("", "any")),
+                Pattern.name("", ".*"));
+
+        MatchResult result = pattern.match(PATH3);
+        assertTrue(errMsg(pattern, PATH3), result.isMatch());
+        assertEquals(errMsg(pattern, PATH3), 1, result.getMatchLength());
+        assertFalse(errMsg(pattern, PATH3), result.isFullMatch());
+        assertEquals(errMsg(pattern, PATH3), PATH3.subPath(0, 1),result.getMatch());
+        assertEquals(errMsg(pattern, PATH3), PATH3.subPath(1, PATH3.getLength()), result.getRemainder());
+        assertEquals(errMsg(pattern, PATH3), PATH3.subPath(1, PATH3.getLength()), Matcher.match(pattern, PATH3));
+    }
+
+    public void testRepeatPatternWithBounds() {
+        for (int i = 0; i <= PATH3.getLength(); i++) {
+            for (int j = 0; j <= PATH3.getLength(); j++) {
+                Pattern pattern = Pattern.repeat(
+                        Pattern.name(".*", "a"), i, j);
+
+                MatchResult result = pattern.match(PATH3);
+                if (i <= 5 && 5 <= j) {
+                    assertTrue(errMsg(pattern, PATH3), result.isMatch());
+                }
+                else {
+                    assertFalse(errMsg(pattern, PATH3), result.isMatch());
+                }
+            }
+        }
+    }
+
+    public void testComplexPattern() {
+        Pattern pattern = Pattern.sequence(
+                Pattern.selection(
+                        Pattern.path(PATH2),
+                        Pattern.name("", "")),
+                Pattern.selection(
+                        Pattern.repeat(Pattern.repeat(Pattern.repeat(
+                                Pattern.name(NAME_FACTORY.create("{}www"))))),
+                        Pattern.sequence(
+                                Pattern.path(PATH_FACTORY.create("{}www\t{}day")),
+                                Pattern.all())));
+
+        MatchResult result = pattern.match(PATH1);
+        assertTrue(errMsg(pattern, PATH1), result.isMatch());
+        assertTrue(errMsg(pattern, PATH1), result.isFullMatch());
+        assertTrue(errMsg(pattern, PATH1), Matcher.matches(pattern, PATH1));
+        assertEquals(errMsg(pattern, PATH1), PATH1, result.getMatch());
+        assertEquals(errMsg(pattern, PATH1), null, result.getRemainder());
+        assertEquals(errMsg(pattern, PATH1), null, Matcher.match(pattern, PATH1));
+    }
+
+    // -----------------------------------------------------< private >---
+
+    private static String errMsg(Pattern pattern, Path path) {
+        return pattern + " matches " + path;
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PatternTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/PatternTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev Url

Modified: jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java?rev=674843&r1=674842&r2=674843&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java (original)
+++ jackrabbit/trunk/jackrabbit-spi-commons/src/test/java/org/apache/jackrabbit/spi/commons/name/TestAll.java Tue Jul  8 07:37:23 2008
@@ -39,6 +39,8 @@
         suite.addTestSuite(PathBuilderTest.class);
         suite.addTestSuite(PathFactoryTest.class);
         suite.addTestSuite(PathTest.class);
+        suite.addTestSuite(PatternTest.class);
+        suite.addTestSuite(MatcherTest.class);
 
         return suite;
     }