Browse Source
The current IgnoreRule/FileNameMatcher implementation scales not well with huge repositories - it is both slow and memory expensive while parsing glob expressions (bug 440732). Addtitionally, the "double star" pattern (/**/) is not understood by the old parser (bug 416348). The proposed implementation is a complete clean room rewrite of the gitignore parser, aiming to add missing double star pattern support and improve the performance and memory consumption. The glob expressions from .gitignore rules are converted to Java regular expressions (java.util.regex.Pattern). java.util.regex.Pattern code can evaluate expression from gitignore rules considerable faster (and with less memory consumption) as the old FileNameMatcher implementation. CQ: 8828 Bug: 416348 Bug: 440732 Change-Id: Ibefb930381f2f16eddb9947e592752f8ae2b76e1 Signed-off-by: Andrey Loskutov <loskutov@gmx.de> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>stable-3.6
Andrey Loskutov
10 years ago
committed by
Matthias Sohn
18 changed files with 3205 additions and 16 deletions
@ -0,0 +1,74 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
import org.eclipse.jgit.ignore.FastIgnoreRule; |
||||
import org.junit.Test; |
||||
|
||||
public class BasicRuleTest { |
||||
|
||||
@Test |
||||
public void test() { |
||||
FastIgnoreRule rule1 = new FastIgnoreRule("/hello/[a]/"); |
||||
FastIgnoreRule rule2 = new FastIgnoreRule("/hello/[a]/"); |
||||
FastIgnoreRule rule3 = new FastIgnoreRule("!/hello/[a]/"); |
||||
FastIgnoreRule rule4 = new FastIgnoreRule("/hello/[a]"); |
||||
assertTrue(rule1.dirOnly()); |
||||
assertTrue(rule3.dirOnly()); |
||||
assertFalse(rule4.dirOnly()); |
||||
assertFalse(rule1.getNegation()); |
||||
assertTrue(rule3.getNegation()); |
||||
assertNotEquals(rule1, null); |
||||
assertEquals(rule1, rule1); |
||||
assertEquals(rule1, rule2); |
||||
assertNotEquals(rule1, rule3); |
||||
assertNotEquals(rule1, rule4); |
||||
assertEquals(rule1.hashCode(), rule2.hashCode()); |
||||
assertNotEquals(rule1.hashCode(), rule3.hashCode()); |
||||
assertEquals(rule1.toString(), rule2.toString()); |
||||
assertNotEquals(rule1.toString(), rule3.toString()); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,561 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore; |
||||
|
||||
import static org.eclipse.jgit.ignore.internal.Strings.split; |
||||
import static org.junit.Assert.*; |
||||
import static org.junit.Assume.*; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import org.eclipse.jgit.ignore.FastIgnoreRule; |
||||
import org.eclipse.jgit.ignore.IgnoreRule; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.junit.runners.*; |
||||
import org.junit.runners.Parameterized.Parameter; |
||||
import org.junit.runners.Parameterized.Parameters; |
||||
|
||||
@SuppressWarnings("deprecation") |
||||
@RunWith(Parameterized.class) |
||||
public class FastIgnoreRuleTest { |
||||
|
||||
@Parameters(name = "JGit? {0}") |
||||
public static Iterable<Boolean[]> data() { |
||||
return Arrays.asList(new Boolean[][] { { Boolean.FALSE }, |
||||
{ Boolean.TRUE } }); |
||||
} |
||||
|
||||
@Parameter |
||||
public Boolean useJGitRule; |
||||
|
||||
@Test |
||||
public void testSimpleCharClass() { |
||||
assertMatched("[a]", "a"); |
||||
assertMatched("[a]", "a/"); |
||||
assertMatched("[a]", "a/b"); |
||||
|
||||
assertMatched("[a]", "b/a"); |
||||
assertMatched("[a]", "b/a/"); |
||||
assertMatched("[a]", "b/a/b"); |
||||
|
||||
assertMatched("[a]", "/a/"); |
||||
assertMatched("[a]", "/a/b"); |
||||
|
||||
assertMatched("[a]", "c/a/b"); |
||||
assertMatched("[a]", "c/b/a"); |
||||
|
||||
assertMatched("/[a]", "a"); |
||||
assertMatched("/[a]", "a/"); |
||||
assertMatched("/[a]", "a/b"); |
||||
assertMatched("/[a]", "/a"); |
||||
assertMatched("/[a]", "/a/"); |
||||
assertMatched("/[a]", "/a/b"); |
||||
|
||||
assertMatched("[a]/", "a/"); |
||||
assertMatched("[a]/", "a/b"); |
||||
assertMatched("[a]/", "/a/"); |
||||
assertMatched("[a]/", "/a/b"); |
||||
|
||||
assertMatched("/[a]/", "a/"); |
||||
assertMatched("/[a]/", "a/b"); |
||||
assertMatched("/[a]/", "/a/"); |
||||
assertMatched("/[a]/", "/a/b"); |
||||
} |
||||
|
||||
@Test |
||||
public void testCharClass() { |
||||
assertMatched("[v-z]", "x"); |
||||
assertMatched("[v-z]", "x/"); |
||||
assertMatched("[v-z]", "x/b"); |
||||
|
||||
assertMatched("[v-z]", "b/x"); |
||||
assertMatched("[v-z]", "b/x/"); |
||||
assertMatched("[v-z]", "b/x/b"); |
||||
|
||||
assertMatched("[v-z]", "/x/"); |
||||
assertMatched("[v-z]", "/x/b"); |
||||
|
||||
assertMatched("[v-z]", "c/x/b"); |
||||
assertMatched("[v-z]", "c/b/x"); |
||||
|
||||
assertMatched("/[v-z]", "x"); |
||||
assertMatched("/[v-z]", "x/"); |
||||
assertMatched("/[v-z]", "x/b"); |
||||
assertMatched("/[v-z]", "/x"); |
||||
assertMatched("/[v-z]", "/x/"); |
||||
assertMatched("/[v-z]", "/x/b"); |
||||
|
||||
assertMatched("[v-z]/", "x/"); |
||||
assertMatched("[v-z]/", "x/b"); |
||||
assertMatched("[v-z]/", "/x/"); |
||||
assertMatched("[v-z]/", "/x/b"); |
||||
|
||||
assertMatched("/[v-z]/", "x/"); |
||||
assertMatched("/[v-z]/", "x/b"); |
||||
assertMatched("/[v-z]/", "/x/"); |
||||
assertMatched("/[v-z]/", "/x/b"); |
||||
} |
||||
|
||||
@Test |
||||
public void testAsteriskDot() { |
||||
assertMatched("*.a", ".a"); |
||||
assertMatched("*.a", "/.a"); |
||||
assertMatched("*.a", "a.a"); |
||||
assertMatched("*.a", "/b.a"); |
||||
assertMatched("*.a", "b.a"); |
||||
assertMatched("*.a", "/a/b.a"); |
||||
assertMatched("*.a", "/b/.a"); |
||||
} |
||||
|
||||
@Test |
||||
public void testAsteriskDotDoNotMatch() { |
||||
assertNotMatched("*.a", ".ab"); |
||||
assertNotMatched("*.a", "/.ab"); |
||||
assertNotMatched("*.a", "/b.ba"); |
||||
assertNotMatched("*.a", "a.ab"); |
||||
assertNotMatched("*.a", "/b.ab"); |
||||
assertNotMatched("*.a", "b.ab"); |
||||
assertNotMatched("*.a", "/a/b.ab"); |
||||
assertNotMatched("*.a", "/b/.ab"); |
||||
} |
||||
|
||||
@Test |
||||
public void testDotAsteriskMatch() { |
||||
assertMatched("a.*", "a."); |
||||
assertMatched("a.*", "a./"); |
||||
assertMatched("a.*", "a.b"); |
||||
|
||||
assertMatched("a.*", "b/a.b"); |
||||
assertMatched("a.*", "b/a.b/"); |
||||
assertMatched("a.*", "b/a.b/b"); |
||||
|
||||
assertMatched("a.*", "/a.b/"); |
||||
assertMatched("a.*", "/a.b/b"); |
||||
|
||||
assertMatched("a.*", "c/a.b/b"); |
||||
assertMatched("a.*", "c/b/a.b"); |
||||
|
||||
assertMatched("/a.*", "a.b"); |
||||
assertMatched("/a.*", "a.b/"); |
||||
assertMatched("/a.*", "a.b/b"); |
||||
assertMatched("/a.*", "/a.b"); |
||||
assertMatched("/a.*", "/a.b/"); |
||||
assertMatched("/a.*", "/a.b/b"); |
||||
|
||||
assertMatched("/a.*/b", "a.b/b"); |
||||
assertMatched("/a.*/b", "/a.b/b"); |
||||
assertMatched("/a.*/b", "/a.bc/b"); |
||||
assertMatched("/a.*/b", "/a./b"); |
||||
} |
||||
|
||||
@Test |
||||
public void testAsterisk() { |
||||
assertMatched("a*", "a"); |
||||
assertMatched("a*", "a/"); |
||||
assertMatched("a*", "ab"); |
||||
|
||||
assertMatched("a*", "b/ab"); |
||||
assertMatched("a*", "b/ab/"); |
||||
assertMatched("a*", "b/ab/b"); |
||||
|
||||
assertMatched("a*", "b/abc"); |
||||
assertMatched("a*", "b/abc/"); |
||||
assertMatched("a*", "b/abc/b"); |
||||
|
||||
assertMatched("a*", "/abc/"); |
||||
assertMatched("a*", "/abc/b"); |
||||
|
||||
assertMatched("a*", "c/abc/b"); |
||||
assertMatched("a*", "c/b/abc"); |
||||
|
||||
assertMatched("/a*", "abc"); |
||||
assertMatched("/a*", "abc/"); |
||||
assertMatched("/a*", "abc/b"); |
||||
assertMatched("/a*", "/abc"); |
||||
assertMatched("/a*", "/abc/"); |
||||
assertMatched("/a*", "/abc/b"); |
||||
|
||||
assertMatched("/a*/b", "abc/b"); |
||||
assertMatched("/a*/b", "/abc/b"); |
||||
assertMatched("/a*/b", "/abcd/b"); |
||||
assertMatched("/a*/b", "/a/b"); |
||||
} |
||||
|
||||
@Test |
||||
public void testQuestionmark() { |
||||
assertMatched("a?", "ab"); |
||||
assertMatched("a?", "ab/"); |
||||
|
||||
assertMatched("a?", "b/ab"); |
||||
assertMatched("a?", "b/ab/"); |
||||
assertMatched("a?", "b/ab/b"); |
||||
|
||||
assertMatched("a?", "/ab/"); |
||||
assertMatched("a?", "/ab/b"); |
||||
|
||||
assertMatched("a?", "c/ab/b"); |
||||
assertMatched("a?", "c/b/ab"); |
||||
|
||||
assertMatched("/a?", "ab"); |
||||
assertMatched("/a?", "ab/"); |
||||
assertMatched("/a?", "ab/b"); |
||||
assertMatched("/a?", "/ab"); |
||||
assertMatched("/a?", "/ab/"); |
||||
assertMatched("/a?", "/ab/b"); |
||||
|
||||
assertMatched("/a?/b", "ab/b"); |
||||
assertMatched("/a?/b", "/ab/b"); |
||||
} |
||||
|
||||
@Test |
||||
public void testQuestionmarkDoNotMatch() { |
||||
assertNotMatched("a?", "a/"); |
||||
assertNotMatched("a?", "abc"); |
||||
assertNotMatched("a?", "abc/"); |
||||
|
||||
assertNotMatched("a?", "b/abc"); |
||||
assertNotMatched("a?", "b/abc/"); |
||||
|
||||
assertNotMatched("a?", "/abc/"); |
||||
assertNotMatched("a?", "/abc/b"); |
||||
|
||||
assertNotMatched("a?", "c/abc/b"); |
||||
assertNotMatched("a?", "c/b/abc"); |
||||
|
||||
assertNotMatched("/a?", "abc"); |
||||
assertNotMatched("/a?", "abc/"); |
||||
assertNotMatched("/a?", "abc/b"); |
||||
assertNotMatched("/a?", "/abc"); |
||||
assertNotMatched("/a?", "/abc/"); |
||||
assertNotMatched("/a?", "/abc/b"); |
||||
|
||||
assertNotMatched("/a?/b", "abc/b"); |
||||
assertNotMatched("/a?/b", "/abc/b"); |
||||
assertNotMatched("/a?/b", "/a/b"); |
||||
} |
||||
|
||||
@Test |
||||
public void testSimplePatterns() { |
||||
assertMatched("a", "a"); |
||||
assertMatched("a", "a/"); |
||||
assertMatched("a", "a/b"); |
||||
|
||||
assertMatched("a", "b/a"); |
||||
assertMatched("a", "b/a/"); |
||||
assertMatched("a", "b/a/b"); |
||||
|
||||
assertMatched("a", "/a/"); |
||||
assertMatched("a", "/a/b"); |
||||
|
||||
assertMatched("a", "c/a/b"); |
||||
assertMatched("a", "c/b/a"); |
||||
|
||||
assertMatched("/a", "a"); |
||||
assertMatched("/a", "a/"); |
||||
assertMatched("/a", "a/b"); |
||||
assertMatched("/a", "/a"); |
||||
assertMatched("/a", "/a/"); |
||||
assertMatched("/a", "/a/b"); |
||||
|
||||
assertMatched("a/", "a/"); |
||||
assertMatched("a/", "a/b"); |
||||
assertMatched("a/", "/a/"); |
||||
assertMatched("a/", "/a/b"); |
||||
|
||||
assertMatched("/a/", "a/"); |
||||
assertMatched("/a/", "a/b"); |
||||
assertMatched("/a/", "/a/"); |
||||
assertMatched("/a/", "/a/b"); |
||||
|
||||
} |
||||
|
||||
@Test |
||||
public void testSimplePatternsDoNotMatch() { |
||||
assertNotMatched("ab", "a"); |
||||
assertNotMatched("abc", "a/"); |
||||
assertNotMatched("abc", "a/b"); |
||||
|
||||
assertNotMatched("a", "ab"); |
||||
assertNotMatched("a", "ba"); |
||||
assertNotMatched("a", "aa"); |
||||
|
||||
assertNotMatched("a", "b/ab"); |
||||
assertNotMatched("a", "b/ba"); |
||||
|
||||
assertNotMatched("a", "b/ba"); |
||||
assertNotMatched("a", "b/ab"); |
||||
|
||||
assertNotMatched("a", "b/ba/"); |
||||
assertNotMatched("a", "b/ba/b"); |
||||
|
||||
assertNotMatched("a", "/aa"); |
||||
assertNotMatched("a", "aa/"); |
||||
assertNotMatched("a", "/aa/"); |
||||
|
||||
assertNotMatched("/a", "b/a"); |
||||
assertNotMatched("/a", "/b/a/"); |
||||
|
||||
assertNotMatched("a/", "a"); |
||||
assertNotMatched("a/", "b/a"); |
||||
|
||||
assertNotMatched("/a/", "a"); |
||||
assertNotMatched("/a/", "/a"); |
||||
assertNotMatched("/a/", "b/a"); |
||||
} |
||||
|
||||
@Test |
||||
public void testSegments() { |
||||
assertMatched("/a/b", "a/b"); |
||||
assertMatched("/a/b", "/a/b"); |
||||
assertMatched("/a/b", "/a/b/"); |
||||
assertMatched("/a/b", "/a/b/c"); |
||||
|
||||
assertMatched("a/b", "a/b"); |
||||
assertMatched("a/b", "/a/b"); |
||||
assertMatched("a/b", "/a/b/"); |
||||
assertMatched("a/b", "/a/b/c"); |
||||
|
||||
assertMatched("a/b/", "a/b/"); |
||||
assertMatched("a/b/", "/a/b/"); |
||||
assertMatched("a/b/", "/a/b/c"); |
||||
} |
||||
|
||||
@Test |
||||
public void testSegmentsDoNotMatch() { |
||||
assertNotMatched("a/b", "/a/bb"); |
||||
assertNotMatched("a/b", "/aa/b"); |
||||
assertNotMatched("a/b", "a/bb"); |
||||
assertNotMatched("a/b", "aa/b"); |
||||
assertNotMatched("a/b", "c/aa/b"); |
||||
assertNotMatched("a/b", "c/a/bb"); |
||||
assertNotMatched("a/b/", "/a/b"); |
||||
assertNotMatched("/a/b/", "/a/b"); |
||||
assertNotMatched("/a/b", "c/a/b"); |
||||
assertNotMatched("/a/b/", "c/a/b"); |
||||
assertNotMatched("/a/b/", "c/a/b/"); |
||||
|
||||
// XXX why is it like this????
|
||||
assertNotMatched("a/b", "c/a/b"); |
||||
assertNotMatched("a/b", "c/a/b/"); |
||||
assertNotMatched("a/b", "c/a/b/c"); |
||||
assertNotMatched("a/b/", "c/a/b/"); |
||||
assertNotMatched("a/b/", "c/a/b/c"); |
||||
} |
||||
|
||||
@SuppressWarnings("boxing") |
||||
@Test |
||||
public void testWildmatch() { |
||||
if (useJGitRule) |
||||
System.err |
||||
.println("IgnoreRule can't understand wildmatch rules, skipping testWildmatch!"); |
||||
|
||||
Boolean assume = useJGitRule; |
||||
assertMatched("**/a/b", "a/b", assume); |
||||
assertMatched("**/a/b", "c/a/b", assume); |
||||
assertMatched("**/a/b", "c/d/a/b", assume); |
||||
assertMatched("**/**/a/b", "c/d/a/b", assume); |
||||
|
||||
assertMatched("/**/a/b", "a/b", assume); |
||||
assertMatched("/**/a/b", "c/a/b", assume); |
||||
assertMatched("/**/a/b", "c/d/a/b", assume); |
||||
assertMatched("/**/**/a/b", "c/d/a/b", assume); |
||||
|
||||
assertMatched("a/b/**", "a/b", assume); |
||||
assertMatched("a/b/**", "a/b/c", assume); |
||||
assertMatched("a/b/**", "a/b/c/d/", assume); |
||||
assertMatched("a/b/**/**", "a/b/c/d", assume); |
||||
|
||||
assertMatched("**/a/**/b", "c/d/a/b", assume); |
||||
assertMatched("**/a/**/b", "c/d/a/e/b", assume); |
||||
assertMatched("**/**/a/**/**/b", "c/d/a/e/b", assume); |
||||
|
||||
assertMatched("/**/a/**/b", "c/d/a/b", assume); |
||||
assertMatched("/**/a/**/b", "c/d/a/e/b", assume); |
||||
assertMatched("/**/**/a/**/**/b", "c/d/a/e/b", assume); |
||||
|
||||
assertMatched("a/**/b", "a/b", assume); |
||||
assertMatched("a/**/b", "a/c/b", assume); |
||||
assertMatched("a/**/b", "a/c/d/b", assume); |
||||
assertMatched("a/**/**/b", "a/c/d/b", assume); |
||||
|
||||
assertMatched("a/**/b/**/c", "a/c/b/d/c", assume); |
||||
assertMatched("a/**/**/b/**/**/c", "a/c/b/d/c", assume); |
||||
} |
||||
|
||||
@SuppressWarnings("boxing") |
||||
@Test |
||||
public void testWildmatchDoNotMatch() { |
||||
if (useJGitRule) |
||||
System.err |
||||
.println("IgnoreRule can't understand wildmatch rules, skipping testWildmatchDoNotMatch!"); |
||||
|
||||
Boolean assume = useJGitRule; |
||||
assertNotMatched("**/a/b", "a/c/b", assume); |
||||
assertNotMatched("!/**/*.zip", "c/a/b.zip", assume); |
||||
assertNotMatched("!**/*.zip", "c/a/b.zip", assume); |
||||
assertNotMatched("a/**/b", "a/c/bb", assume); |
||||
} |
||||
|
||||
@SuppressWarnings("unused") |
||||
@Test |
||||
public void testSimpleRules() { |
||||
try { |
||||
new FastIgnoreRule(null); |
||||
fail("Illegal input allowed!"); |
||||
} catch (IllegalArgumentException e) { |
||||
// expected
|
||||
} |
||||
assertFalse(new FastIgnoreRule("/").isMatch("/", false)); |
||||
assertFalse(new FastIgnoreRule("//").isMatch("//", false)); |
||||
assertFalse(new FastIgnoreRule("#").isMatch("#", false)); |
||||
assertFalse(new FastIgnoreRule("").isMatch("", false)); |
||||
assertFalse(new FastIgnoreRule(" ").isMatch(" ", false)); |
||||
} |
||||
|
||||
@Test |
||||
public void testSplit() { |
||||
try { |
||||
split("/", '/').toArray(); |
||||
fail("should not allow single slash"); |
||||
} catch (IllegalStateException e) { |
||||
// expected
|
||||
} |
||||
|
||||
assertArrayEquals(new String[] { "a", "b" }, split("a/b", '/') |
||||
.toArray()); |
||||
assertArrayEquals(new String[] { "a", "b/" }, split("a/b/", '/') |
||||
.toArray()); |
||||
assertArrayEquals(new String[] { "/a", "b" }, split("/a/b", '/') |
||||
.toArray()); |
||||
assertArrayEquals(new String[] { "/a", "b/" }, split("/a/b/", '/') |
||||
.toArray()); |
||||
assertArrayEquals(new String[] { "/a", "b", "c" }, split("/a/b/c", '/') |
||||
.toArray()); |
||||
assertArrayEquals(new String[] { "/a", "b", "c/" }, |
||||
split("/a/b/c/", '/').toArray()); |
||||
} |
||||
|
||||
public void assertMatched(String pattern, String path, Boolean... assume) { |
||||
boolean match = match(pattern, path); |
||||
String result = path + " is " + (match ? "ignored" : "not ignored") |
||||
+ " via '" + pattern + "' rule"; |
||||
if (!match) |
||||
System.err.println(result); |
||||
if (assume.length == 0 || !assume[0].booleanValue()) |
||||
assertTrue("Expected a match for: " + pattern + " with: " + path, |
||||
match); |
||||
else |
||||
assumeTrue("Expected a match for: " + pattern + " with: " + path, |
||||
match); |
||||
|
||||
if (pattern.startsWith("!")) |
||||
pattern = pattern.substring(1); |
||||
else |
||||
pattern = "!" + pattern; |
||||
match = match(pattern, path); |
||||
if (assume.length == 0 || !assume[0].booleanValue()) |
||||
assertFalse("Expected no match for: " + pattern + " with: " + path, |
||||
match); |
||||
else |
||||
assumeFalse("Expected no match for: " + pattern + " with: " + path, |
||||
match); |
||||
} |
||||
|
||||
public void assertNotMatched(String pattern, String path, Boolean... assume) { |
||||
boolean match = match(pattern, path); |
||||
String result = path + " is " + (match ? "ignored" : "not ignored") |
||||
+ " via '" + pattern + "' rule"; |
||||
if (match) |
||||
System.err.println(result); |
||||
if (assume.length == 0 || !assume[0].booleanValue()) |
||||
assertFalse("Expected no match for: " + pattern + " with: " + path, |
||||
match); |
||||
else |
||||
assumeFalse("Expected no match for: " + pattern + " with: " + path, |
||||
match); |
||||
|
||||
if (pattern.startsWith("!")) |
||||
pattern = pattern.substring(1); |
||||
else |
||||
pattern = "!" + pattern; |
||||
match = match(pattern, path); |
||||
if (assume.length == 0 || !assume[0].booleanValue()) |
||||
assertTrue("Expected a match for: " + pattern + " with: " + path, |
||||
match); |
||||
else |
||||
assumeTrue("Expected a match for: " + pattern + " with: " + path, |
||||
match); |
||||
} |
||||
|
||||
/** |
||||
* Check for a match. If target ends with "/", match will assume that the |
||||
* target is meant to be a directory. |
||||
* |
||||
* @param pattern |
||||
* Pattern as it would appear in a .gitignore file |
||||
* @param target |
||||
* Target file path relative to repository's GIT_DIR |
||||
* @return Result of {@link FastIgnoreRule#isMatch(String, boolean)} |
||||
*/ |
||||
private boolean match(String pattern, String target) { |
||||
boolean isDirectory = target.endsWith("/"); |
||||
if (useJGitRule.booleanValue()) { |
||||
IgnoreRule r = new IgnoreRule(pattern); |
||||
// If speed of this test is ever an issue, we can use a presetRule
|
||||
// field
|
||||
// to avoid recompiling a pattern each time.
|
||||
boolean match = r.isMatch(target, isDirectory); |
||||
if (r.getNegation()) |
||||
match = !match; |
||||
return match; |
||||
} |
||||
FastIgnoreRule r = new FastIgnoreRule(pattern); |
||||
// If speed of this test is ever an issue, we can use a presetRule field
|
||||
// to avoid recompiling a pattern each time.
|
||||
boolean match = r.isMatch(target, isDirectory); |
||||
if (r.getNegation()) |
||||
match = !match; |
||||
return match; |
||||
} |
||||
} |
@ -0,0 +1,371 @@
|
||||
/* |
||||
* Copyright (C) 2010, Red Hat Inc. |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore; |
||||
|
||||
import static org.junit.Assert.*; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import org.eclipse.jgit.ignore.FastIgnoreRule; |
||||
import org.eclipse.jgit.ignore.IgnoreRule; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.junit.runners.*; |
||||
import org.junit.runners.Parameterized.Parameter; |
||||
import org.junit.runners.Parameterized.Parameters; |
||||
|
||||
/** |
||||
* Tests ignore pattern matches |
||||
*/ |
||||
@SuppressWarnings("deprecation") |
||||
@RunWith(Parameterized.class) |
||||
public class IgnoreMatcherParametrizedTest { |
||||
|
||||
@Parameters(name = "JGit? {0}") |
||||
public static Iterable<Boolean[]> data() { |
||||
return Arrays.asList(new Boolean[][] { { Boolean.FALSE }, |
||||
{ Boolean.TRUE } }); |
||||
} |
||||
|
||||
@Parameter |
||||
public Boolean useJGitRule; |
||||
|
||||
@Test |
||||
public void testBasic() { |
||||
String pattern = "/test.stp"; |
||||
assertMatched(pattern, "/test.stp"); |
||||
|
||||
pattern = "#/test.stp"; |
||||
assertNotMatched(pattern, "/test.stp"); |
||||
} |
||||
|
||||
@Test |
||||
public void testFileNameWildcards() { |
||||
// Test basic * and ? for any pattern + any character
|
||||
String pattern = "*.st?"; |
||||
assertMatched(pattern, "/test.stp"); |
||||
assertMatched(pattern, "/anothertest.stg"); |
||||
assertMatched(pattern, "/anothertest.st0"); |
||||
assertNotMatched(pattern, "/anothertest.sta1"); |
||||
// Check that asterisk does not expand to "/"
|
||||
assertNotMatched(pattern, "/another/test.sta1"); |
||||
|
||||
// Same as above, with a leading slash to ensure that doesn't cause
|
||||
// problems
|
||||
pattern = "/*.st?"; |
||||
assertMatched(pattern, "/test.stp"); |
||||
assertMatched(pattern, "/anothertest.stg"); |
||||
assertMatched(pattern, "/anothertest.st0"); |
||||
assertNotMatched(pattern, "/anothertest.sta1"); |
||||
// Check that asterisk does not expand to "/"
|
||||
assertNotMatched(pattern, "/another/test.sta1"); |
||||
|
||||
// Test for numbers
|
||||
pattern = "*.sta[0-5]"; |
||||
assertMatched(pattern, "/test.sta5"); |
||||
assertMatched(pattern, "/test.sta4"); |
||||
assertMatched(pattern, "/test.sta3"); |
||||
assertMatched(pattern, "/test.sta2"); |
||||
assertMatched(pattern, "/test.sta1"); |
||||
assertMatched(pattern, "/test.sta0"); |
||||
assertMatched(pattern, "/anothertest.sta2"); |
||||
assertNotMatched(pattern, "test.stag"); |
||||
assertNotMatched(pattern, "test.sta6"); |
||||
|
||||
// Test for letters
|
||||
pattern = "/[tv]est.sta[a-d]"; |
||||
assertMatched(pattern, "/test.staa"); |
||||
assertMatched(pattern, "/test.stab"); |
||||
assertMatched(pattern, "/test.stac"); |
||||
assertMatched(pattern, "/test.stad"); |
||||
assertMatched(pattern, "/vest.stac"); |
||||
assertNotMatched(pattern, "test.stae"); |
||||
assertNotMatched(pattern, "test.sta9"); |
||||
|
||||
// Test child directory/file is matched
|
||||
pattern = "/src/ne?"; |
||||
assertMatched(pattern, "/src/new/"); |
||||
assertMatched(pattern, "/src/new"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/src/new/a/a.c"); |
||||
assertNotMatched(pattern, "/src/new.c"); |
||||
|
||||
// Test name-only fnmatcher matches
|
||||
pattern = "ne?"; |
||||
assertMatched(pattern, "/src/new/"); |
||||
assertMatched(pattern, "/src/new"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/src/new/a/a.c"); |
||||
assertMatched(pattern, "/neb"); |
||||
assertNotMatched(pattern, "/src/new.c"); |
||||
} |
||||
|
||||
@Test |
||||
public void testTargetWithoutLeadingSlash() { |
||||
// Test basic * and ? for any pattern + any character
|
||||
String pattern = "/*.st?"; |
||||
assertMatched(pattern, "test.stp"); |
||||
assertMatched(pattern, "anothertest.stg"); |
||||
assertMatched(pattern, "anothertest.st0"); |
||||
assertNotMatched(pattern, "anothertest.sta1"); |
||||
// Check that asterisk does not expand to ""
|
||||
assertNotMatched(pattern, "another/test.sta1"); |
||||
|
||||
// Same as above, with a leading slash to ensure that doesn't cause
|
||||
// problems
|
||||
pattern = "/*.st?"; |
||||
assertMatched(pattern, "test.stp"); |
||||
assertMatched(pattern, "anothertest.stg"); |
||||
assertMatched(pattern, "anothertest.st0"); |
||||
assertNotMatched(pattern, "anothertest.sta1"); |
||||
// Check that asterisk does not expand to ""
|
||||
assertNotMatched(pattern, "another/test.sta1"); |
||||
|
||||
// Test for numbers
|
||||
pattern = "/*.sta[0-5]"; |
||||
assertMatched(pattern, "test.sta5"); |
||||
assertMatched(pattern, "test.sta4"); |
||||
assertMatched(pattern, "test.sta3"); |
||||
assertMatched(pattern, "test.sta2"); |
||||
assertMatched(pattern, "test.sta1"); |
||||
assertMatched(pattern, "test.sta0"); |
||||
assertMatched(pattern, "anothertest.sta2"); |
||||
assertNotMatched(pattern, "test.stag"); |
||||
assertNotMatched(pattern, "test.sta6"); |
||||
|
||||
// Test for letters
|
||||
pattern = "/[tv]est.sta[a-d]"; |
||||
assertMatched(pattern, "test.staa"); |
||||
assertMatched(pattern, "test.stab"); |
||||
assertMatched(pattern, "test.stac"); |
||||
assertMatched(pattern, "test.stad"); |
||||
assertMatched(pattern, "vest.stac"); |
||||
assertNotMatched(pattern, "test.stae"); |
||||
assertNotMatched(pattern, "test.sta9"); |
||||
|
||||
// Test child directory/file is matched
|
||||
pattern = "/src/ne?"; |
||||
assertMatched(pattern, "src/new/"); |
||||
assertMatched(pattern, "src/new"); |
||||
assertMatched(pattern, "src/new/a.c"); |
||||
assertMatched(pattern, "src/new/a/a.c"); |
||||
assertNotMatched(pattern, "src/new.c"); |
||||
|
||||
// Test name-only fnmatcher matches
|
||||
pattern = "ne?"; |
||||
assertMatched(pattern, "src/new/"); |
||||
assertMatched(pattern, "src/new"); |
||||
assertMatched(pattern, "src/new/a.c"); |
||||
assertMatched(pattern, "src/new/a/a.c"); |
||||
assertMatched(pattern, "neb"); |
||||
assertNotMatched(pattern, "src/new.c"); |
||||
} |
||||
|
||||
@Test |
||||
public void testParentDirectoryGitIgnores() { |
||||
// Contains git ignore patterns such as might be seen in a parent
|
||||
// directory
|
||||
|
||||
// Test for wildcards
|
||||
String pattern = "/*/*.c"; |
||||
assertMatched(pattern, "/file/a.c"); |
||||
assertMatched(pattern, "/src/a.c"); |
||||
assertNotMatched(pattern, "/src/new/a.c"); |
||||
|
||||
// Test child directory/file is matched
|
||||
pattern = "/src/new"; |
||||
assertMatched(pattern, "/src/new/"); |
||||
assertMatched(pattern, "/src/new"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/src/new/a/a.c"); |
||||
assertNotMatched(pattern, "/src/new.c"); |
||||
|
||||
// Test child directory is matched, slash after name
|
||||
pattern = "/src/new/"; |
||||
assertMatched(pattern, "/src/new/"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/src/new/a/a.c"); |
||||
assertNotMatched(pattern, "/src/new"); |
||||
assertNotMatched(pattern, "/src/new.c"); |
||||
|
||||
// Test directory is matched by name only
|
||||
pattern = "b1"; |
||||
assertMatched(pattern, "/src/new/a/b1/a.c"); |
||||
assertNotMatched(pattern, "/src/new/a/b2/file.c"); |
||||
assertNotMatched(pattern, "/src/new/a/bb1/file.c"); |
||||
assertNotMatched(pattern, "/src/new/a/file.c"); |
||||
} |
||||
|
||||
@Test |
||||
public void testTrailingSlash() { |
||||
String pattern = "/src/"; |
||||
assertMatched(pattern, "/src/"); |
||||
assertMatched(pattern, "/src/new"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/src/a.c"); |
||||
assertNotMatched(pattern, "/src"); |
||||
assertNotMatched(pattern, "/srcA/"); |
||||
} |
||||
|
||||
@Test |
||||
public void testNameOnlyMatches() { |
||||
/* |
||||
* Name-only matches do not contain any path separators |
||||
*/ |
||||
// Test matches for file extension
|
||||
String pattern = "*.stp"; |
||||
assertMatched(pattern, "/test.stp"); |
||||
assertMatched(pattern, "/src/test.stp"); |
||||
assertNotMatched(pattern, "/test.stp1"); |
||||
assertNotMatched(pattern, "/test.astp"); |
||||
|
||||
// Test matches for name-only, applies to file name or folder name
|
||||
pattern = "src"; |
||||
assertMatched(pattern, "/src"); |
||||
assertMatched(pattern, "/src/"); |
||||
assertMatched(pattern, "/src/a.c"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/new/src/a.c"); |
||||
assertMatched(pattern, "/file/src"); |
||||
|
||||
// Test matches for name-only, applies only to folder names
|
||||
pattern = "src/"; |
||||
assertMatched(pattern, "/src/"); |
||||
assertMatched(pattern, "/src/a.c"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/new/src/a.c"); |
||||
assertNotMatched(pattern, "/src"); |
||||
assertNotMatched(pattern, "/file/src"); |
||||
|
||||
// Test matches for name-only, applies to file name or folder name
|
||||
// With a small wildcard
|
||||
pattern = "?rc"; |
||||
assertMatched(pattern, "/src/a.c"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/new/src/a.c"); |
||||
assertMatched(pattern, "/file/src"); |
||||
assertMatched(pattern, "/src/"); |
||||
|
||||
// Test matches for name-only, applies to file name or folder name
|
||||
// With a small wildcard
|
||||
pattern = "?r[a-c]"; |
||||
assertMatched(pattern, "/src/a.c"); |
||||
assertMatched(pattern, "/src/new/a.c"); |
||||
assertMatched(pattern, "/new/src/a.c"); |
||||
assertMatched(pattern, "/file/src"); |
||||
assertMatched(pattern, "/src/"); |
||||
assertMatched(pattern, "/srb/a.c"); |
||||
assertMatched(pattern, "/grb/new/a.c"); |
||||
assertMatched(pattern, "/new/crb/a.c"); |
||||
assertMatched(pattern, "/file/3rb"); |
||||
assertMatched(pattern, "/xrb/"); |
||||
assertMatched(pattern, "/3ra/a.c"); |
||||
assertMatched(pattern, "/5ra/new/a.c"); |
||||
assertMatched(pattern, "/new/1ra/a.c"); |
||||
assertMatched(pattern, "/file/dra"); |
||||
assertMatched(pattern, "/era/"); |
||||
assertNotMatched(pattern, "/crg"); |
||||
assertNotMatched(pattern, "/cr3"); |
||||
} |
||||
|
||||
@Test |
||||
public void testNegation() { |
||||
String pattern = "!/test.stp"; |
||||
assertMatched(pattern, "/test.stp"); |
||||
} |
||||
|
||||
/** |
||||
* Check for a match. If target ends with "/", match will assume that the |
||||
* target is meant to be a directory. |
||||
* |
||||
* @param pattern |
||||
* Pattern as it would appear in a .gitignore file |
||||
* @param target |
||||
* Target file path relative to repository's GIT_DIR |
||||
*/ |
||||
public void assertMatched(String pattern, String target) { |
||||
boolean value = match(pattern, target); |
||||
assertTrue("Expected a match for: " + pattern + " with: " + target, |
||||
value); |
||||
} |
||||
|
||||
/** |
||||
* Check for a match. If target ends with "/", match will assume that the |
||||
* target is meant to be a directory. |
||||
* |
||||
* @param pattern |
||||
* Pattern as it would appear in a .gitignore file |
||||
* @param target |
||||
* Target file path relative to repository's GIT_DIR |
||||
*/ |
||||
public void assertNotMatched(String pattern, String target) { |
||||
boolean value = match(pattern, target); |
||||
assertFalse("Expected no match for: " + pattern + " with: " + target, |
||||
value); |
||||
} |
||||
|
||||
/** |
||||
* Check for a match. If target ends with "/", match will assume that the |
||||
* target is meant to be a directory. |
||||
* |
||||
* @param pattern |
||||
* Pattern as it would appear in a .gitignore file |
||||
* @param target |
||||
* Target file path relative to repository's GIT_DIR |
||||
* @return Result of {@link FastIgnoreRule#isMatch(String, boolean)} |
||||
*/ |
||||
private boolean match(String pattern, String target) { |
||||
boolean isDirectory = target.endsWith("/"); |
||||
if (useJGitRule.booleanValue()) { |
||||
IgnoreRule r = new IgnoreRule(pattern); |
||||
// If speed of this test is ever an issue, we can use a presetRule
|
||||
// field
|
||||
// to avoid recompiling a pattern each time.
|
||||
return r.isMatch(target, isDirectory); |
||||
} |
||||
FastIgnoreRule r = new FastIgnoreRule(pattern); |
||||
// If speed of this test is ever an issue, we can use a presetRule field
|
||||
// to avoid recompiling a pattern each time.
|
||||
return r.isMatch(target, isDirectory); |
||||
} |
||||
} |
@ -0,0 +1,904 @@
|
||||
/* |
||||
* Copyright (C) 2008, Florian Koeberle <florianskarten@web.de> |
||||
* Copyright (C) 2008, Florian Köberle <florianskarten@web.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore; |
||||
|
||||
import static org.junit.Assert.*; |
||||
import static org.junit.Assume.*; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
import org.eclipse.jgit.ignore.FastIgnoreRule; |
||||
import org.eclipse.jgit.ignore.IgnoreRule; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.junit.runners.*; |
||||
import org.junit.runners.Parameterized.Parameter; |
||||
import org.junit.runners.Parameterized.Parameters; |
||||
|
||||
@RunWith(Parameterized.class) |
||||
@SuppressWarnings({ "deprecation", "boxing" }) |
||||
public class IgnoreRuleSpecialCasesTest { |
||||
|
||||
@Parameters(name = "JGit? {0}") |
||||
public static Iterable<Boolean[]> data() { |
||||
return Arrays.asList(new Boolean[][] { { Boolean.FALSE }, |
||||
{ Boolean.TRUE } }); |
||||
} |
||||
|
||||
@Parameter |
||||
public Boolean useJGitRule; |
||||
|
||||
private void assertMatch(final String pattern, final String input, |
||||
final boolean matchExpected, Boolean... assume) { |
||||
boolean assumeDir = input.endsWith("/"); |
||||
if (useJGitRule.booleanValue()) { |
||||
final IgnoreRule matcher = new IgnoreRule(pattern); |
||||
if (assume.length == 0 || !assume[0].booleanValue()) |
||||
assertEquals(matchExpected, matcher.isMatch(input, assumeDir)); |
||||
else |
||||
assumeTrue(matchExpected == matcher.isMatch(input, assumeDir)); |
||||
} else { |
||||
FastIgnoreRule matcher = new FastIgnoreRule(pattern); |
||||
if (assume.length == 0 || !assume[0].booleanValue()) |
||||
assertEquals(matchExpected, matcher.isMatch(input, assumeDir)); |
||||
else |
||||
assumeTrue(matchExpected == matcher.isMatch(input, assumeDir)); |
||||
} |
||||
} |
||||
|
||||
private void assertFileNameMatch(final String pattern, final String input, |
||||
final boolean matchExpected) { |
||||
boolean assumeDir = input.endsWith("/"); |
||||
if (useJGitRule.booleanValue()) { |
||||
final IgnoreRule matcher = new IgnoreRule(pattern); |
||||
assertEquals(matchExpected, matcher.isMatch(input, assumeDir)); |
||||
} else { |
||||
FastIgnoreRule matcher = new FastIgnoreRule(pattern); |
||||
assertEquals(matchExpected, matcher.isMatch(input, assumeDir)); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimplePatternCase0() throws Exception { |
||||
if (useJGitRule) |
||||
System.err |
||||
.println("IgnoreRule can't understand blank lines, skipping"); |
||||
Boolean assume = useJGitRule; |
||||
assertMatch("", "", false, assume); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimplePatternCase1() throws Exception { |
||||
assertMatch("ab", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimplePatternCase2() throws Exception { |
||||
assertMatch("ab", "ab", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimplePatternCase3() throws Exception { |
||||
assertMatch("ab", "ac", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimplePatternCase4() throws Exception { |
||||
assertMatch("ab", "abc", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleWildcardCase0() throws Exception { |
||||
assertMatch("?", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleWildCardCase1() throws Exception { |
||||
assertMatch("??", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleWildCardCase2() throws Exception { |
||||
assertMatch("??", "ab", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleWildCardCase3() throws Exception { |
||||
assertMatch("??", "abc", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleStarCase0() throws Exception { |
||||
// can't happen, but blank lines should never match
|
||||
assertMatch("*", "", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleStarCase1() throws Exception { |
||||
assertMatch("*", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleStarCase2() throws Exception { |
||||
assertMatch("*", "ab", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSimpleStarCase0() throws Exception { |
||||
assertMatch("a*b", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testSimpleStarCase1() throws Exception { |
||||
assertMatch("a*c", "ac", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSimpleStarCase2() throws Exception { |
||||
assertMatch("a*c", "ab", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testSimpleStarCase3() throws Exception { |
||||
assertMatch("a*c", "abc", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testManySolutionsCase0() throws Exception { |
||||
assertMatch("a*a*a", "aaa", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testManySolutionsCase1() throws Exception { |
||||
assertMatch("a*a*a", "aaaa", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testManySolutionsCase2() throws Exception { |
||||
assertMatch("a*a*a", "ababa", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testManySolutionsCase3() throws Exception { |
||||
assertMatch("a*a*a", "aaaaaaaa", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testManySolutionsCase4() throws Exception { |
||||
assertMatch("a*a*a", "aaaaaaab", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupCase0() throws Exception { |
||||
assertMatch("[ab]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupCase1() throws Exception { |
||||
assertMatch("[ab]", "b", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupCase2() throws Exception { |
||||
assertMatch("[ab]", "ab", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupRangeCase0() throws Exception { |
||||
assertMatch("[b-d]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupRangeCase1() throws Exception { |
||||
assertMatch("[b-d]", "b", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupRangeCase2() throws Exception { |
||||
assertMatch("[b-d]", "c", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupRangeCase3() throws Exception { |
||||
assertMatch("[b-d]", "d", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupRangeCase4() throws Exception { |
||||
assertMatch("[b-d]", "e", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testVerySimpleGroupRangeCase5() throws Exception { |
||||
assertMatch("[b-d]", "-", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoGroupsCase0() throws Exception { |
||||
assertMatch("[b-d][ab]", "bb", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoGroupsCase1() throws Exception { |
||||
assertMatch("[b-d][ab]", "ca", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoGroupsCase2() throws Exception { |
||||
assertMatch("[b-d][ab]", "fa", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoGroupsCase3() throws Exception { |
||||
assertMatch("[b-d][ab]", "bc", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoRangesInOneGroupCase0() throws Exception { |
||||
assertMatch("[b-ce-e]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoRangesInOneGroupCase1() throws Exception { |
||||
assertMatch("[b-ce-e]", "b", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoRangesInOneGroupCase2() throws Exception { |
||||
assertMatch("[b-ce-e]", "c", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoRangesInOneGroupCase3() throws Exception { |
||||
assertMatch("[b-ce-e]", "d", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoRangesInOneGroupCase4() throws Exception { |
||||
assertMatch("[b-ce-e]", "e", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testTwoRangesInOneGroupCase5() throws Exception { |
||||
assertMatch("[b-ce-e]", "f", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testIncompleteRangesInOneGroupCase0() throws Exception { |
||||
assertMatch("a[b-]", "ab", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testIncompleteRangesInOneGroupCase1() throws Exception { |
||||
assertMatch("a[b-]", "ac", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testIncompleteRangesInOneGroupCase2() throws Exception { |
||||
assertMatch("a[b-]", "a-", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testCombinedRangesInOneGroupCase0() throws Exception { |
||||
assertMatch("[a-c-e]", "b", true); |
||||
} |
||||
|
||||
/** |
||||
* The c belongs to the range a-c. "-e" is no valid range so d should not |
||||
* match. |
||||
* |
||||
* @throws Exception |
||||
* for some reasons |
||||
*/ |
||||
@Test |
||||
public void testCombinedRangesInOneGroupCase1() throws Exception { |
||||
assertMatch("[a-c-e]", "d", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testCombinedRangesInOneGroupCase2() throws Exception { |
||||
assertMatch("[a-c-e]", "e", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testInversedGroupCase0() throws Exception { |
||||
assertMatch("[!b-c]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testInversedGroupCase1() throws Exception { |
||||
assertMatch("[!b-c]", "b", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testInversedGroupCase2() throws Exception { |
||||
assertMatch("[!b-c]", "c", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testInversedGroupCase3() throws Exception { |
||||
assertMatch("[!b-c]", "d", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testAlphaGroupCase0() throws Exception { |
||||
assertMatch("[[:alpha:]]", "d", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testAlphaGroupCase1() throws Exception { |
||||
assertMatch("[[:alpha:]]", ":", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testAlphaGroupCase2() throws Exception { |
||||
// \u00f6 = 'o' with dots on it
|
||||
assertMatch("[[:alpha:]]", "\u00f6", true); |
||||
} |
||||
|
||||
@Test |
||||
public void test2AlphaGroupsCase0() throws Exception { |
||||
// \u00f6 = 'o' with dots on it
|
||||
assertMatch("[[:alpha:]][[:alpha:]]", "a\u00f6", true); |
||||
assertMatch("[[:alpha:]][[:alpha:]]", "a1", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testAlnumGroupCase0() throws Exception { |
||||
assertMatch("[[:alnum:]]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testAlnumGroupCase1() throws Exception { |
||||
assertMatch("[[:alnum:]]", "1", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testAlnumGroupCase2() throws Exception { |
||||
assertMatch("[[:alnum:]]", ":", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testBlankGroupCase0() throws Exception { |
||||
assertMatch("[[:blank:]]", " ", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testBlankGroupCase1() throws Exception { |
||||
assertMatch("[[:blank:]]", "\t", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testBlankGroupCase2() throws Exception { |
||||
assertMatch("[[:blank:]]", "\r", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testBlankGroupCase3() throws Exception { |
||||
assertMatch("[[:blank:]]", "\n", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testBlankGroupCase4() throws Exception { |
||||
assertMatch("[[:blank:]]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testCntrlGroupCase0() throws Exception { |
||||
assertMatch("[[:cntrl:]]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testCntrlGroupCase1() throws Exception { |
||||
assertMatch("[[:cntrl:]]", String.valueOf((char) 7), true); |
||||
} |
||||
|
||||
@Test |
||||
public void testDigitGroupCase0() throws Exception { |
||||
assertMatch("[[:digit:]]", "0", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testDigitGroupCase1() throws Exception { |
||||
assertMatch("[[:digit:]]", "5", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testDigitGroupCase2() throws Exception { |
||||
assertMatch("[[:digit:]]", "9", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testDigitGroupCase3() throws Exception { |
||||
// \u06f9 = EXTENDED ARABIC-INDIC DIGIT NINE
|
||||
assertMatch("[[:digit:]]", "\u06f9", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testDigitGroupCase4() throws Exception { |
||||
assertMatch("[[:digit:]]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testDigitGroupCase5() throws Exception { |
||||
assertMatch("[[:digit:]]", "]", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testGraphGroupCase0() throws Exception { |
||||
assertMatch("[[:graph:]]", "]", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testGraphGroupCase1() throws Exception { |
||||
assertMatch("[[:graph:]]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testGraphGroupCase2() throws Exception { |
||||
assertMatch("[[:graph:]]", ".", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testGraphGroupCase3() throws Exception { |
||||
assertMatch("[[:graph:]]", "0", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testGraphGroupCase4() throws Exception { |
||||
assertMatch("[[:graph:]]", " ", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testGraphGroupCase5() throws Exception { |
||||
// \u00f6 = 'o' with dots on it
|
||||
assertMatch("[[:graph:]]", "\u00f6", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testLowerGroupCase0() throws Exception { |
||||
assertMatch("[[:lower:]]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testLowerGroupCase1() throws Exception { |
||||
assertMatch("[[:lower:]]", "h", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testLowerGroupCase2() throws Exception { |
||||
assertMatch("[[:lower:]]", "A", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testLowerGroupCase3() throws Exception { |
||||
assertMatch("[[:lower:]]", "H", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testLowerGroupCase4() throws Exception { |
||||
// \u00e4 = small 'a' with dots on it
|
||||
assertMatch("[[:lower:]]", "\u00e4", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testLowerGroupCase5() throws Exception { |
||||
assertMatch("[[:lower:]]", ".", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testPrintGroupCase0() throws Exception { |
||||
assertMatch("[[:print:]]", "]", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPrintGroupCase1() throws Exception { |
||||
assertMatch("[[:print:]]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPrintGroupCase2() throws Exception { |
||||
assertMatch("[[:print:]]", ".", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPrintGroupCase3() throws Exception { |
||||
assertMatch("[[:print:]]", "0", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPrintGroupCase4() throws Exception { |
||||
assertMatch("[[:print:]]", " ", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPrintGroupCase5() throws Exception { |
||||
// \u00f6 = 'o' with dots on it
|
||||
assertMatch("[[:print:]]", "\u00f6", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPunctGroupCase0() throws Exception { |
||||
assertMatch("[[:punct:]]", ".", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPunctGroupCase1() throws Exception { |
||||
assertMatch("[[:punct:]]", "@", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testPunctGroupCase2() throws Exception { |
||||
assertMatch("[[:punct:]]", " ", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testPunctGroupCase3() throws Exception { |
||||
assertMatch("[[:punct:]]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpaceGroupCase0() throws Exception { |
||||
assertMatch("[[:space:]]", " ", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpaceGroupCase1() throws Exception { |
||||
assertMatch("[[:space:]]", "\t", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpaceGroupCase2() throws Exception { |
||||
assertMatch("[[:space:]]", "\r", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpaceGroupCase3() throws Exception { |
||||
assertMatch("[[:space:]]", "\n", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpaceGroupCase4() throws Exception { |
||||
assertMatch("[[:space:]]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpperGroupCase0() throws Exception { |
||||
assertMatch("[[:upper:]]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpperGroupCase1() throws Exception { |
||||
assertMatch("[[:upper:]]", "h", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpperGroupCase2() throws Exception { |
||||
assertMatch("[[:upper:]]", "A", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpperGroupCase3() throws Exception { |
||||
assertMatch("[[:upper:]]", "H", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpperGroupCase4() throws Exception { |
||||
// \u00c4 = 'A' with dots on it
|
||||
assertMatch("[[:upper:]]", "\u00c4", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testUpperGroupCase5() throws Exception { |
||||
assertMatch("[[:upper:]]", ".", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase0() throws Exception { |
||||
assertMatch("[[:xdigit:]]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase1() throws Exception { |
||||
assertMatch("[[:xdigit:]]", "d", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase2() throws Exception { |
||||
assertMatch("[[:xdigit:]]", "f", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase3() throws Exception { |
||||
assertMatch("[[:xdigit:]]", "0", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase4() throws Exception { |
||||
assertMatch("[[:xdigit:]]", "5", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase5() throws Exception { |
||||
assertMatch("[[:xdigit:]]", "9", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase6() throws Exception { |
||||
assertMatch("[[:xdigit:]]", "۹", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testXDigitGroupCase7() throws Exception { |
||||
assertMatch("[[:xdigit:]]", ".", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testWordGroupCase0() throws Exception { |
||||
assertMatch("[[:word:]]", "g", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testWordGroupCase1() throws Exception { |
||||
// \u00f6 = 'o' with dots on it
|
||||
assertMatch("[[:word:]]", "\u00f6", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testWordGroupCase2() throws Exception { |
||||
assertMatch("[[:word:]]", "5", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testWordGroupCase3() throws Exception { |
||||
assertMatch("[[:word:]]", "_", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testWordGroupCase4() throws Exception { |
||||
assertMatch("[[:word:]]", " ", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testWordGroupCase5() throws Exception { |
||||
assertMatch("[[:word:]]", ".", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase0() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "A", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase1() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "C", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase2() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "e", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase3() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "3", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase4() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "4", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase5() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "5", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase6() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "B", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase7() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "2", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase8() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", "6", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testMixedGroupCase9() throws Exception { |
||||
assertMatch("[A[:lower:]C3-5]", ".", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase0() throws Exception { |
||||
assertMatch("[[]", "[", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase1() throws Exception { |
||||
assertMatch("[]]", "]", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase2() throws Exception { |
||||
assertMatch("[]a]", "]", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase3() throws Exception { |
||||
assertMatch("[a[]", "[", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase4() throws Exception { |
||||
assertMatch("[a[]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase5() throws Exception { |
||||
assertMatch("[!]]", "]", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase6() throws Exception { |
||||
assertMatch("[!]]", "x", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase7() throws Exception { |
||||
assertMatch("[:]]", ":]", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase8() throws Exception { |
||||
assertMatch("[:]]", ":", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testSpecialGroupCase9() throws Exception { |
||||
if (useJGitRule) |
||||
System.err.println("IgnoreRule can't understand [[:], skipping"); |
||||
Boolean assume = useJGitRule; |
||||
// Second bracket is threated literally, so both [ and : should match
|
||||
assertMatch("[[:]", ":", true, assume); |
||||
assertMatch("[[:]", "[", true, assume); |
||||
} |
||||
|
||||
@Test |
||||
public void testUnsupportedGroupCase0() throws Exception { |
||||
assertMatch("[[=a=]]", "a", false); |
||||
assertMatch("[[=a=]]", "=", false); |
||||
assertMatch("[=a=]", "a", true); |
||||
assertMatch("[=a=]", "=", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testUnsupportedGroupCase01() throws Exception { |
||||
assertMatch("[.a.]*[.a.]", "aha", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testUnsupportedGroupCase1() throws Exception { |
||||
assertMatch("[[.a.]]", "a", false); |
||||
assertMatch("[[.a.]]", ".", false); |
||||
assertMatch("[.a.]", "a", true); |
||||
assertMatch("[.a.]", ".", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testEscapedBracket1() throws Exception { |
||||
assertMatch("\\[", "[", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testEscapedBracket2() throws Exception { |
||||
assertMatch("\\[[a]", "[", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testEscapedBracket3() throws Exception { |
||||
assertMatch("\\[[a]", "a", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testEscapedBracket4() throws Exception { |
||||
assertMatch("\\[[a]", "[a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testEscapedBracket5() throws Exception { |
||||
assertMatch("[a\\]]", "]", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testEscapedBracket6() throws Exception { |
||||
assertMatch("[a\\]]", "a", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testEscapedBackslash() throws Exception { |
||||
if (useJGitRule) |
||||
System.err |
||||
.println("IgnoreRule can't understand escaped backslashes, skipping"); |
||||
Boolean assume = useJGitRule; |
||||
// In Git CLI a\\b matches a\b file
|
||||
assertMatch("a\\\\b", "a\\b", true, assume); |
||||
} |
||||
|
||||
@Test |
||||
public void testMultipleEscapedCharacters1() throws Exception { |
||||
assertMatch("\\]a?c\\*\\[d\\?\\]", "]abc*[d?]", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testFilePathSimpleCase() throws Exception { |
||||
assertFileNameMatch("a/b", "a/b", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testFilePathCase0() throws Exception { |
||||
assertFileNameMatch("a*b", "a/b", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testFilePathCase1() throws Exception { |
||||
assertFileNameMatch("a?b", "a/b", false); |
||||
} |
||||
|
||||
@Test |
||||
public void testFilePathCase2() throws Exception { |
||||
assertFileNameMatch("a*b", "a\\b", true); |
||||
} |
||||
|
||||
@Test |
||||
public void testFilePathCase3() throws Exception { |
||||
assertFileNameMatch("a?b", "a\\b", true); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,229 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore; |
||||
|
||||
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailing; |
||||
|
||||
import org.eclipse.jgit.errors.InvalidPatternException; |
||||
import org.eclipse.jgit.ignore.internal.IMatcher; |
||||
import org.eclipse.jgit.ignore.internal.PathMatcher; |
||||
|
||||
/** |
||||
* "Fast" (compared with IgnoreRule) git ignore rule implementation supporting |
||||
* also double star <code>**<code> pattern. |
||||
* <p> |
||||
* This class is immutable and thread safe. |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public class FastIgnoreRule { |
||||
|
||||
/** |
||||
* Character used as default path separator for ignore entries |
||||
*/ |
||||
public static final char PATH_SEPARATOR = '/'; |
||||
|
||||
private static final NoResultMatcher NO_MATCH = new NoResultMatcher(); |
||||
|
||||
private final IMatcher matcher; |
||||
|
||||
private final boolean inverse; |
||||
|
||||
private final boolean dirOnly; |
||||
|
||||
/** |
||||
* |
||||
* @param pattern |
||||
* ignore pattern as described in <a href= |
||||
* "https://www.kernel.org/pub/software/scm/git/docs/gitignore.html" |
||||
* >git manual</a>. If pattern is invalid or is not a pattern |
||||
* (comment), this rule doesn't match anything. |
||||
*/ |
||||
public FastIgnoreRule(String pattern) { |
||||
if (pattern == null) |
||||
throw new IllegalArgumentException("Pattern must not be null!"); //$NON-NLS-1$
|
||||
if (pattern.length() == 0) { |
||||
dirOnly = false; |
||||
inverse = false; |
||||
this.matcher = NO_MATCH; |
||||
return; |
||||
} |
||||
inverse = pattern.charAt(0) == '!'; |
||||
if (inverse) { |
||||
pattern = pattern.substring(1); |
||||
if (pattern.length() == 0) { |
||||
dirOnly = false; |
||||
this.matcher = NO_MATCH; |
||||
return; |
||||
} |
||||
} |
||||
if (pattern.charAt(0) == '#') { |
||||
this.matcher = NO_MATCH; |
||||
dirOnly = false; |
||||
} else { |
||||
dirOnly = pattern.charAt(pattern.length() - 1) == PATH_SEPARATOR; |
||||
if (dirOnly) { |
||||
pattern = stripTrailing(pattern, PATH_SEPARATOR); |
||||
if (pattern.length() == 0) { |
||||
this.matcher = NO_MATCH; |
||||
return; |
||||
} |
||||
} |
||||
IMatcher m; |
||||
try { |
||||
m = PathMatcher.createPathMatcher(pattern, |
||||
Character.valueOf(PATH_SEPARATOR), dirOnly); |
||||
} catch (InvalidPatternException e) { |
||||
m = NO_MATCH; |
||||
} |
||||
this.matcher = m; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns true if a match was made. <br> |
||||
* This function does NOT return the actual ignore status of the target! |
||||
* Please consult {@link #getResult()} for the negation status. The actual |
||||
* ignore status may be true or false depending on whether this rule is an |
||||
* ignore rule or a negation rule. |
||||
* |
||||
* @param path |
||||
* Name pattern of the file, relative to the base directory of |
||||
* this rule |
||||
* @param directory |
||||
* Whether the target file is a directory or not |
||||
* @return True if a match was made. This does not necessarily mean that the |
||||
* target is ignored. Call {@link #getResult() getResult()} for the |
||||
* result. |
||||
*/ |
||||
public boolean isMatch(String path, boolean directory) { |
||||
if (path == null) |
||||
return false; |
||||
if (path.length() == 0) |
||||
return false; |
||||
boolean match = matcher.matches(path, directory); |
||||
return match; |
||||
} |
||||
|
||||
/** |
||||
* @return True if the pattern is just a file name and not a path |
||||
*/ |
||||
public boolean getNameOnly() { |
||||
return !(matcher instanceof PathMatcher); |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @return True if the pattern should match directories only |
||||
*/ |
||||
public boolean dirOnly() { |
||||
return dirOnly; |
||||
} |
||||
|
||||
/** |
||||
* Indicates whether the rule is non-negation or negation. |
||||
* |
||||
* @return True if the pattern had a "!" in front of it |
||||
*/ |
||||
public boolean getNegation() { |
||||
return inverse; |
||||
} |
||||
|
||||
/** |
||||
* Indicates whether the rule is non-negation or negation. |
||||
* |
||||
* @return True if the target is to be ignored, false otherwise. |
||||
*/ |
||||
public boolean getResult() { |
||||
return !inverse; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
if (inverse) |
||||
sb.append('!'); |
||||
sb.append(matcher); |
||||
if (dirOnly) |
||||
sb.append(PATH_SEPARATOR); |
||||
return sb.toString(); |
||||
|
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = prime * result + (inverse ? 1231 : 1237); |
||||
result = prime * result + (dirOnly ? 1231 : 1237); |
||||
result = prime * result + ((matcher == null) ? 0 : matcher.hashCode()); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) |
||||
return true; |
||||
if (!(obj instanceof FastIgnoreRule)) |
||||
return false; |
||||
|
||||
FastIgnoreRule other = (FastIgnoreRule) obj; |
||||
if (inverse != other.inverse) |
||||
return false; |
||||
if (dirOnly != other.dirOnly) |
||||
return false; |
||||
return matcher.equals(other.matcher); |
||||
} |
||||
|
||||
static final class NoResultMatcher implements IMatcher { |
||||
|
||||
public boolean matches(String path, boolean assumeDirectory) { |
||||
return false; |
||||
} |
||||
|
||||
public boolean matches(String segment, int startIncl, int endExcl, |
||||
boolean assumeDirectory) { |
||||
return false; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,88 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore.internal; |
||||
|
||||
/** |
||||
* Base class for default methods as {@link #toString()} and such. |
||||
* <p> |
||||
* This class is immutable and thread safe. |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public abstract class AbstractMatcher implements IMatcher { |
||||
|
||||
final boolean dirOnly; |
||||
|
||||
final String pattern; |
||||
|
||||
/** |
||||
* @param pattern |
||||
* string to parse |
||||
* @param dirOnly |
||||
* true if this matcher should match only directories |
||||
*/ |
||||
AbstractMatcher(String pattern, boolean dirOnly) { |
||||
this.pattern = pattern; |
||||
this.dirOnly = dirOnly; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return pattern; |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return pattern.hashCode(); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) |
||||
return true; |
||||
if (!(obj instanceof AbstractMatcher)) |
||||
return false; |
||||
AbstractMatcher other = (AbstractMatcher) obj; |
||||
return dirOnly == other.dirOnly && pattern.equals(other.pattern); |
||||
} |
||||
} |
@ -0,0 +1,80 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore.internal; |
||||
|
||||
/** |
||||
* Generic string matcher |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public interface IMatcher { |
||||
|
||||
/** |
||||
* Matches entire given string |
||||
* |
||||
* @param path |
||||
* string which is not null, but might be empty |
||||
* @param assumeDirectory |
||||
* true to assume this path as directory (even if it doesn't end |
||||
* with a slash) |
||||
* @return true if this matcher pattern matches given string |
||||
*/ |
||||
boolean matches(String path, boolean assumeDirectory); |
||||
|
||||
/** |
||||
* Matches only part of given string |
||||
* |
||||
* @param segment |
||||
* string which is not null, but might be empty |
||||
* @param startIncl |
||||
* start index, inclusive |
||||
* @param endExcl |
||||
* end index, exclusive |
||||
* @param assumeDirectory |
||||
* true to assume this path as directory (even if it doesn't end |
||||
* with a slash) |
||||
* @return true if this matcher pattern matches given string |
||||
*/ |
||||
boolean matches(String segment, int startIncl, int endExcl, |
||||
boolean assumeDirectory); |
||||
} |
@ -0,0 +1,112 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore.internal; |
||||
|
||||
import static org.eclipse.jgit.ignore.internal.Strings.getPathSeparator; |
||||
|
||||
/** |
||||
* Matcher built from patterns for file names (single path segments). This class
|
||||
* is immutable and thread safe. |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public class NameMatcher extends AbstractMatcher { |
||||
|
||||
final boolean beginning; |
||||
|
||||
final char slash; |
||||
|
||||
final String subPattern; |
||||
|
||||
NameMatcher(String pattern, Character pathSeparator, boolean dirOnly) { |
||||
super(pattern, dirOnly); |
||||
slash = getPathSeparator(pathSeparator); |
||||
beginning = pattern.length() == 0 ? false : pattern.charAt(0) == slash; |
||||
if (!beginning) |
||||
this.subPattern = pattern; |
||||
else |
||||
this.subPattern = pattern.substring(1); |
||||
} |
||||
|
||||
public boolean matches(String path, boolean assumeDirectory) { |
||||
int end = 0; |
||||
int firstChar = 0; |
||||
do { |
||||
firstChar = getFirstNotSlash(path, end); |
||||
end = getFirstSlash(path, firstChar); |
||||
boolean match = matches(path, firstChar, end, assumeDirectory); |
||||
if (match) |
||||
// make sure the directory matches: either if we are done with
|
||||
// segment and there is next one, or if the directory is assumed
|
||||
return !dirOnly ? true : (end > 0 && end != path.length()) |
||||
|| assumeDirectory; |
||||
} while (!beginning && end != path.length()); |
||||
return false; |
||||
} |
||||
|
||||
public boolean matches(String segment, int startIncl, int endExcl, |
||||
boolean assumeDirectory) { |
||||
// faster local access, same as in string.indexOf()
|
||||
String s = subPattern; |
||||
if (s.length() != (endExcl - startIncl)) |
||||
return false; |
||||
for (int i = 0; i < s.length(); i++) { |
||||
char c1 = s.charAt(i); |
||||
char c2 = segment.charAt(i + startIncl); |
||||
if (c1 != c2) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
private int getFirstNotSlash(String s, int start) { |
||||
int slashIdx = s.indexOf(slash, start); |
||||
return slashIdx == start ? start + 1 : start; |
||||
} |
||||
|
||||
private int getFirstSlash(String s, int start) { |
||||
int slashIdx = s.indexOf(slash, start); |
||||
return slashIdx == -1 ? s.length() : slashIdx; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,239 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore.internal; |
||||
|
||||
import static org.eclipse.jgit.ignore.internal.Strings.*; |
||||
|
||||
import java.util.*; |
||||
|
||||
import org.eclipse.jgit.errors.InvalidPatternException; |
||||
import org.eclipse.jgit.ignore.FastIgnoreRule; |
||||
|
||||
/** |
||||
* Matcher built by patterns consists of multiple path segments. |
||||
* <p> |
||||
* This class is immutable and thread safe. |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public class PathMatcher extends AbstractMatcher { |
||||
|
||||
private static final WildMatcher WILD = WildMatcher.INSTANCE; |
||||
|
||||
private final List<IMatcher> matchers; |
||||
|
||||
private final char slash; |
||||
|
||||
private boolean beginning; |
||||
|
||||
PathMatcher(String pattern, Character pathSeparator, boolean dirOnly) |
||||
throws InvalidPatternException { |
||||
super(pattern, dirOnly); |
||||
slash = getPathSeparator(pathSeparator); |
||||
beginning = pattern.indexOf(slash) == 0; |
||||
if (isSimplePathWithSegments(pattern)) |
||||
matchers = null; |
||||
else |
||||
matchers = createMatchers(split(pattern, slash), pathSeparator, |
||||
dirOnly); |
||||
} |
||||
|
||||
private boolean isSimplePathWithSegments(String path) { |
||||
return !isWildCard(path) && count(path, slash, true) > 0; |
||||
} |
||||
|
||||
static private List<IMatcher> createMatchers(List<String> segments, |
||||
Character pathSeparator, boolean dirOnly) |
||||
throws InvalidPatternException { |
||||
List<IMatcher> matchers = new ArrayList<IMatcher>(segments.size()); |
||||
for (int i = 0; i < segments.size(); i++) { |
||||
String segment = segments.get(i); |
||||
IMatcher matcher = createNameMatcher0(segment, pathSeparator, |
||||
dirOnly); |
||||
if (matcher == WILD && i > 0 |
||||
&& matchers.get(matchers.size() - 1) == WILD) |
||||
// collapse wildmatchers **/** is same as **
|
||||
continue; |
||||
matchers.add(matcher); |
||||
} |
||||
return matchers; |
||||
} |
||||
|
||||
/** |
||||
* |
||||
* @param pattern |
||||
* @param pathSeparator |
||||
* if this parameter isn't null then this character will not |
||||
* match at wildcards(* and ? are wildcards). |
||||
* @param dirOnly |
||||
* @return never null |
||||
* @throws InvalidPatternException |
||||
*/ |
||||
public static IMatcher createPathMatcher(String pattern, |
||||
Character pathSeparator, boolean dirOnly) |
||||
throws InvalidPatternException { |
||||
pattern = pattern.trim(); |
||||
char slash = Strings.getPathSeparator(pathSeparator); |
||||
// ignore possible leading and trailing slash
|
||||
int slashIdx = pattern.indexOf(slash, 1); |
||||
if (slashIdx > 0 && slashIdx < pattern.length() - 1) |
||||
return new PathMatcher(pattern, pathSeparator, dirOnly); |
||||
return createNameMatcher0(pattern, pathSeparator, dirOnly); |
||||
} |
||||
|
||||
private static IMatcher createNameMatcher0(String segment, |
||||
Character pathSeparator, boolean dirOnly) |
||||
throws InvalidPatternException { |
||||
// check if we see /** or ** segments => double star pattern
|
||||
if (WildMatcher.WILDMATCH.equals(segment) |
||||
|| WildMatcher.WILDMATCH2.equals(segment)) |
||||
return WILD; |
||||
if (isWildCard(segment)) |
||||
return new WildCardMatcher(segment, pathSeparator, dirOnly); |
||||
return new NameMatcher(segment, pathSeparator, dirOnly); |
||||
} |
||||
|
||||
public boolean matches(String path, boolean assumeDirectory) { |
||||
if (matchers == null) |
||||
return simpleMatch(path, assumeDirectory); |
||||
return iterate(path, 0, path.length(), assumeDirectory); |
||||
} |
||||
|
||||
/* |
||||
* Stupid but fast string comparison: the case where we don't have to match |
||||
* wildcards or single segments (mean: this is multi-segment path which must |
||||
* be at the beginning of the another string) |
||||
*/ |
||||
private boolean simpleMatch(String path, boolean assumeDirectory) { |
||||
boolean hasSlash = path.indexOf(slash) == 0; |
||||
if (beginning && !hasSlash) |
||||
path = slash + path; |
||||
|
||||
if (!beginning && hasSlash) |
||||
path = path.substring(1); |
||||
|
||||
if (path.equals(pattern)) |
||||
// Exact match
|
||||
if (dirOnly && !assumeDirectory) |
||||
// Directory expectations not met
|
||||
return false; |
||||
else |
||||
// Directory expectations met
|
||||
return true; |
||||
|
||||
/* |
||||
* Add slashes for startsWith check. This avoids matching e.g. |
||||
* "/src/new" to /src/newfile" but allows "/src/new" to match |
||||
* "/src/new/newfile", as is the git standard |
||||
*/ |
||||
if (path.startsWith(pattern + FastIgnoreRule.PATH_SEPARATOR)) |
||||
return true; |
||||
|
||||
return false; |
||||
} |
||||
|
||||
public boolean matches(String segment, int startIncl, int endExcl, |
||||
boolean assumeDirectory) { |
||||
throw new UnsupportedOperationException( |
||||
"Path matcher works only on entire paths"); //$NON-NLS-1$
|
||||
} |
||||
|
||||
boolean iterate(final String path, final int startIncl, final int endExcl, |
||||
boolean assumeDirectory) { |
||||
int matcher = 0; |
||||
int right = startIncl; |
||||
boolean match = false; |
||||
int lastWildmatch = -1; |
||||
while (true) { |
||||
int left = right; |
||||
right = path.indexOf(slash, right); |
||||
if (right == -1) { |
||||
if (left < endExcl) |
||||
match = matches(matcher, path, left, endExcl, |
||||
assumeDirectory); |
||||
if (match) { |
||||
if (matcher == matchers.size() - 2 |
||||
&& matchers.get(matcher + 1) == WILD) |
||||
// ** can match *nothing*: a/b/** match also a/b
|
||||
return true; |
||||
if (matcher < matchers.size() - 1 |
||||
&& matchers.get(matcher) == WILD) { |
||||
// ** can match *nothing*: a/**/b match also a/b
|
||||
matcher++; |
||||
match = matches(matcher, path, left, endExcl, |
||||
assumeDirectory); |
||||
} else if (dirOnly) |
||||
return false; |
||||
} |
||||
return match && matcher + 1 == matchers.size(); |
||||
} |
||||
if (right - left > 0) |
||||
match = matches(matcher, path, left, right, assumeDirectory); |
||||
else { |
||||
// path starts with slash???
|
||||
right++; |
||||
continue; |
||||
} |
||||
if (match) { |
||||
if (matchers.get(matcher) == WILD) { |
||||
lastWildmatch = matcher; |
||||
// ** can match *nothing*: a/**/b match also a/b
|
||||
right = left - 1; |
||||
} |
||||
matcher++; |
||||
if (matcher == matchers.size()) |
||||
return true; |
||||
} else if (lastWildmatch != -1) |
||||
matcher = lastWildmatch + 1; |
||||
else |
||||
return false; |
||||
right++; |
||||
} |
||||
} |
||||
|
||||
boolean matches(int matcherIdx, String path, int startIncl, int endExcl, |
||||
boolean assumeDirectory) { |
||||
IMatcher matcher = matchers.get(matcherIdx); |
||||
return matcher.matches(path, startIncl, endExcl, assumeDirectory); |
||||
} |
||||
} |
@ -0,0 +1,373 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore.internal; |
||||
|
||||
import static java.lang.Character.isLetter; |
||||
|
||||
import java.util.*; |
||||
import java.util.regex.Pattern; |
||||
|
||||
import org.eclipse.jgit.errors.InvalidPatternException; |
||||
import org.eclipse.jgit.ignore.FastIgnoreRule; |
||||
|
||||
/** |
||||
* Various {@link String} related utility methods, written mostly to avoid |
||||
* generation of new String objects (e.g. via splitting Strings etc). |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public class Strings { |
||||
|
||||
static char getPathSeparator(Character pathSeparator) { |
||||
return pathSeparator == null ? FastIgnoreRule.PATH_SEPARATOR |
||||
: pathSeparator.charValue(); |
||||
} |
||||
|
||||
/** |
||||
* @param pattern |
||||
* non null |
||||
* @param c |
||||
* character to remove |
||||
* @return new string with all trailing characters removed |
||||
*/ |
||||
public static String stripTrailing(String pattern, char c) { |
||||
while (pattern.length() > 0 |
||||
&& pattern.charAt(pattern.length() - 1) == c) |
||||
pattern = pattern.substring(0, pattern.length() - 1); |
||||
return pattern; |
||||
} |
||||
|
||||
static int count(String s, char c, boolean ignoreFirstLast) { |
||||
int start = 0; |
||||
int count = 0; |
||||
while (true) { |
||||
start = s.indexOf(c, start); |
||||
if (start == -1) |
||||
break; |
||||
if (!ignoreFirstLast || (start != 0 && start != s.length())) |
||||
count++; |
||||
start++; |
||||
} |
||||
return count; |
||||
} |
||||
|
||||
/** |
||||
* Splits given string to substrings by given separator |
||||
* |
||||
* @param pattern |
||||
* non null |
||||
* @param slash |
||||
* separator char |
||||
* @return list of substrings |
||||
*/ |
||||
public static List<String> split(String pattern, char slash) { |
||||
int count = count(pattern, slash, true); |
||||
if (count < 1) |
||||
throw new IllegalStateException( |
||||
"Pattern must have at least two segments: " + pattern); //$NON-NLS-1$
|
||||
List<String> segments = new ArrayList<String>(count); |
||||
int right = 0; |
||||
while (true) { |
||||
int left = right; |
||||
right = pattern.indexOf(slash, right); |
||||
if (right == -1) { |
||||
if (left < pattern.length()) |
||||
segments.add(pattern.substring(left)); |
||||
break; |
||||
} |
||||
if (right - left > 0) |
||||
if (left == 1) |
||||
// leading slash should remain by the first pattern
|
||||
segments.add(pattern.substring(left - 1, right)); |
||||
else if (right == pattern.length() - 1) |
||||
// trailing slash should remain too
|
||||
segments.add(pattern.substring(left, right + 1)); |
||||
else |
||||
segments.add(pattern.substring(left, right)); |
||||
right++; |
||||
} |
||||
return segments; |
||||
} |
||||
|
||||
static boolean isWildCard(String pattern) { |
||||
return pattern.indexOf('*') != -1 || pattern.indexOf('?') != -1 |
||||
|| pattern.indexOf('[') != -1 |
||||
// required to match escaped backslashes '\\\\'
|
||||
|| pattern.indexOf('\\') != -1 || pattern.indexOf(']') != -1; |
||||
} |
||||
|
||||
final static List<String> POSIX_CHAR_CLASSES = Arrays.asList( |
||||
"alnum", "alpha", "blank", "cntrl", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
// [:alnum:] [:alpha:] [:blank:] [:cntrl:]
|
||||
"digit", "graph", "lower", "print", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
// [:digit:] [:graph:] [:lower:] [:print:]
|
||||
"punct", "space", "upper", "xdigit", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
// [:punct:] [:space:] [:upper:] [:xdigit:]
|
||||
"word" //$NON-NLS-1$
|
||||
// [:word:] XXX I don't see it in
|
||||
// http://man7.org/linux/man-pages/man7/glob.7.html
|
||||
// but this was in org.eclipse.jgit.fnmatch.GroupHead.java ???
|
||||
); |
||||
|
||||
private static final String DL = "\\p{javaDigit}\\p{javaLetter}"; //$NON-NLS-1$
|
||||
|
||||
final static List<String> JAVA_CHAR_CLASSES = Arrays |
||||
.asList("\\p{Alnum}", "\\p{javaLetter}", "\\p{Blank}", "\\p{Cntrl}", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
// [:alnum:] [:alpha:] [:blank:] [:cntrl:]
|
||||
"\\p{javaDigit}", "[\\p{Graph}" + DL + "]", "\\p{Ll}", "[\\p{Print}" + DL + "]", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
|
||||
// [:digit:] [:graph:] [:lower:] [:print:]
|
||||
"\\p{Punct}", "\\p{Space}", "\\p{Lu}", "\\p{XDigit}", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
|
||||
// [:punct:] [:space:] [:upper:] [:xdigit:]
|
||||
"[" + DL + "_]" //$NON-NLS-1$ //$NON-NLS-2$
|
||||
// [:word:]
|
||||
); |
||||
|
||||
// Collating symbols [[.a.]] or equivalence class expressions [[=a=]] are
|
||||
// not supported by CLI git (at least not by 1.9.1)
|
||||
final static Pattern UNSUPPORTED = Pattern |
||||
.compile("\\[\\[[.=]\\w+[.=]\\]\\]"); //$NON-NLS-1$
|
||||
|
||||
/** |
||||
* Conversion from glob to Java regex following two sources: <li> |
||||
* http://man7.org/linux/man-pages/man7/glob.7.html <li>
|
||||
* org.eclipse.jgit.fnmatch.FileNameMatcher.java Seems that there are |
||||
* various ways to define what "glob" can be. |
||||
* |
||||
* @param pattern |
||||
* non null pattern |
||||
* |
||||
* @return Java regex pattern corresponding to given glob pattern |
||||
* @throws InvalidPatternException |
||||
*/ |
||||
static Pattern convertGlob(String pattern) throws InvalidPatternException { |
||||
if (UNSUPPORTED.matcher(pattern).find()) |
||||
throw new InvalidPatternException( |
||||
"Collating symbols [[.a.]] or equivalence class expressions [[=a=]] are not supported", //$NON-NLS-1$
|
||||
pattern); |
||||
|
||||
StringBuilder sb = new StringBuilder(pattern.length()); |
||||
|
||||
int in_brackets = 0; |
||||
boolean seenEscape = false; |
||||
boolean ignoreLastBracket = false; |
||||
boolean in_char_class = false; |
||||
// 6 is the length of the longest posix char class "xdigit"
|
||||
char[] charClass = new char[6]; |
||||
|
||||
for (int i = 0; i < pattern.length(); i++) { |
||||
char c = pattern.charAt(i); |
||||
switch (c) { |
||||
|
||||
case '*': |
||||
if (seenEscape || in_brackets > 0) |
||||
sb.append(c); |
||||
else |
||||
sb.append('.').append(c); |
||||
break; |
||||
|
||||
case '.': |
||||
if (seenEscape) |
||||
sb.append(c); |
||||
else |
||||
sb.append('\\').append('.'); |
||||
break; |
||||
|
||||
case '?': |
||||
if (seenEscape || in_brackets > 0) |
||||
sb.append(c); |
||||
else |
||||
sb.append('.'); |
||||
break; |
||||
|
||||
case ':': |
||||
if (in_brackets > 0) |
||||
if (lookBehind(sb) == '[' |
||||
&& isLetter(lookAhead(pattern, i))) |
||||
in_char_class = true; |
||||
sb.append(':'); |
||||
break; |
||||
|
||||
case '-': |
||||
if (in_brackets > 0) { |
||||
if (lookAhead(pattern, i) == ']') |
||||
sb.append('\\').append(c); |
||||
else |
||||
sb.append(c); |
||||
} else |
||||
sb.append('-'); |
||||
break; |
||||
|
||||
case '\\': |
||||
if (in_brackets > 0) { |
||||
char lookAhead = lookAhead(pattern, i); |
||||
if (lookAhead == ']' || lookAhead == '[') |
||||
ignoreLastBracket = true; |
||||
} |
||||
sb.append(c); |
||||
break; |
||||
|
||||
case '[': |
||||
if (in_brackets > 0) { |
||||
sb.append('\\').append('['); |
||||
ignoreLastBracket = true; |
||||
} else { |
||||
if (!seenEscape) { |
||||
in_brackets++; |
||||
ignoreLastBracket = false; |
||||
} |
||||
sb.append('['); |
||||
} |
||||
break; |
||||
|
||||
case ']': |
||||
if (seenEscape) { |
||||
sb.append(']'); |
||||
ignoreLastBracket = true; |
||||
break; |
||||
} |
||||
if (in_brackets <= 0) { |
||||
sb.append('\\').append(']'); |
||||
ignoreLastBracket = true; |
||||
break; |
||||
} |
||||
char lookBehind = lookBehind(sb); |
||||
if ((lookBehind == '[' && !ignoreLastBracket) |
||||
|| lookBehind == '^') { |
||||
sb.append('\\'); |
||||
sb.append(']'); |
||||
ignoreLastBracket = true; |
||||
} else { |
||||
ignoreLastBracket = false; |
||||
if (!in_char_class) { |
||||
in_brackets--; |
||||
sb.append(']'); |
||||
} else { |
||||
in_char_class = false; |
||||
String charCl = checkPosixCharClass(charClass); |
||||
// delete last \[:: chars and set the pattern
|
||||
if (charCl != null) { |
||||
sb.setLength(sb.length() - 4); |
||||
sb.append(charCl); |
||||
} |
||||
reset(charClass); |
||||
} |
||||
} |
||||
break; |
||||
|
||||
case '!': |
||||
if (in_brackets > 0) { |
||||
if (lookBehind(sb) == '[') |
||||
sb.append('^'); |
||||
else |
||||
sb.append(c); |
||||
} else |
||||
sb.append(c); |
||||
break; |
||||
|
||||
default: |
||||
if (in_char_class) |
||||
setNext(charClass, c); |
||||
else |
||||
sb.append(c); |
||||
break; |
||||
} // end switch
|
||||
|
||||
seenEscape = c == '\\'; |
||||
|
||||
} // end for
|
||||
|
||||
if (in_brackets > 0) |
||||
throw new InvalidPatternException("Not closed bracket?", pattern); //$NON-NLS-1$
|
||||
return Pattern.compile(sb.toString()); |
||||
} |
||||
|
||||
/** |
||||
* @param buffer |
||||
* @return zero of the buffer is empty, otherwise the last character from |
||||
* buffer |
||||
*/ |
||||
private static char lookBehind(StringBuilder buffer) { |
||||
return buffer.length() > 0 ? buffer.charAt(buffer.length() - 1) : 0; |
||||
} |
||||
|
||||
/** |
||||
* @param pattern |
||||
* @param i |
||||
* current pointer in the pattern |
||||
* @return zero of the index is out of range, otherwise the next character |
||||
* from given position |
||||
*/ |
||||
private static char lookAhead(String pattern, int i) { |
||||
int idx = i + 1; |
||||
return idx >= pattern.length() ? 0 : pattern.charAt(idx); |
||||
} |
||||
|
||||
private static void setNext(char[] buffer, char c) { |
||||
for (int i = 0; i < buffer.length; i++) |
||||
if (buffer[i] == 0) { |
||||
buffer[i] = c; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
private static void reset(char[] buffer) { |
||||
for (int i = 0; i < buffer.length; i++) |
||||
buffer[i] = 0; |
||||
} |
||||
|
||||
private static String checkPosixCharClass(char[] buffer) { |
||||
for (int i = 0; i < POSIX_CHAR_CLASSES.size(); i++) { |
||||
String clazz = POSIX_CHAR_CLASSES.get(i); |
||||
boolean match = true; |
||||
for (int j = 0; j < clazz.length(); j++) |
||||
if (buffer[j] != clazz.charAt(j)) { |
||||
match = false; |
||||
break; |
||||
} |
||||
if (match) |
||||
return JAVA_CHAR_CLASSES.get(i); |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,74 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore.internal; |
||||
|
||||
import static org.eclipse.jgit.ignore.internal.Strings.convertGlob; |
||||
|
||||
import java.util.regex.Pattern; |
||||
|
||||
import org.eclipse.jgit.errors.InvalidPatternException; |
||||
|
||||
/** |
||||
* Matcher built from path segments containing wildcards. This matcher converts |
||||
* glob wildcards to Java {@link Pattern}'s. |
||||
* <p> |
||||
* This class is immutable and thread safe. |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public class WildCardMatcher extends NameMatcher { |
||||
|
||||
final Pattern p; |
||||
|
||||
WildCardMatcher(String pattern, Character pathSeparator, boolean dirOnly) |
||||
throws InvalidPatternException { |
||||
super(pattern, pathSeparator, dirOnly); |
||||
p = convertGlob(subPattern); |
||||
} |
||||
|
||||
@Override |
||||
public boolean matches(String segment, int startIncl, int endExcl, |
||||
boolean assumeDirectory) { |
||||
return p.matcher(segment.substring(startIncl, endExcl)).matches(); |
||||
} |
||||
} |
@ -0,0 +1,75 @@
|
||||
/* |
||||
* Copyright (C) 2014, Andrey Loskutov <loskutov@gmx.de> |
||||
* and other copyright owners as documented in the project's IP log. |
||||
* |
||||
* This program and the accompanying materials are made available |
||||
* under the terms of the Eclipse Distribution License v1.0 which |
||||
* accompanies this distribution, is reproduced below, and is |
||||
* available at http://www.eclipse.org/org/documents/edl-v10.php
|
||||
* |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or |
||||
* without modification, are permitted provided that the following |
||||
* conditions are met: |
||||
* |
||||
* - Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* - Redistributions in binary form must reproduce the above |
||||
* copyright notice, this list of conditions and the following |
||||
* disclaimer in the documentation and/or other materials provided |
||||
* with the distribution. |
||||
* |
||||
* - Neither the name of the Eclipse Foundation, Inc. nor the |
||||
* names of its contributors may be used to endorse or promote |
||||
* products derived from this software without specific prior |
||||
* written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND |
||||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, |
||||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
||||
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
package org.eclipse.jgit.ignore.internal; |
||||
|
||||
/** |
||||
* Wildmatch matcher for "double star" (<code>**</code>) pattern only. This |
||||
* matcher matches any path. |
||||
* <p> |
||||
* This class is immutable and thread safe. |
||||
* |
||||
* @since 3.6 |
||||
*/ |
||||
public final class WildMatcher extends AbstractMatcher { |
||||
|
||||
static final String WILDMATCH = "**"; //$NON-NLS-1$
|
||||
|
||||
// double star for the beginning of pattern
|
||||
static final String WILDMATCH2 = "/**"; //$NON-NLS-1$
|
||||
|
||||
static final WildMatcher INSTANCE = new WildMatcher(); |
||||
|
||||
private WildMatcher() { |
||||
super(WILDMATCH, false); |
||||
} |
||||
|
||||
public final boolean matches(String path, boolean assumeDirectory) { |
||||
return true; |
||||
} |
||||
|
||||
public final boolean matches(String segment, int startIncl, int endExcl, |
||||
boolean assumeDirectory) { |
||||
return true; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue