From mboxrd@z Thu Jan 1 00:00:00 1970 From: Florian Koeberle Subject: [JGIT Patch] Added the package fnmatch and two exceptions. Date: Wed, 18 Jun 2008 12:22:30 +0200 Message-ID: <1213784550-11726-1-git-send-email-florianskarten@web.de> Mime-Version: 1.0 Content-Type: TEXT/PLAIN; charset=ISO-8859-1 Content-Transfer-Encoding: QUOTED-PRINTABLE Cc: Florian Koeberle To: git@vger.kernel.org X-From: git-owner@vger.kernel.org Wed Jun 18 12:23:48 2008 Return-path: Envelope-to: gcvg-git-2@gmane.org Received: from vger.kernel.org ([209.132.176.167]) by lo.gmane.org with esmtp (Exim 4.50) id 1K8upP-0002IB-4h for gcvg-git-2@gmane.org; Wed, 18 Jun 2008 12:23:48 +0200 Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752449AbYFRKWv (ORCPT ); Wed, 18 Jun 2008 06:22:51 -0400 X-Warning: Original message contained 8-bit characters, however during the SMTP transport session the receiving system did not announce capability of receiving 8-bit SMTP (RFC 1651-1653), and as this message does not have MIME headers (RFC 2045-2049) to enable encoding change, we had very little choice. X-Warning: We ASSUME it is less harmful to add the MIME headers, and convert the text to Quoted-Printable, than not to do so, and to strip the message to 7-bits.. (RFC 1428 Appendix A) X-Warning: We don't know what character set the user used, thus we had to write these MIME-headers with our local system default value. Received: (majordomo@vger.kernel.org) by vger.kernel.org id S1752214AbYFRKWv (ORCPT ); Wed, 18 Jun 2008 06:22:51 -0400 Received: from fmmailgate02.web.de ([217.72.192.227]:40736 "EHLO fmmailgate02.web.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752027AbYFRKWs (ORCPT ); Wed, 18 Jun 2008 06:22:48 -0400 Received: from smtp08.web.de (fmsmtp08.dlan.cinetic.de [172.20.5.216]) by fmmailgate02.web.de (Postfix) with ESMTP id 0CF38E216471 for ; Wed, 18 Jun 2008 12:22:46 +0200 (CEST) Received: from [84.150.114.112] (helo=localhost.localdomain) by smtp08.web.de with asmtp (WEB.DE 4.109 #226) id 1K8uoO-0004yx-00; Wed, 18 Jun 2008 12:22:44 +0200 X-Mailer: git-send-email 1.5.5.1 X-Sender: florianskarten@web.de X-Provags-ID: V01U2FsdGVkX18GUSGqFZh68n27Zvd+hdePudVYQBHQUyCWfizE 5mcI07JHSZX8VfRyNcwSa5GRhiSCqiWXLQtMJIHAZ145jBOCzK k7pcaNPP+XZRRvNY2DQw== Sender: git-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: git@vger.kernel.org Archived-At: Signed-off-by: Florian Koeberle --- .../spearce/jgit/fnmatch/FileNameMatcherTest.java | 726 ++++++++++++= ++++++++ .../jgit/errors/InvalidPatternException.java | 65 ++ .../jgit/errors/NoClosingBracketException.java | 69 ++ .../src/org/spearce/jgit/fnmatch/AbstractHead.java | 74 ++ .../org/spearce/jgit/fnmatch/CharacterHead.java | 53 ++ .../org/spearce/jgit/fnmatch/FileNameMatcher.java | 351 ++++++++++ .../src/org/spearce/jgit/fnmatch/GroupHead.java | 220 ++++++ .../src/org/spearce/jgit/fnmatch/Head.java | 50 ++ .../src/org/spearce/jgit/fnmatch/LastHead.java | 56 ++ .../jgit/fnmatch/RestrictedWildCardHead.java | 52 ++ .../src/org/spearce/jgit/fnmatch/WildCardHead.java | 49 ++ 11 files changed, 1765 insertions(+), 0 deletions(-) create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/= =46ileNameMatcherTest.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/Invali= dPatternException.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/NoClos= ingBracketException.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/Abstr= actHead.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/Chara= cterHead.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileN= ameMatcher.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/Group= Head.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.= java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastH= ead.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/Restr= ictedWildCardHead.java create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildC= ardHead.java diff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNam= eMatcherTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/= =46ileNameMatcherTest.java new file mode 100644 index 0000000..f8634dc --- /dev/null +++ b/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatche= rTest.java @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +import org.spearce.jgit.errors.InvalidPatternException; +import org.spearce.jgit.fnmatch.FileNameMatcher; + +import junit.framework.TestCase; + +public class FileNameMatcherTest extends TestCase { + + private void assertMatch(final String pattern, final String input, + final boolean matchExpected, final boolean appendCanMatchExpected) + throws InvalidPatternException { + final FileNameMatcher matcher =3D new FileNameMatcher(pattern, null)= ; + matcher.append(input); + assertEquals(matchExpected, matcher.isMatch()); + assertEquals(appendCanMatchExpected, matcher.canAppendMatch()); + } + + private void assertFileNameMatch(final String pattern, final String i= nput, + final char excludedCharacter, final boolean matchExpected, + final boolean appendCanMatchExpected) + throws InvalidPatternException { + final FileNameMatcher matcher =3D new FileNameMatcher(pattern, + new Character(excludedCharacter)); + matcher.append(input); + assertEquals(matchExpected, matcher.isMatch()); + assertEquals(appendCanMatchExpected, matcher.canAppendMatch()); + } + + public void testVerySimplePatternCase0() throws Exception { + assertMatch("", "", true, false); + } + + public void testVerySimplePatternCase1() throws Exception { + assertMatch("ab", "a", false, true); + } + + public void testVerySimplePatternCase2() throws Exception { + assertMatch("ab", "ab", true, false); + } + + public void testVerySimplePatternCase3() throws Exception { + assertMatch("ab", "ac", false, false); + } + + public void testVerySimplePatternCase4() throws Exception { + assertMatch("ab", "abc", false, false); + } + + public void testVerySimpleWirdcardCase0() throws Exception { + assertMatch("?", "a", true, false); + } + + public void testVerySimpleWildCardCase1() throws Exception { + assertMatch("??", "a", false, true); + } + + public void testVerySimpleWildCardCase2() throws Exception { + assertMatch("??", "ab", true, false); + } + + public void testVerySimpleWildCardCase3() throws Exception { + assertMatch("??", "abc", false, false); + } + + public void testVerySimpleStarCase0() throws Exception { + assertMatch("*", "", true, true); + } + + public void testVerySimpleStarCase1() throws Exception { + assertMatch("*", "a", true, true); + } + + public void testVerySimpleStarCase2() throws Exception { + assertMatch("*", "ab", true, true); + } + + public void testSimpleStarCase0() throws Exception { + assertMatch("a*b", "a", false, true); + } + + public void testSimpleStarCase1() throws Exception { + assertMatch("a*c", "ac", true, true); + } + + public void testSimpleStarCase2() throws Exception { + assertMatch("a*c", "ab", false, true); + } + + public void testSimpleStarCase3() throws Exception { + assertMatch("a*c", "abc", true, true); + } + + public void testManySolutionsCase0() throws Exception { + assertMatch("a*a*a", "aaa", true, true); + } + + public void testManySolutionsCase1() throws Exception { + assertMatch("a*a*a", "aaaa", true, true); + } + + public void testManySolutionsCase2() throws Exception { + assertMatch("a*a*a", "ababa", true, true); + } + + public void testManySolutionsCase3() throws Exception { + assertMatch("a*a*a", "aaaaaaaa", true, true); + } + + public void testManySolutionsCase4() throws Exception { + assertMatch("a*a*a", "aaaaaaab", false, true); + } + + public void testVerySimpleGroupCase0() throws Exception { + assertMatch("[ab]", "a", true, false); + } + + public void testVerySimpleGroupCase1() throws Exception { + assertMatch("[ab]", "b", true, false); + } + + public void testVerySimpleGroupCase2() throws Exception { + assertMatch("[ab]", "ab", false, false); + } + + public void testVerySimpleGroupRangeCase0() throws Exception { + assertMatch("[b-d]", "a", false, false); + } + + public void testVerySimpleGroupRangeCase1() throws Exception { + assertMatch("[b-d]", "b", true, false); + } + + public void testVerySimpleGroupRangeCase2() throws Exception { + assertMatch("[b-d]", "c", true, false); + } + + public void testVerySimpleGroupRangeCase3() throws Exception { + assertMatch("[b-d]", "d", true, false); + } + + public void testVerySimpleGroupRangeCase4() throws Exception { + assertMatch("[b-d]", "e", false, false); + } + + public void testVerySimpleGroupRangeCase5() throws Exception { + assertMatch("[b-d]", "-", false, false); + } + + public void testTwoGroupsCase0() throws Exception { + assertMatch("[b-d][ab]", "bb", true, false); + } + + public void testTwoGroupsCase1() throws Exception { + assertMatch("[b-d][ab]", "ca", true, false); + } + + public void testTwoGroupsCase2() throws Exception { + assertMatch("[b-d][ab]", "fa", false, false); + } + + public void testTwoGroupsCase3() throws Exception { + assertMatch("[b-d][ab]", "bc", false, false); + } + + public void testTwoRangesInOneGroupCase0() throws Exception { + assertMatch("[b-ce-e]", "a", false, false); + } + + public void testTwoRangesInOneGroupCase1() throws Exception { + assertMatch("[b-ce-e]", "b", true, false); + } + + public void testTwoRangesInOneGroupCase2() throws Exception { + assertMatch("[b-ce-e]", "c", true, false); + } + + public void testTwoRangesInOneGroupCase3() throws Exception { + assertMatch("[b-ce-e]", "d", false, false); + } + + public void testTwoRangesInOneGroupCase4() throws Exception { + assertMatch("[b-ce-e]", "e", true, false); + } + + public void testTwoRangesInOneGroupCase5() throws Exception { + assertMatch("[b-ce-e]", "f", false, false); + } + + public void testIncompleteRangesInOneGroupCase0() throws Exception { + assertMatch("a[b-]", "ab", true, false); + } + + public void testIncompleteRangesInOneGroupCase1() throws Exception { + assertMatch("a[b-]", "ac", false, false); + } + + public void testIncompleteRangesInOneGroupCase2() throws Exception { + assertMatch("a[b-]", "a-", true, false); + } + + public void testCombinedRangesInOneGroupCase0() throws Exception { + assertMatch("[a-c-e]", "b", true, false); + } + + /** + * The c belongs to the range a-c. "-e" is no valid range so d should= not + * match. + *=20 + * @throws Exception + * for some reasons + */ + public void testCombinedRangesInOneGroupCase1() throws Exception { + assertMatch("[a-c-e]", "d", false, false); + } + + public void testCombinedRangesInOneGroupCase2() throws Exception { + assertMatch("[a-c-e]", "e", true, false); + } + + public void testInversedGroupCase0() throws Exception { + assertMatch("[!b-c]", "a", true, false); + } + + public void testInversedGroupCase1() throws Exception { + assertMatch("[!b-c]", "b", false, false); + } + + public void testInversedGroupCase2() throws Exception { + assertMatch("[!b-c]", "c", false, false); + } + + public void testInversedGroupCase3() throws Exception { + assertMatch("[!b-c]", "d", true, false); + } + + public void testAlphaGroupCase0() throws Exception { + assertMatch("[[:alpha:]]", "d", true, false); + } + + public void testAlphaGroupCase1() throws Exception { + assertMatch("[[:alpha:]]", ":", false, false); + } + + public void testAlphaGroupCase2() throws Exception { + // \u00f6 =3D 'o' with dots on it + assertMatch("[[:alpha:]]", "\u00f6", true, false); + } + + public void test2AlphaGroupsCase0() throws Exception { + // \u00f6 =3D 'o' with dots on it + assertMatch("[[:alpha:]][[:alpha:]]", "a\u00f6", true, false); + assertMatch("[[:alpha:]][[:alpha:]]", "a1", false, false); + } + + public void testAlnumGroupCase0() throws Exception { + assertMatch("[[:alnum:]]", "a", true, false); + } + + public void testAlnumGroupCase1() throws Exception { + assertMatch("[[:alnum:]]", "1", true, false); + } + + public void testAlnumGroupCase2() throws Exception { + assertMatch("[[:alnum:]]", ":", false, false); + } + + public void testBlankGroupCase0() throws Exception { + assertMatch("[[:blank:]]", " ", true, false); + } + + public void testBlankGroupCase1() throws Exception { + assertMatch("[[:blank:]]", "\t", true, false); + } + + public void testBlankGroupCase2() throws Exception { + assertMatch("[[:blank:]]", "\r", false, false); + } + + public void testBlankGroupCase3() throws Exception { + assertMatch("[[:blank:]]", "\n", false, false); + } + + public void testBlankGroupCase4() throws Exception { + assertMatch("[[:blank:]]", "a", false, false); + } + + public void testCntrlGroupCase0() throws Exception { + assertMatch("[[:cntrl:]]", "a", false, false); + } + + public void testCntrlGroupCase1() throws Exception { + assertMatch("[[:cntrl:]]", String.valueOf((char) 7), true, false); + } + + public void testDigitGroupCase0() throws Exception { + assertMatch("[[:digit:]]", "0", true, false); + } + + public void testDigitGroupCase1() throws Exception { + assertMatch("[[:digit:]]", "5", true, false); + } + + public void testDigitGroupCase2() throws Exception { + assertMatch("[[:digit:]]", "9", true, false); + } + + public void testDigitGroupCase3() throws Exception { + // \u06f9 =3D EXTENDED ARABIC-INDIC DIGIT NINE + assertMatch("[[:digit:]]", "\u06f9", true, false); + } + + public void testDigitGroupCase4() throws Exception { + assertMatch("[[:digit:]]", "a", false, false); + } + + public void testDigitGroupCase5() throws Exception { + assertMatch("[[:digit:]]", "]", false, false); + } + + public void testGraphGroupCase0() throws Exception { + assertMatch("[[:graph:]]", "]", true, false); + } + + public void testGraphGroupCase1() throws Exception { + assertMatch("[[:graph:]]", "a", true, false); + } + + public void testGraphGroupCase2() throws Exception { + assertMatch("[[:graph:]]", ".", true, false); + } + + public void testGraphGroupCase3() throws Exception { + assertMatch("[[:graph:]]", "0", true, false); + } + + public void testGraphGroupCase4() throws Exception { + assertMatch("[[:graph:]]", " ", false, false); + } + + public void testGraphGroupCase5() throws Exception { + // \u00f6 =3D 'o' with dots on it + assertMatch("[[:graph:]]", "\u00f6", true, false); + } + + public void testLowerGroupCase0() throws Exception { + assertMatch("[[:lower:]]", "a", true, false); + } + + public void testLowerGroupCase1() throws Exception { + assertMatch("[[:lower:]]", "h", true, false); + } + + public void testLowerGroupCase2() throws Exception { + assertMatch("[[:lower:]]", "A", false, false); + } + + public void testLowerGroupCase3() throws Exception { + assertMatch("[[:lower:]]", "H", false, false); + } + + public void testLowerGroupCase4() throws Exception { + // \u00e4 =3D small 'a' with dots on it + assertMatch("[[:lower:]]", "\u00e4", true, false); + } + + public void testLowerGroupCase5() throws Exception { + assertMatch("[[:lower:]]", ".", false, false); + } + + public void testPrintGroupCase0() throws Exception { + assertMatch("[[:print:]]", "]", true, false); + } + + public void testPrintGroupCase1() throws Exception { + assertMatch("[[:print:]]", "a", true, false); + } + + public void testPrintGroupCase2() throws Exception { + assertMatch("[[:print:]]", ".", true, false); + } + + public void testPrintGroupCase3() throws Exception { + assertMatch("[[:print:]]", "0", true, false); + } + + public void testPrintGroupCase4() throws Exception { + assertMatch("[[:print:]]", " ", true, false); + } + + public void testPrintGroupCase5() throws Exception { + // \u00f6 =3D 'o' with dots on it + assertMatch("[[:print:]]", "\u00f6", true, false); + } + + public void testPunctGroupCase0() throws Exception { + assertMatch("[[:punct:]]", ".", true, false); + } + + public void testPunctGroupCase1() throws Exception { + assertMatch("[[:punct:]]", "@", true, false); + } + + public void testPunctGroupCase2() throws Exception { + assertMatch("[[:punct:]]", " ", false, false); + } + + public void testPunctGroupCase3() throws Exception { + assertMatch("[[:punct:]]", "a", false, false); + } + + public void testSpaceGroupCase0() throws Exception { + assertMatch("[[:space:]]", " ", true, false); + } + + public void testSpaceGroupCase1() throws Exception { + assertMatch("[[:space:]]", "\t", true, false); + } + + public void testSpaceGroupCase2() throws Exception { + assertMatch("[[:space:]]", "\r", true, false); + } + + public void testSpaceGroupCase3() throws Exception { + assertMatch("[[:space:]]", "\n", true, false); + } + + public void testSpaceGroupCase4() throws Exception { + assertMatch("[[:space:]]", "a", false, false); + } + + public void testUpperGroupCase0() throws Exception { + assertMatch("[[:upper:]]", "a", false, false); + } + + public void testUpperGroupCase1() throws Exception { + assertMatch("[[:upper:]]", "h", false, false); + } + + public void testUpperGroupCase2() throws Exception { + assertMatch("[[:upper:]]", "A", true, false); + } + + public void testUpperGroupCase3() throws Exception { + assertMatch("[[:upper:]]", "H", true, false); + } + + public void testUpperGroupCase4() throws Exception { + // \u00c4 =3D 'A' with dots on it + assertMatch("[[:upper:]]", "\u00c4", true, false); + } + + public void testUpperGroupCase5() throws Exception { + assertMatch("[[:upper:]]", ".", false, false); + } + + public void testXDigitGroupCase0() throws Exception { + assertMatch("[[:xdigit:]]", "a", true, false); + } + + public void testXDigitGroupCase1() throws Exception { + assertMatch("[[:xdigit:]]", "d", true, false); + } + + public void testXDigitGroupCase2() throws Exception { + assertMatch("[[:xdigit:]]", "f", true, false); + } + + public void testXDigitGroupCase3() throws Exception { + assertMatch("[[:xdigit:]]", "0", true, false); + } + + public void testXDigitGroupCase4() throws Exception { + assertMatch("[[:xdigit:]]", "5", true, false); + } + + public void testXDigitGroupCase5() throws Exception { + assertMatch("[[:xdigit:]]", "9", true, false); + } + + public void testXDigitGroupCase6() throws Exception { + assertMatch("[[:xdigit:]]", "=DB=B9", false, false); + } + + public void testXDigitGroupCase7() throws Exception { + assertMatch("[[:xdigit:]]", ".", false, false); + } + + public void testWordroupCase0() throws Exception { + assertMatch("[[:word:]]", "g", true, false); + } + + public void testWordroupCase1() throws Exception { + // \u00f6 =3D 'o' with dots on it + assertMatch("[[:word:]]", "\u00f6", true, false); + } + + public void testWordroupCase2() throws Exception { + assertMatch("[[:word:]]", "5", true, false); + } + + public void testWordroupCase3() throws Exception { + assertMatch("[[:word:]]", "_", true, false); + } + + public void testWordroupCase4() throws Exception { + assertMatch("[[:word:]]", " ", false, false); + } + + public void testWordroupCase5() throws Exception { + assertMatch("[[:word:]]", ".", false, false); + } + + public void testMixedGroupCase0() throws Exception { + assertMatch("[A[:lower:]C3-5]", "A", true, false); + } + + public void testMixedGroupCase1() throws Exception { + assertMatch("[A[:lower:]C3-5]", "C", true, false); + } + + public void testMixedGroupCase2() throws Exception { + assertMatch("[A[:lower:]C3-5]", "e", true, false); + } + + public void testMixedGroupCase3() throws Exception { + assertMatch("[A[:lower:]C3-5]", "3", true, false); + } + + public void testMixedGroupCase4() throws Exception { + assertMatch("[A[:lower:]C3-5]", "4", true, false); + } + + public void testMixedGroupCase5() throws Exception { + assertMatch("[A[:lower:]C3-5]", "5", true, false); + } + + public void testMixedGroupCase6() throws Exception { + assertMatch("[A[:lower:]C3-5]", "B", false, false); + } + + public void testMixedGroupCase7() throws Exception { + assertMatch("[A[:lower:]C3-5]", "2", false, false); + } + + public void testMixedGroupCase8() throws Exception { + assertMatch("[A[:lower:]C3-5]", "6", false, false); + } + + public void testMixedGroupCase9() throws Exception { + assertMatch("[A[:lower:]C3-5]", ".", false, false); + } + + public void testSpecialGroupCase0() throws Exception { + assertMatch("[[]", "[", true, false); + } + + public void testSpecialGroupCase1() throws Exception { + assertMatch("[]]", "]", true, false); + } + + public void testSpecialGroupCase2() throws Exception { + assertMatch("[]a]", "]", true, false); + } + + public void testSpecialGroupCase3() throws Exception { + assertMatch("[a[]", "[", true, false); + } + + public void testSpecialGroupCase4() throws Exception { + assertMatch("[a[]", "a", true, false); + } + + public void testSpecialGroupCase5() throws Exception { + assertMatch("[!]]", "]", false, false); + } + + public void testSpecialGroupCase6() throws Exception { + assertMatch("[!]]", "x", true, false); + } + + public void testSpecialGroupCase7() throws Exception { + assertMatch("[:]]", ":]", true, false); + } + + public void testSpecialGroupCase8() throws Exception { + assertMatch("[:]]", ":", false, true); + } + + public void testSpecialGroupCase9() throws Exception { + try { + assertMatch("[[:]", ":", true, true); + fail("InvalidPatternException expected"); + } catch (InvalidPatternException e) { + // expected + } + } + + public void testUnsupportedGroupCase0() throws Exception { + try { + assertMatch("[[=3Da=3D]]", "b", false, false); + fail("InvalidPatternException expected"); + } catch (InvalidPatternException e) { + assertTrue(e.getMessage().contains("[=3Da=3D]")); + } + } + + public void testUnsupportedGroupCase1() throws Exception { + try { + assertMatch("[[.a.]]", "b", false, false); + fail("InvalidPatternException expected"); + } catch (InvalidPatternException e) { + assertTrue(e.getMessage().contains("[.a.]")); + } + } + + public void testFilePathSimpleCase() throws Exception { + assertFileNameMatch("a/b", "a/b", '/', true, false); + } + + public void testFilePathCase0() throws Exception { + assertFileNameMatch("a*b", "a/b", '/', false, false); + } + + public void testFilePathCase1() throws Exception { + assertFileNameMatch("a?b", "a/b", '/', false, false); + } + + public void testFilePathCase2() throws Exception { + assertFileNameMatch("a*b", "a\\b", '\\', false, false); + } + + public void testFilePathCase3() throws Exception { + assertFileNameMatch("a?b", "a\\b", '\\', false, false); + } + + public void testReset() throws Exception { + final String pattern =3D "helloworld"; + final FileNameMatcher matcher =3D new FileNameMatcher(pattern, null)= ; + matcher.append("helloworld"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + matcher.reset(); + matcher.append("hello"); + assertEquals(false, matcher.isMatch()); + assertEquals(true, matcher.canAppendMatch()); + matcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + matcher.append("to much"); + assertEquals(false, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + matcher.reset(); + matcher.append("helloworld"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + } + + public void testCreateMatcherForSuffix() throws Exception { + final String pattern =3D "helloworld"; + final FileNameMatcher matcher =3D new FileNameMatcher(pattern, null)= ; + matcher.append("hello"); + final FileNameMatcher childMatcher =3D matcher.createMatcherForSuffi= x(); + assertEquals(false, matcher.isMatch()); + assertEquals(true, matcher.canAppendMatch()); + assertEquals(false, childMatcher.isMatch()); + assertEquals(true, childMatcher.canAppendMatch()); + matcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(false, childMatcher.isMatch()); + assertEquals(true, childMatcher.canAppendMatch()); + childMatcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(true, childMatcher.isMatch()); + assertEquals(false, childMatcher.canAppendMatch()); + childMatcher.reset(); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(false, childMatcher.isMatch()); + assertEquals(true, childMatcher.canAppendMatch()); + childMatcher.append("world"); + assertEquals(true, matcher.isMatch()); + assertEquals(false, matcher.canAppendMatch()); + assertEquals(true, childMatcher.isMatch()); + assertEquals(false, childMatcher.canAppendMatch()); + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatter= nException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidP= atternException.java new file mode 100644 index 0000000..02f67fe --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternExcept= ion.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.errors; + +/** + * Thrown when a pattern passed in an argument was wrong. + *=20 + */ +public class InvalidPatternException extends Exception { + private final String pattern; + + /** + * @param message + * explains what was wrong with the pattern. + * @param pattern + * the invalid pattern. + */ + public InvalidPatternException(String message, String pattern) { + super(message); + this.pattern =3D pattern; + } + + /** + * @return the invalid pattern. + */ + public String getPattern() { + return pattern; + } + +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBrac= ketException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/NoClos= ingBracketException.java new file mode 100644 index 0000000..1a93906 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketExce= ption.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.errors; + +/** + * Thrown when a pattern contains a character group which is open to t= he right + * side or a character class which is open to the right side. + */ +public class NoClosingBracketException extends InvalidPatternException= { + + /** + * @param indexOfOpeningBracket + * the position of the [ character which has no ] characte= r. + * @param openingBracket + * the unclosed bracket. + * @param closingBracket + * the missing closing bracket. + * @param pattern + * the invalid pattern. + */ + public NoClosingBracketException(final int indexOfOpeningBracket, + final String openingBracket, final String closingBracket, + final String pattern) { + super(createMessage(indexOfOpeningBracket, openingBracket, + closingBracket), pattern); + } + + private static String createMessage(final int indexOfOpeningBracket, + final String openingBracket, final String closingBracket) { + return String.format("No closing %s found for %s at index %s.", + closingBracket, openingBracket, new Integer( + indexOfOpeningBracket)); + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead= =2Ejava b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.ja= va new file mode 100644 index 0000000..1e9a0ca --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +import java.util.List; + +abstract class AbstractHead implements Head { + private List newHeads =3D null; + + private final boolean star; + + protected abstract boolean matches(char c); + + AbstractHead(boolean star) { + this.star =3D star; + } + + /** + *=20 + * @param newHeads + * a list of {@link Head}s which will not be modified. + */ + public final void setNewHeads(List newHeads) { + if (this.newHeads !=3D null) + throw new IllegalStateException("Property is already non null"); + this.newHeads =3D newHeads; + } + + public List getNextHeads(char c) { + if (matches(c)) + return newHeads; + else + return FileNameMatcher.EMPTY_HEAD_LIST; + } + + boolean isStar() { + return star; + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHea= d.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.ja= va new file mode 100644 index 0000000..01c3403 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +final class CharacterHead extends AbstractHead { + private final char expectedCharacter; + + protected CharacterHead(final char expectedCharacter) { + super(false); + this.expectedCharacter =3D expectedCharacter; + } + + @Override + protected final boolean matches(final char c) { + return c =3D=3D expectedCharacter; + } + +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatc= her.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatche= r.java new file mode 100644 index 0000000..30a5930 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.jav= a @@ -0,0 +1,351 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.spearce.jgit.errors.InvalidPatternException; +import org.spearce.jgit.errors.NoClosingBracketException; + +/** + * This class can be used to match filenames against fnmatch like patt= erns. It + * is not thread save. + *

+ * Supported are the wildcard characters * and ? and groups with: + *

    + *
  • characters e.g. [abc]
  • + *
  • ranges e.g. [a-z]
  • + *
  • the following character classes + *
      + *
    • [:alnum:]
    • + *
    • [:alpha:]
    • + *
    • [:blank:]
    • + *
    • [:cntrl:]
    • + *
    • [:digit:]
    • + *
    • [:graph:]
    • + *
    • [:lower:]
    • + *
    • [:print:]
    • + *
    • [:punct:]
    • + *
    • [:space:]
    • + *
    • [:upper:]
    • + *
    • [:word:]
    • + *
    • [:xdigit:]
    • + *
    + * e. g. [[:xdigit:]]
  • + *
+ *

+ */ +public class FileNameMatcher { + static final List EMPTY_HEAD_LIST =3D Collections.emptyList(); + + private static final Pattern characterClassStartPattern =3D Pattern + .compile("\\[[.:=3D]"); + + private List headsStartValue; + + private List heads; + + /** + * {{@link #extendStringToMatchByOneCharacter(char)} needs a list for= the + * new heads, allocating a new array would be bad for the performance= , as + * the method gets called very often. + *=20 + */ + private List listForLocalUseage; + + /** + *=20 + * @param headsStartValue + * must be a list which will never be modified. + */ + private FileNameMatcher(final List headsStartValue) { + this.headsStartValue =3D headsStartValue; + this.heads =3D new ArrayList(headsStartValue.size()); + this.heads.addAll(this.headsStartValue); + this.listForLocalUseage =3D new ArrayList(headsStartValue.size= ()); + } + + /** + * @param patternString + * must contain a pattern which fnmatch would accept. + * @param invalidWildgetCharacter + * if this parameter isn't null then this character will n= ot + * match at wildcards(* and ? are wildcards). + * @throws InvalidPatternException + * if the patternString contains a invalid fnmatch patter= n. + */ + public FileNameMatcher(final String patternString, + final Character invalidWildgetCharacter) + throws InvalidPatternException { + this(createHeadsStartValues(patternString, invalidWildgetCharacter))= ; + } + + private static List createHeadsStartValues( + final String patternString, final Character invalidWildgetCharacter= ) + throws InvalidPatternException { + + final List allHeads =3D parseHeads(patternString, + invalidWildgetCharacter); + + List nextHeadsSuggestion =3D new ArrayList(2); + nextHeadsSuggestion.add(LastHead.INSTANCE); + for (int i =3D allHeads.size() - 1; i >=3D 0; i--) { + final AbstractHead head =3D allHeads.get(i); + + // explanation: + // a and * of the pattern "a*b" + // need *b as newHeads + // that's why * extends the list for it self and it's left neighbor= =2E + if (head.isStar()) { + nextHeadsSuggestion.add(head); + head.setNewHeads(nextHeadsSuggestion); + } else { + head.setNewHeads(nextHeadsSuggestion); + nextHeadsSuggestion =3D new ArrayList(2); + nextHeadsSuggestion.add(head); + } + } + return nextHeadsSuggestion; + } + + private static int findGroupEnd(final int indexOfStartBracket, + final String pattern) throws InvalidPatternException { + int firstValidCharClassIndex =3D indexOfStartBracket + 1; + int firstValidEndBracketIndex =3D indexOfStartBracket + 2; + + if (indexOfStartBracket + 1 >=3D pattern.length()) + throw new NoClosingBracketException(indexOfStartBracket, "[", "]", + pattern); + + if (pattern.charAt(firstValidCharClassIndex) =3D=3D '!') { + firstValidCharClassIndex++; + firstValidEndBracketIndex++; + } + + final Matcher charClassStartMatcher =3D characterClassStartPattern + .matcher(pattern); + + int groupEnd =3D -1; + while (groupEnd =3D=3D -1) { + + final int possibleGroupEnd =3D pattern.indexOf(']', + firstValidEndBracketIndex); + if (possibleGroupEnd =3D=3D -1) + throw new NoClosingBracketException(indexOfStartBracket, "[", + "]", pattern); + + final boolean foundCharClass =3D charClassStartMatcher + .find(firstValidCharClassIndex); + + if (foundCharClass + && charClassStartMatcher.start() < possibleGroupEnd) { + + final String classStart =3D charClassStartMatcher.group(0); + final String classEnd =3D classStart.charAt(1) + "]"; + + final int classStartIndex =3D charClassStartMatcher.start(); + final int classEndIndex =3D pattern.indexOf(classEnd, + classStartIndex + 2); + + if (classEndIndex =3D=3D -1) + throw new NoClosingBracketException(classStartIndex, + classStart, classEnd, pattern); + + firstValidCharClassIndex =3D classEndIndex + 2; + firstValidEndBracketIndex =3D firstValidCharClassIndex; + } else { + groupEnd =3D possibleGroupEnd; + } + } + return groupEnd; + } + + private static List parseHeads(final String pattern, + final Character invalidWildgetCharacter) + throws InvalidPatternException { + + int currentIndex =3D 0; + List heads =3D new ArrayList(); + while (currentIndex < pattern.length()) { + final int groupStart =3D pattern.indexOf('[', currentIndex); + if (groupStart =3D=3D -1) { + final String patternPart =3D pattern.substring(currentIndex); + heads.addAll(createSimpleHeads(patternPart, + invalidWildgetCharacter)); + currentIndex =3D pattern.length(); + } else { + final String patternPart =3D pattern.substring(currentIndex, + groupStart); + heads.addAll(createSimpleHeads(patternPart, + invalidWildgetCharacter)); + + final int groupEnd =3D findGroupEnd(groupStart, pattern); + final String groupPart =3D pattern.substring(groupStart + 1, + groupEnd); + heads.add(new GroupHead(groupPart, pattern)); + currentIndex =3D groupEnd + 1; + } + } + return heads; + } + + private static List createSimpleHeads( + final String patternPart, final Character invalidWildgetCharacter) = { + final List heads =3D new ArrayList( + patternPart.length()); + for (int i =3D 0; i < patternPart.length(); i++) { + final char c =3D patternPart.charAt(i); + switch (c) { + case '*': { + final AbstractHead head =3D createWildCardHead( + invalidWildgetCharacter, true); + heads.add(head); + break; + } + case '?': { + final AbstractHead head =3D createWildCardHead( + invalidWildgetCharacter, false); + heads.add(head); + break; + } + default: + final CharacterHead head =3D new CharacterHead(c); + heads.add(head); + } + } + return heads; + } + + private static AbstractHead createWildCardHead( + final Character invalidWildgetCharacter, final boolean star) { + if (invalidWildgetCharacter !=3D null) + return new RestrictedWildCardHead(invalidWildgetCharacter + .charValue(), star); + else + return new WildCardHead(star); + } + + private void extendStringToMatchByOneCharacter(final char c) { + final List newHeads =3D listForLocalUseage; + newHeads.clear(); + List lastAddedHeads =3D null; + for (int i =3D 0; i < heads.size(); i++) { + final Head head =3D heads.get(i); + final List headsToAdd =3D head.getNextHeads(c); + // Why the next performance optimization isn't wrong: + // Some times two heads return the very same list. + // We save future effort if we don't add these heads again. + // This is the case with the heads "a" and "*" of "a*b" which + // both can return the list ["*","b"] + if (headsToAdd !=3D lastAddedHeads) { + newHeads.addAll(headsToAdd); + lastAddedHeads =3D headsToAdd; + } + } + listForLocalUseage =3D heads; + heads =3D newHeads; + } + + /** + *=20 + * @param stringToMatch + * extends the string which is matched against the pattern= s of + * this class. + */ + public void append(final String stringToMatch) { + for (int i =3D 0; i < stringToMatch.length(); i++) { + final char c =3D stringToMatch.charAt(i); + extendStringToMatchByOneCharacter(c); + } + } + + /** + * Resets this matcher to it's state right after construction. + */ + public void reset() { + heads.clear(); + heads.addAll(headsStartValue); + } + + /** + *=20 + * @return a {@link FileNameMatcher} instance which uses the same pat= tern + * like this matcher, but has the current state of this match= er as + * reset and start point. + */ + public FileNameMatcher createMatcherForSuffix() { + final List copyOfHeads =3D new ArrayList(heads.size()); + copyOfHeads.addAll(heads); + return new FileNameMatcher(copyOfHeads); + } + + /** + *=20 + * @return true, if the string currently being matched does match. + */ + public boolean isMatch() { + final ListIterator headIterator =3D heads + .listIterator(heads.size()); + while (headIterator.hasPrevious()) { + final Head head =3D headIterator.previous(); + if (head =3D=3D LastHead.INSTANCE) { + return true; + } + } + return false; + } + + /** + *=20 + * @return false, if the string being matched will not match when the= string + * gets extended. + */ + public boolean canAppendMatch() { + for (int i =3D 0; i < heads.size(); i++) { + if (heads.get(i) !=3D LastHead.INSTANCE) { + return true; + } + } + return false; + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.ja= va b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java new file mode 100644 index 0000000..9f72010 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.spearce.jgit.errors.InvalidPatternException; + +final class GroupHead extends AbstractHead { + private final List characterClasses; + + private static final Pattern REGEX_PATTERN =3D Pattern + .compile("([^-][-][^-]|\\[[.:=3D].*?[.:=3D]\\])"); + + private final boolean inverse; + + GroupHead(String pattern, final String wholePattern) + throws InvalidPatternException { + super(false); + this.characterClasses =3D new ArrayList(); + this.inverse =3D pattern.startsWith("!"); + if (inverse) { + pattern =3D pattern.substring(1); + } + final Matcher matcher =3D REGEX_PATTERN.matcher(pattern); + while (matcher.find()) { + final String characterClass =3D matcher.group(0); + if (characterClass.length() =3D=3D 3 && characterClass.charAt(1) =3D= =3D '-') { + final char start =3D characterClass.charAt(0); + final char end =3D characterClass.charAt(2); + characterClasses.add(new CharacterRange(start, end)); + } else if (characterClass.equals("[:alnum:]")) { + characterClasses.add(LetterPattern.INSTANCE); + characterClasses.add(DigitPattern.INSTANCE); + } else if (characterClass.equals("[:alpha:]")) { + characterClasses.add(LetterPattern.INSTANCE); + } else if (characterClass.equals("[:blank:]")) { + characterClasses.add(new OneCharacterPattern(' ')); + characterClasses.add(new OneCharacterPattern('\t')); + } else if (characterClass.equals("[:cntrl:]")) { + characterClasses.add(new CharacterRange('\u0000', '\u001F')); + characterClasses.add(new OneCharacterPattern('\u007F')); + } else if (characterClass.equals("[:digit:]")) { + characterClasses.add(DigitPattern.INSTANCE); + } else if (characterClass.equals("[:graph:]")) { + characterClasses.add(new CharacterRange('\u0021', '\u007E')); + characterClasses.add(LetterPattern.INSTANCE); + characterClasses.add(DigitPattern.INSTANCE); + } else if (characterClass.equals("[:lower:]")) { + characterClasses.add(LowerPattern.INSTANCE); + } else if (characterClass.equals("[:print:]")) { + characterClasses.add(new CharacterRange('\u0020', '\u007E')); + characterClasses.add(LetterPattern.INSTANCE); + characterClasses.add(DigitPattern.INSTANCE); + } else if (characterClass.equals("[:punct:]")) { + characterClasses.add(PunctPattern.INSTANCE); + } else if (characterClass.equals("[:space:]")) { + characterClasses.add(WhitespacePattern.INSTANCE); + } else if (characterClass.equals("[:upper:]")) { + characterClasses.add(UpperPattern.INSTANCE); + } else if (characterClass.equals("[:xdigit:]")) { + characterClasses.add(new CharacterRange('0', '9')); + characterClasses.add(new CharacterRange('a', 'f')); + characterClasses.add(new CharacterRange('A', 'F')); + } else if (characterClass.equals("[:word:]")) { + characterClasses.add(new OneCharacterPattern('_')); + characterClasses.add(LetterPattern.INSTANCE); + characterClasses.add(DigitPattern.INSTANCE); + } else { + final String message =3D String.format( + "The character class %s is not supported.", + characterClass); + throw new InvalidPatternException(message, wholePattern); + } + + pattern =3D matcher.replaceFirst(""); + matcher.reset(pattern); + } + // pattern contains now no ranges + for (int i =3D 0; i < pattern.length(); i++) { + final char c =3D pattern.charAt(i); + characterClasses.add(new OneCharacterPattern(c)); + } + } + + @Override + protected final boolean matches(final char c) { + for (CharacterPattern pattern : characterClasses) { + if (pattern.matches(c)) { + return !inverse; + } + } + return inverse; + } + + private interface CharacterPattern { + /** + * @param c + * the character to test + * @return returns true if the character matches a pattern. + */ + boolean matches(char c); + } + + private static final class CharacterRange implements CharacterPattern= { + private final char start; + + private final char end; + + CharacterRange(char start, char end) { + this.start =3D start; + this.end =3D end; + } + + public final boolean matches(char c) { + return start <=3D c && c <=3D end; + } + } + + private static final class DigitPattern implements CharacterPattern { + static final GroupHead.DigitPattern INSTANCE =3D new DigitPattern(); + + public final boolean matches(char c) { + return Character.isDigit(c); + } + } + + private static final class LetterPattern implements CharacterPattern = { + static final GroupHead.LetterPattern INSTANCE =3D new LetterPattern(= ); + + public final boolean matches(char c) { + return Character.isLetter(c); + } + } + + private static final class LowerPattern implements CharacterPattern { + static final GroupHead.LowerPattern INSTANCE =3D new LowerPattern(); + + public final boolean matches(char c) { + return Character.isLowerCase(c); + } + } + + private static final class UpperPattern implements CharacterPattern { + static final GroupHead.UpperPattern INSTANCE =3D new UpperPattern(); + + public final boolean matches(char c) { + return Character.isUpperCase(c); + } + } + + private static final class WhitespacePattern implements CharacterPatt= ern { + static final GroupHead.WhitespacePattern INSTANCE =3D new Whitespace= Pattern(); + + public final boolean matches(char c) { + return Character.isWhitespace(c); + } + } + + private static final class OneCharacterPattern implements CharacterPa= ttern { + private char expectedCharacter; + + OneCharacterPattern(final char c) { + this.expectedCharacter =3D c; + } + + public final boolean matches(char c) { + return this.expectedCharacter =3D=3D c; + } + } + + private static final class PunctPattern implements CharacterPattern { + static final GroupHead.PunctPattern INSTANCE =3D new PunctPattern(); + + private static String punctCharacters =3D "-!\"#$%&'()*+,./:;<=3D>?@= [\\]_`{|}~"; + + public boolean matches(char c) { + return punctCharacters.indexOf(c) !=3D -1; + } + } + +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java b/= org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java new file mode 100644 index 0000000..498f96c --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +import java.util.List; + +interface Head { + /** + *=20 + * @param c + * the character which decides which heads are returned. + * @return a list of heads based on the input. + */ + public abstract List getNextHeads(char c); +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.jav= a b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java new file mode 100644 index 0000000..d3c9813 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +import java.util.List; + +final class LastHead implements Head { + static final Head INSTANCE =3D new LastHead(); + + /** + * Don't call this constructor, use {@link #INSTANCE} + */ + private LastHead() { + // defined because of javadoc and visibility modifier. + } + + public List getNextHeads(char c) { + return FileNameMatcher.EMPTY_HEAD_LIST; + } + +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWi= ldCardHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Restric= tedWildCardHead.java new file mode 100644 index 0000000..9d8d277 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardH= ead.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +final class RestrictedWildCardHead extends AbstractHead { + private final char excludedCharacter; + + RestrictedWildCardHead(final char excludedCharacter, final boolean st= ar) { + super(star); + this.excludedCharacter =3D excludedCharacter; + } + + @Override + protected final boolean matches(final char c) { + return c !=3D excludedCharacter; + } +} diff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead= =2Ejava b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.ja= va new file mode 100644 index 0000000..570e374 --- /dev/null +++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2008, Florian K=C3=B6berle + * + * 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 Git Development Community 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.spearce.jgit.fnmatch; + +final class WildCardHead extends AbstractHead { + WildCardHead(boolean star) { + super(star); + } + + @Override + protected final boolean matches(final char c) { + return true; + } +} --=20 1.5.4.3