{"thread":{"id":"14012","subject":"[JGIT Patch] Added the package fnmatch and two exceptions.","startedAt":"2008-06-18T10:22:30Z","lastAt":"2008-06-18T10:22:30Z","messageCount":1,"participants":["Florian Koeberle"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"80212","messageId":"1213784550-11726-1-git-send-email-florianskarten@web.de","threadId":"14012","inReplyTo":null,"subject":"[JGIT Patch] Added the package fnmatch and two exceptions.","fromName":"Florian Koeberle","fromEmail":"florianskarten@web.de","sentAt":"2008-06-18T10:22:30Z","receivedAt":"2008-06-18T10:22:30Z","isPatch":true,"sender":{"key":"florianskarten@web.de","avatar":null},"body":"Signed-off-by: Florian Koeberle <florianskarten@web.de>\n---\n .../spearce/jgit/fnmatch/FileNameMatcherTest.java  |  726 ++++++++++++++++++++\n .../jgit/errors/InvalidPatternException.java       |   65 ++\n .../jgit/errors/NoClosingBracketException.java     |   69 ++\n .../src/org/spearce/jgit/fnmatch/AbstractHead.java |   74 ++\n .../org/spearce/jgit/fnmatch/CharacterHead.java    |   53 ++\n .../org/spearce/jgit/fnmatch/FileNameMatcher.java  |  351 ++++++++++\n .../src/org/spearce/jgit/fnmatch/GroupHead.java    |  220 ++++++\n .../src/org/spearce/jgit/fnmatch/Head.java         |   50 ++\n .../src/org/spearce/jgit/fnmatch/LastHead.java     |   56 ++\n .../jgit/fnmatch/RestrictedWildCardHead.java       |   52 ++\n .../src/org/spearce/jgit/fnmatch/WildCardHead.java |   49 ++\n 11 files changed, 1765 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java\nnew file mode 100644\nindex 0000000..f8634dc\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/fnmatch/FileNameMatcherTest.java\n@@ -0,0 +1,726 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+import org.spearce.jgit.errors.InvalidPatternException;\n+import org.spearce.jgit.fnmatch.FileNameMatcher;\n+\n+import junit.framework.TestCase;\n+\n+public class FileNameMatcherTest extends TestCase {\n+\n+\tprivate void assertMatch(final String pattern, final String input,\n+\t\t\tfinal boolean matchExpected, final boolean appendCanMatchExpected)\n+\t\t\tthrows InvalidPatternException {\n+\t\tfinal FileNameMatcher matcher = new FileNameMatcher(pattern, null);\n+\t\tmatcher.append(input);\n+\t\tassertEquals(matchExpected, matcher.isMatch());\n+\t\tassertEquals(appendCanMatchExpected, matcher.canAppendMatch());\n+\t}\n+\n+\tprivate void assertFileNameMatch(final String pattern, final String input,\n+\t\t\tfinal char excludedCharacter, final boolean matchExpected,\n+\t\t\tfinal boolean appendCanMatchExpected)\n+\t\t\tthrows InvalidPatternException {\n+\t\tfinal FileNameMatcher matcher = new FileNameMatcher(pattern,\n+\t\t\t\tnew Character(excludedCharacter));\n+\t\tmatcher.append(input);\n+\t\tassertEquals(matchExpected, matcher.isMatch());\n+\t\tassertEquals(appendCanMatchExpected, matcher.canAppendMatch());\n+\t}\n+\n+\tpublic void testVerySimplePatternCase0() throws Exception {\n+\t\tassertMatch(\"\", \"\", true, false);\n+\t}\n+\n+\tpublic void testVerySimplePatternCase1() throws Exception {\n+\t\tassertMatch(\"ab\", \"a\", false, true);\n+\t}\n+\n+\tpublic void testVerySimplePatternCase2() throws Exception {\n+\t\tassertMatch(\"ab\", \"ab\", true, false);\n+\t}\n+\n+\tpublic void testVerySimplePatternCase3() throws Exception {\n+\t\tassertMatch(\"ab\", \"ac\", false, false);\n+\t}\n+\n+\tpublic void testVerySimplePatternCase4() throws Exception {\n+\t\tassertMatch(\"ab\", \"abc\", false, false);\n+\t}\n+\n+\tpublic void testVerySimpleWirdcardCase0() throws Exception {\n+\t\tassertMatch(\"?\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testVerySimpleWildCardCase1() throws Exception {\n+\t\tassertMatch(\"??\", \"a\", false, true);\n+\t}\n+\n+\tpublic void testVerySimpleWildCardCase2() throws Exception {\n+\t\tassertMatch(\"??\", \"ab\", true, false);\n+\t}\n+\n+\tpublic void testVerySimpleWildCardCase3() throws Exception {\n+\t\tassertMatch(\"??\", \"abc\", false, false);\n+\t}\n+\n+\tpublic void testVerySimpleStarCase0() throws Exception {\n+\t\tassertMatch(\"*\", \"\", true, true);\n+\t}\n+\n+\tpublic void testVerySimpleStarCase1() throws Exception {\n+\t\tassertMatch(\"*\", \"a\", true, true);\n+\t}\n+\n+\tpublic void testVerySimpleStarCase2() throws Exception {\n+\t\tassertMatch(\"*\", \"ab\", true, true);\n+\t}\n+\n+\tpublic void testSimpleStarCase0() throws Exception {\n+\t\tassertMatch(\"a*b\", \"a\", false, true);\n+\t}\n+\n+\tpublic void testSimpleStarCase1() throws Exception {\n+\t\tassertMatch(\"a*c\", \"ac\", true, true);\n+\t}\n+\n+\tpublic void testSimpleStarCase2() throws Exception {\n+\t\tassertMatch(\"a*c\", \"ab\", false, true);\n+\t}\n+\n+\tpublic void testSimpleStarCase3() throws Exception {\n+\t\tassertMatch(\"a*c\", \"abc\", true, true);\n+\t}\n+\n+\tpublic void testManySolutionsCase0() throws Exception {\n+\t\tassertMatch(\"a*a*a\", \"aaa\", true, true);\n+\t}\n+\n+\tpublic void testManySolutionsCase1() throws Exception {\n+\t\tassertMatch(\"a*a*a\", \"aaaa\", true, true);\n+\t}\n+\n+\tpublic void testManySolutionsCase2() throws Exception {\n+\t\tassertMatch(\"a*a*a\", \"ababa\", true, true);\n+\t}\n+\n+\tpublic void testManySolutionsCase3() throws Exception {\n+\t\tassertMatch(\"a*a*a\", \"aaaaaaaa\", true, true);\n+\t}\n+\n+\tpublic void testManySolutionsCase4() throws Exception {\n+\t\tassertMatch(\"a*a*a\", \"aaaaaaab\", false, true);\n+\t}\n+\n+\tpublic void testVerySimpleGroupCase0() throws Exception {\n+\t\tassertMatch(\"[ab]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupCase1() throws Exception {\n+\t\tassertMatch(\"[ab]\", \"b\", true, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupCase2() throws Exception {\n+\t\tassertMatch(\"[ab]\", \"ab\", false, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupRangeCase0() throws Exception {\n+\t\tassertMatch(\"[b-d]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupRangeCase1() throws Exception {\n+\t\tassertMatch(\"[b-d]\", \"b\", true, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupRangeCase2() throws Exception {\n+\t\tassertMatch(\"[b-d]\", \"c\", true, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupRangeCase3() throws Exception {\n+\t\tassertMatch(\"[b-d]\", \"d\", true, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupRangeCase4() throws Exception {\n+\t\tassertMatch(\"[b-d]\", \"e\", false, false);\n+\t}\n+\n+\tpublic void testVerySimpleGroupRangeCase5() throws Exception {\n+\t\tassertMatch(\"[b-d]\", \"-\", false, false);\n+\t}\n+\n+\tpublic void testTwoGroupsCase0() throws Exception {\n+\t\tassertMatch(\"[b-d][ab]\", \"bb\", true, false);\n+\t}\n+\n+\tpublic void testTwoGroupsCase1() throws Exception {\n+\t\tassertMatch(\"[b-d][ab]\", \"ca\", true, false);\n+\t}\n+\n+\tpublic void testTwoGroupsCase2() throws Exception {\n+\t\tassertMatch(\"[b-d][ab]\", \"fa\", false, false);\n+\t}\n+\n+\tpublic void testTwoGroupsCase3() throws Exception {\n+\t\tassertMatch(\"[b-d][ab]\", \"bc\", false, false);\n+\t}\n+\n+\tpublic void testTwoRangesInOneGroupCase0() throws Exception {\n+\t\tassertMatch(\"[b-ce-e]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testTwoRangesInOneGroupCase1() throws Exception {\n+\t\tassertMatch(\"[b-ce-e]\", \"b\", true, false);\n+\t}\n+\n+\tpublic void testTwoRangesInOneGroupCase2() throws Exception {\n+\t\tassertMatch(\"[b-ce-e]\", \"c\", true, false);\n+\t}\n+\n+\tpublic void testTwoRangesInOneGroupCase3() throws Exception {\n+\t\tassertMatch(\"[b-ce-e]\", \"d\", false, false);\n+\t}\n+\n+\tpublic void testTwoRangesInOneGroupCase4() throws Exception {\n+\t\tassertMatch(\"[b-ce-e]\", \"e\", true, false);\n+\t}\n+\n+\tpublic void testTwoRangesInOneGroupCase5() throws Exception {\n+\t\tassertMatch(\"[b-ce-e]\", \"f\", false, false);\n+\t}\n+\n+\tpublic void testIncompleteRangesInOneGroupCase0() throws Exception {\n+\t\tassertMatch(\"a[b-]\", \"ab\", true, false);\n+\t}\n+\n+\tpublic void testIncompleteRangesInOneGroupCase1() throws Exception {\n+\t\tassertMatch(\"a[b-]\", \"ac\", false, false);\n+\t}\n+\n+\tpublic void testIncompleteRangesInOneGroupCase2() throws Exception {\n+\t\tassertMatch(\"a[b-]\", \"a-\", true, false);\n+\t}\n+\n+\tpublic void testCombinedRangesInOneGroupCase0() throws Exception {\n+\t\tassertMatch(\"[a-c-e]\", \"b\", true, false);\n+\t}\n+\n+\t/**\n+\t * The c belongs to the range a-c. \"-e\" is no valid range so d should not\n+\t * match.\n+\t * \n+\t * @throws Exception\n+\t *             for some reasons\n+\t */\n+\tpublic void testCombinedRangesInOneGroupCase1() throws Exception {\n+\t\tassertMatch(\"[a-c-e]\", \"d\", false, false);\n+\t}\n+\n+\tpublic void testCombinedRangesInOneGroupCase2() throws Exception {\n+\t\tassertMatch(\"[a-c-e]\", \"e\", true, false);\n+\t}\n+\n+\tpublic void testInversedGroupCase0() throws Exception {\n+\t\tassertMatch(\"[!b-c]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testInversedGroupCase1() throws Exception {\n+\t\tassertMatch(\"[!b-c]\", \"b\", false, false);\n+\t}\n+\n+\tpublic void testInversedGroupCase2() throws Exception {\n+\t\tassertMatch(\"[!b-c]\", \"c\", false, false);\n+\t}\n+\n+\tpublic void testInversedGroupCase3() throws Exception {\n+\t\tassertMatch(\"[!b-c]\", \"d\", true, false);\n+\t}\n+\n+\tpublic void testAlphaGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:alpha:]]\", \"d\", true, false);\n+\t}\n+\n+\tpublic void testAlphaGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:alpha:]]\", \":\", false, false);\n+\t}\n+\n+\tpublic void testAlphaGroupCase2() throws Exception {\n+\t\t// \\u00f6 = 'o' with dots on it\n+\t\tassertMatch(\"[[:alpha:]]\", \"\\u00f6\", true, false);\n+\t}\n+\n+\tpublic void test2AlphaGroupsCase0() throws Exception {\n+\t\t// \\u00f6 = 'o' with dots on it\n+\t\tassertMatch(\"[[:alpha:]][[:alpha:]]\", \"a\\u00f6\", true, false);\n+\t\tassertMatch(\"[[:alpha:]][[:alpha:]]\", \"a1\", false, false);\n+\t}\n+\n+\tpublic void testAlnumGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:alnum:]]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testAlnumGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:alnum:]]\", \"1\", true, false);\n+\t}\n+\n+\tpublic void testAlnumGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:alnum:]]\", \":\", false, false);\n+\t}\n+\n+\tpublic void testBlankGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:blank:]]\", \" \", true, false);\n+\t}\n+\n+\tpublic void testBlankGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:blank:]]\", \"\\t\", true, false);\n+\t}\n+\n+\tpublic void testBlankGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:blank:]]\", \"\\r\", false, false);\n+\t}\n+\n+\tpublic void testBlankGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:blank:]]\", \"\\n\", false, false);\n+\t}\n+\n+\tpublic void testBlankGroupCase4() throws Exception {\n+\t\tassertMatch(\"[[:blank:]]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testCntrlGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:cntrl:]]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testCntrlGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:cntrl:]]\", String.valueOf((char) 7), true, false);\n+\t}\n+\n+\tpublic void testDigitGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:digit:]]\", \"0\", true, false);\n+\t}\n+\n+\tpublic void testDigitGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:digit:]]\", \"5\", true, false);\n+\t}\n+\n+\tpublic void testDigitGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:digit:]]\", \"9\", true, false);\n+\t}\n+\n+\tpublic void testDigitGroupCase3() throws Exception {\n+\t\t// \\u06f9 = EXTENDED ARABIC-INDIC DIGIT NINE\n+\t\tassertMatch(\"[[:digit:]]\", \"\\u06f9\", true, false);\n+\t}\n+\n+\tpublic void testDigitGroupCase4() throws Exception {\n+\t\tassertMatch(\"[[:digit:]]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testDigitGroupCase5() throws Exception {\n+\t\tassertMatch(\"[[:digit:]]\", \"]\", false, false);\n+\t}\n+\n+\tpublic void testGraphGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:graph:]]\", \"]\", true, false);\n+\t}\n+\n+\tpublic void testGraphGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:graph:]]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testGraphGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:graph:]]\", \".\", true, false);\n+\t}\n+\n+\tpublic void testGraphGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:graph:]]\", \"0\", true, false);\n+\t}\n+\n+\tpublic void testGraphGroupCase4() throws Exception {\n+\t\tassertMatch(\"[[:graph:]]\", \" \", false, false);\n+\t}\n+\n+\tpublic void testGraphGroupCase5() throws Exception {\n+\t\t// \\u00f6 = 'o' with dots on it\n+\t\tassertMatch(\"[[:graph:]]\", \"\\u00f6\", true, false);\n+\t}\n+\n+\tpublic void testLowerGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:lower:]]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testLowerGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:lower:]]\", \"h\", true, false);\n+\t}\n+\n+\tpublic void testLowerGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:lower:]]\", \"A\", false, false);\n+\t}\n+\n+\tpublic void testLowerGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:lower:]]\", \"H\", false, false);\n+\t}\n+\n+\tpublic void testLowerGroupCase4() throws Exception {\n+\t\t// \\u00e4 = small 'a' with dots on it\n+\t\tassertMatch(\"[[:lower:]]\", \"\\u00e4\", true, false);\n+\t}\n+\n+\tpublic void testLowerGroupCase5() throws Exception {\n+\t\tassertMatch(\"[[:lower:]]\", \".\", false, false);\n+\t}\n+\n+\tpublic void testPrintGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:print:]]\", \"]\", true, false);\n+\t}\n+\n+\tpublic void testPrintGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:print:]]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testPrintGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:print:]]\", \".\", true, false);\n+\t}\n+\n+\tpublic void testPrintGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:print:]]\", \"0\", true, false);\n+\t}\n+\n+\tpublic void testPrintGroupCase4() throws Exception {\n+\t\tassertMatch(\"[[:print:]]\", \" \", true, false);\n+\t}\n+\n+\tpublic void testPrintGroupCase5() throws Exception {\n+\t\t// \\u00f6 = 'o' with dots on it\n+\t\tassertMatch(\"[[:print:]]\", \"\\u00f6\", true, false);\n+\t}\n+\n+\tpublic void testPunctGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:punct:]]\", \".\", true, false);\n+\t}\n+\n+\tpublic void testPunctGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:punct:]]\", \"@\", true, false);\n+\t}\n+\n+\tpublic void testPunctGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:punct:]]\", \" \", false, false);\n+\t}\n+\n+\tpublic void testPunctGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:punct:]]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testSpaceGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:space:]]\", \" \", true, false);\n+\t}\n+\n+\tpublic void testSpaceGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:space:]]\", \"\\t\", true, false);\n+\t}\n+\n+\tpublic void testSpaceGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:space:]]\", \"\\r\", true, false);\n+\t}\n+\n+\tpublic void testSpaceGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:space:]]\", \"\\n\", true, false);\n+\t}\n+\n+\tpublic void testSpaceGroupCase4() throws Exception {\n+\t\tassertMatch(\"[[:space:]]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testUpperGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:upper:]]\", \"a\", false, false);\n+\t}\n+\n+\tpublic void testUpperGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:upper:]]\", \"h\", false, false);\n+\t}\n+\n+\tpublic void testUpperGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:upper:]]\", \"A\", true, false);\n+\t}\n+\n+\tpublic void testUpperGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:upper:]]\", \"H\", true, false);\n+\t}\n+\n+\tpublic void testUpperGroupCase4() throws Exception {\n+\t\t// \\u00c4 = 'A' with dots on it\n+\t\tassertMatch(\"[[:upper:]]\", \"\\u00c4\", true, false);\n+\t}\n+\n+\tpublic void testUpperGroupCase5() throws Exception {\n+\t\tassertMatch(\"[[:upper:]]\", \".\", false, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase1() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \"d\", true, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \"f\", true, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \"0\", true, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase4() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \"5\", true, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase5() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \"9\", true, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase6() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \"Û¹\", false, false);\n+\t}\n+\n+\tpublic void testXDigitGroupCase7() throws Exception {\n+\t\tassertMatch(\"[[:xdigit:]]\", \".\", false, false);\n+\t}\n+\n+\tpublic void testWordroupCase0() throws Exception {\n+\t\tassertMatch(\"[[:word:]]\", \"g\", true, false);\n+\t}\n+\n+\tpublic void testWordroupCase1() throws Exception {\n+\t\t// \\u00f6 = 'o' with dots on it\n+\t\tassertMatch(\"[[:word:]]\", \"\\u00f6\", true, false);\n+\t}\n+\n+\tpublic void testWordroupCase2() throws Exception {\n+\t\tassertMatch(\"[[:word:]]\", \"5\", true, false);\n+\t}\n+\n+\tpublic void testWordroupCase3() throws Exception {\n+\t\tassertMatch(\"[[:word:]]\", \"_\", true, false);\n+\t}\n+\n+\tpublic void testWordroupCase4() throws Exception {\n+\t\tassertMatch(\"[[:word:]]\", \" \", false, false);\n+\t}\n+\n+\tpublic void testWordroupCase5() throws Exception {\n+\t\tassertMatch(\"[[:word:]]\", \".\", false, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase0() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"A\", true, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase1() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"C\", true, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase2() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"e\", true, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase3() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"3\", true, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase4() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"4\", true, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase5() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"5\", true, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase6() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"B\", false, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase7() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"2\", false, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase8() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \"6\", false, false);\n+\t}\n+\n+\tpublic void testMixedGroupCase9() throws Exception {\n+\t\tassertMatch(\"[A[:lower:]C3-5]\", \".\", false, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase0() throws Exception {\n+\t\tassertMatch(\"[[]\", \"[\", true, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase1() throws Exception {\n+\t\tassertMatch(\"[]]\", \"]\", true, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase2() throws Exception {\n+\t\tassertMatch(\"[]a]\", \"]\", true, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase3() throws Exception {\n+\t\tassertMatch(\"[a[]\", \"[\", true, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase4() throws Exception {\n+\t\tassertMatch(\"[a[]\", \"a\", true, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase5() throws Exception {\n+\t\tassertMatch(\"[!]]\", \"]\", false, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase6() throws Exception {\n+\t\tassertMatch(\"[!]]\", \"x\", true, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase7() throws Exception {\n+\t\tassertMatch(\"[:]]\", \":]\", true, false);\n+\t}\n+\n+\tpublic void testSpecialGroupCase8() throws Exception {\n+\t\tassertMatch(\"[:]]\", \":\", false, true);\n+\t}\n+\n+\tpublic void testSpecialGroupCase9() throws Exception {\n+\t\ttry {\n+\t\t\tassertMatch(\"[[:]\", \":\", true, true);\n+\t\t\tfail(\"InvalidPatternException expected\");\n+\t\t} catch (InvalidPatternException e) {\n+\t\t\t// expected\n+\t\t}\n+\t}\n+\n+\tpublic void testUnsupportedGroupCase0() throws Exception {\n+\t\ttry {\n+\t\t\tassertMatch(\"[[=a=]]\", \"b\", false, false);\n+\t\t\tfail(\"InvalidPatternException expected\");\n+\t\t} catch (InvalidPatternException e) {\n+\t\t\tassertTrue(e.getMessage().contains(\"[=a=]\"));\n+\t\t}\n+\t}\n+\n+\tpublic void testUnsupportedGroupCase1() throws Exception {\n+\t\ttry {\n+\t\t\tassertMatch(\"[[.a.]]\", \"b\", false, false);\n+\t\t\tfail(\"InvalidPatternException expected\");\n+\t\t} catch (InvalidPatternException e) {\n+\t\t\tassertTrue(e.getMessage().contains(\"[.a.]\"));\n+\t\t}\n+\t}\n+\n+\tpublic void testFilePathSimpleCase() throws Exception {\n+\t\tassertFileNameMatch(\"a/b\", \"a/b\", '/', true, false);\n+\t}\n+\n+\tpublic void testFilePathCase0() throws Exception {\n+\t\tassertFileNameMatch(\"a*b\", \"a/b\", '/', false, false);\n+\t}\n+\n+\tpublic void testFilePathCase1() throws Exception {\n+\t\tassertFileNameMatch(\"a?b\", \"a/b\", '/', false, false);\n+\t}\n+\n+\tpublic void testFilePathCase2() throws Exception {\n+\t\tassertFileNameMatch(\"a*b\", \"a\\\\b\", '\\\\', false, false);\n+\t}\n+\n+\tpublic void testFilePathCase3() throws Exception {\n+\t\tassertFileNameMatch(\"a?b\", \"a\\\\b\", '\\\\', false, false);\n+\t}\n+\n+\tpublic void testReset() throws Exception {\n+\t\tfinal String pattern = \"helloworld\";\n+\t\tfinal FileNameMatcher matcher = new FileNameMatcher(pattern, null);\n+\t\tmatcher.append(\"helloworld\");\n+\t\tassertEquals(true, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t\tmatcher.reset();\n+\t\tmatcher.append(\"hello\");\n+\t\tassertEquals(false, matcher.isMatch());\n+\t\tassertEquals(true, matcher.canAppendMatch());\n+\t\tmatcher.append(\"world\");\n+\t\tassertEquals(true, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t\tmatcher.append(\"to much\");\n+\t\tassertEquals(false, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t\tmatcher.reset();\n+\t\tmatcher.append(\"helloworld\");\n+\t\tassertEquals(true, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t}\n+\n+\tpublic void testCreateMatcherForSuffix() throws Exception {\n+\t\tfinal String pattern = \"helloworld\";\n+\t\tfinal FileNameMatcher matcher = new FileNameMatcher(pattern, null);\n+\t\tmatcher.append(\"hello\");\n+\t\tfinal FileNameMatcher childMatcher = matcher.createMatcherForSuffix();\n+\t\tassertEquals(false, matcher.isMatch());\n+\t\tassertEquals(true, matcher.canAppendMatch());\n+\t\tassertEquals(false, childMatcher.isMatch());\n+\t\tassertEquals(true, childMatcher.canAppendMatch());\n+\t\tmatcher.append(\"world\");\n+\t\tassertEquals(true, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t\tassertEquals(false, childMatcher.isMatch());\n+\t\tassertEquals(true, childMatcher.canAppendMatch());\n+\t\tchildMatcher.append(\"world\");\n+\t\tassertEquals(true, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t\tassertEquals(true, childMatcher.isMatch());\n+\t\tassertEquals(false, childMatcher.canAppendMatch());\n+\t\tchildMatcher.reset();\n+\t\tassertEquals(true, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t\tassertEquals(false, childMatcher.isMatch());\n+\t\tassertEquals(true, childMatcher.canAppendMatch());\n+\t\tchildMatcher.append(\"world\");\n+\t\tassertEquals(true, matcher.isMatch());\n+\t\tassertEquals(false, matcher.canAppendMatch());\n+\t\tassertEquals(true, childMatcher.isMatch());\n+\t\tassertEquals(false, childMatcher.canAppendMatch());\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java\nnew file mode 100644\nindex 0000000..02f67fe\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/InvalidPatternException.java\n@@ -0,0 +1,65 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.errors;\n+\n+/**\n+ * Thrown when a pattern passed in an argument was wrong.\n+ * \n+ */\n+public class InvalidPatternException extends Exception {\n+\tprivate final String pattern;\n+\n+\t/**\n+\t * @param message\n+\t *            explains what was wrong with the pattern.\n+\t * @param pattern\n+\t *            the invalid pattern.\n+\t */\n+\tpublic InvalidPatternException(String message, String pattern) {\n+\t\tsuper(message);\n+\t\tthis.pattern = pattern;\n+\t}\n+\n+\t/**\n+\t * @return the invalid pattern.\n+\t */\n+\tpublic String getPattern() {\n+\t\treturn pattern;\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java b/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java\nnew file mode 100644\nindex 0000000..1a93906\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/errors/NoClosingBracketException.java\n@@ -0,0 +1,69 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.errors;\n+\n+/**\n+ * Thrown when a pattern contains a character group which is open to the right\n+ * side or a character class which is open to the right side.\n+ */\n+public class NoClosingBracketException extends InvalidPatternException {\n+\n+\t/**\n+\t * @param indexOfOpeningBracket\n+\t *            the position of the [ character which has no ] character.\n+\t * @param openingBracket\n+\t *            the unclosed bracket.\n+\t * @param closingBracket\n+\t *            the missing closing bracket.\n+\t * @param pattern\n+\t *            the invalid pattern.\n+\t */\n+\tpublic NoClosingBracketException(final int indexOfOpeningBracket,\n+\t\t\tfinal String openingBracket, final String closingBracket,\n+\t\t\tfinal String pattern) {\n+\t\tsuper(createMessage(indexOfOpeningBracket, openingBracket,\n+\t\t\t\tclosingBracket), pattern);\n+\t}\n+\n+\tprivate static String createMessage(final int indexOfOpeningBracket,\n+\t\t\tfinal String openingBracket, final String closingBracket) {\n+\t\treturn String.format(\"No closing %s found for %s at index %s.\",\n+\t\t\t\tclosingBracket, openingBracket, new Integer(\n+\t\t\t\t\t\tindexOfOpeningBracket));\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java\nnew file mode 100644\nindex 0000000..1e9a0ca\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/AbstractHead.java\n@@ -0,0 +1,74 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+import java.util.List;\n+\n+abstract class AbstractHead implements Head {\n+\tprivate List<Head> newHeads = null;\n+\n+\tprivate final boolean star;\n+\n+\tprotected abstract boolean matches(char c);\n+\n+\tAbstractHead(boolean star) {\n+\t\tthis.star = star;\n+\t}\n+\n+\t/**\n+\t * \n+\t * @param newHeads\n+\t *            a list of {@link Head}s which will not be modified.\n+\t */\n+\tpublic final void setNewHeads(List<Head> newHeads) {\n+\t\tif (this.newHeads != null)\n+\t\t\tthrow new IllegalStateException(\"Property is already non null\");\n+\t\tthis.newHeads = newHeads;\n+\t}\n+\n+\tpublic List<Head> getNextHeads(char c) {\n+\t\tif (matches(c))\n+\t\t\treturn newHeads;\n+\t\telse\n+\t\t\treturn FileNameMatcher.EMPTY_HEAD_LIST;\n+\t}\n+\n+\tboolean isStar() {\n+\t\treturn star;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java\nnew file mode 100644\nindex 0000000..01c3403\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/CharacterHead.java\n@@ -0,0 +1,53 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+final class CharacterHead extends AbstractHead {\n+\tprivate final char expectedCharacter;\n+\n+\tprotected CharacterHead(final char expectedCharacter) {\n+\t\tsuper(false);\n+\t\tthis.expectedCharacter = expectedCharacter;\n+\t}\n+\n+\t@Override\n+\tprotected final boolean matches(final char c) {\n+\t\treturn c == expectedCharacter;\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java\nnew file mode 100644\nindex 0000000..30a5930\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/FileNameMatcher.java\n@@ -0,0 +1,351 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.List;\n+import java.util.ListIterator;\n+import java.util.regex.Matcher;\n+import java.util.regex.Pattern;\n+\n+import org.spearce.jgit.errors.InvalidPatternException;\n+import org.spearce.jgit.errors.NoClosingBracketException;\n+\n+/**\n+ * This class can be used to match filenames against fnmatch like patterns. It\n+ * is not thread save.\n+ * <p>\n+ * Supported are the wildcard characters * and ? and groups with:\n+ * <ul>\n+ * <li> characters e.g. [abc]</li>\n+ * <li> ranges e.g. [a-z]</li>\n+ * <li> the following character classes\n+ * <ul>\n+ * <li>[:alnum:]</li>\n+ * <li>[:alpha:]</li>\n+ * <li>[:blank:]</li>\n+ * <li>[:cntrl:]</li>\n+ * <li>[:digit:]</li>\n+ * <li>[:graph:]</li>\n+ * <li>[:lower:]</li>\n+ * <li>[:print:]</li>\n+ * <li>[:punct:]</li>\n+ * <li>[:space:]</li>\n+ * <li>[:upper:]</li>\n+ * <li>[:word:]</li>\n+ * <li>[:xdigit:]</li>\n+ * </ul>\n+ * e. g. [[:xdigit:]] </li>\n+ * </ul>\n+ * </p>\n+ */\n+public class FileNameMatcher {\n+\tstatic final List<Head> EMPTY_HEAD_LIST = Collections.emptyList();\n+\n+\tprivate static final Pattern characterClassStartPattern = Pattern\n+\t\t\t.compile(\"\\\\[[.:=]\");\n+\n+\tprivate List<Head> headsStartValue;\n+\n+\tprivate List<Head> heads;\n+\n+\t/**\n+\t * {{@link #extendStringToMatchByOneCharacter(char)} needs a list for the\n+\t * new heads, allocating a new array would be bad for the performance, as\n+\t * the method gets called very often.\n+\t * \n+\t */\n+\tprivate List<Head> listForLocalUseage;\n+\n+\t/**\n+\t * \n+\t * @param headsStartValue\n+\t *            must be a list which will never be modified.\n+\t */\n+\tprivate FileNameMatcher(final List<Head> headsStartValue) {\n+\t\tthis.headsStartValue = headsStartValue;\n+\t\tthis.heads = new ArrayList<Head>(headsStartValue.size());\n+\t\tthis.heads.addAll(this.headsStartValue);\n+\t\tthis.listForLocalUseage = new ArrayList<Head>(headsStartValue.size());\n+\t}\n+\n+\t/**\n+\t * @param patternString\n+\t *            must contain a pattern which fnmatch would accept.\n+\t * @param invalidWildgetCharacter\n+\t *            if this parameter isn't null then this character will not\n+\t *            match at wildcards(* and ? are wildcards).\n+\t * @throws InvalidPatternException\n+\t *             if the patternString contains a invalid fnmatch pattern.\n+\t */\n+\tpublic FileNameMatcher(final String patternString,\n+\t\t\tfinal Character invalidWildgetCharacter)\n+\t\t\tthrows InvalidPatternException {\n+\t\tthis(createHeadsStartValues(patternString, invalidWildgetCharacter));\n+\t}\n+\n+\tprivate static List<Head> createHeadsStartValues(\n+\t\t\tfinal String patternString, final Character invalidWildgetCharacter)\n+\t\t\tthrows InvalidPatternException {\n+\n+\t\tfinal List<AbstractHead> allHeads = parseHeads(patternString,\n+\t\t\t\tinvalidWildgetCharacter);\n+\n+\t\tList<Head> nextHeadsSuggestion = new ArrayList<Head>(2);\n+\t\tnextHeadsSuggestion.add(LastHead.INSTANCE);\n+\t\tfor (int i = allHeads.size() - 1; i >= 0; i--) {\n+\t\t\tfinal AbstractHead head = allHeads.get(i);\n+\n+\t\t\t// explanation:\n+\t\t\t// a and * of the pattern \"a*b\"\n+\t\t\t// need *b as newHeads\n+\t\t\t// that's why * extends the list for it self and it's left neighbor.\n+\t\t\tif (head.isStar()) {\n+\t\t\t\tnextHeadsSuggestion.add(head);\n+\t\t\t\thead.setNewHeads(nextHeadsSuggestion);\n+\t\t\t} else {\n+\t\t\t\thead.setNewHeads(nextHeadsSuggestion);\n+\t\t\t\tnextHeadsSuggestion = new ArrayList<Head>(2);\n+\t\t\t\tnextHeadsSuggestion.add(head);\n+\t\t\t}\n+\t\t}\n+\t\treturn nextHeadsSuggestion;\n+\t}\n+\n+\tprivate static int findGroupEnd(final int indexOfStartBracket,\n+\t\t\tfinal String pattern) throws InvalidPatternException {\n+\t\tint firstValidCharClassIndex = indexOfStartBracket + 1;\n+\t\tint firstValidEndBracketIndex = indexOfStartBracket + 2;\n+\n+\t\tif (indexOfStartBracket + 1 >= pattern.length())\n+\t\t\tthrow new NoClosingBracketException(indexOfStartBracket, \"[\", \"]\",\n+\t\t\t\t\tpattern);\n+\n+\t\tif (pattern.charAt(firstValidCharClassIndex) == '!') {\n+\t\t\tfirstValidCharClassIndex++;\n+\t\t\tfirstValidEndBracketIndex++;\n+\t\t}\n+\n+\t\tfinal Matcher charClassStartMatcher = characterClassStartPattern\n+\t\t\t\t.matcher(pattern);\n+\n+\t\tint groupEnd = -1;\n+\t\twhile (groupEnd == -1) {\n+\n+\t\t\tfinal int possibleGroupEnd = pattern.indexOf(']',\n+\t\t\t\t\tfirstValidEndBracketIndex);\n+\t\t\tif (possibleGroupEnd == -1)\n+\t\t\t\tthrow new NoClosingBracketException(indexOfStartBracket, \"[\",\n+\t\t\t\t\t\t\"]\", pattern);\n+\n+\t\t\tfinal boolean foundCharClass = charClassStartMatcher\n+\t\t\t\t\t.find(firstValidCharClassIndex);\n+\n+\t\t\tif (foundCharClass\n+\t\t\t\t\t&& charClassStartMatcher.start() < possibleGroupEnd) {\n+\n+\t\t\t\tfinal String classStart = charClassStartMatcher.group(0);\n+\t\t\t\tfinal String classEnd = classStart.charAt(1) + \"]\";\n+\n+\t\t\t\tfinal int classStartIndex = charClassStartMatcher.start();\n+\t\t\t\tfinal int classEndIndex = pattern.indexOf(classEnd,\n+\t\t\t\t\t\tclassStartIndex + 2);\n+\n+\t\t\t\tif (classEndIndex == -1)\n+\t\t\t\t\tthrow new NoClosingBracketException(classStartIndex,\n+\t\t\t\t\t\t\tclassStart, classEnd, pattern);\n+\n+\t\t\t\tfirstValidCharClassIndex = classEndIndex + 2;\n+\t\t\t\tfirstValidEndBracketIndex = firstValidCharClassIndex;\n+\t\t\t} else {\n+\t\t\t\tgroupEnd = possibleGroupEnd;\n+\t\t\t}\n+\t\t}\n+\t\treturn groupEnd;\n+\t}\n+\n+\tprivate static List<AbstractHead> parseHeads(final String pattern,\n+\t\t\tfinal Character invalidWildgetCharacter)\n+\t\t\tthrows InvalidPatternException {\n+\n+\t\tint currentIndex = 0;\n+\t\tList<AbstractHead> heads = new ArrayList<AbstractHead>();\n+\t\twhile (currentIndex < pattern.length()) {\n+\t\t\tfinal int groupStart = pattern.indexOf('[', currentIndex);\n+\t\t\tif (groupStart == -1) {\n+\t\t\t\tfinal String patternPart = pattern.substring(currentIndex);\n+\t\t\t\theads.addAll(createSimpleHeads(patternPart,\n+\t\t\t\t\t\tinvalidWildgetCharacter));\n+\t\t\t\tcurrentIndex = pattern.length();\n+\t\t\t} else {\n+\t\t\t\tfinal String patternPart = pattern.substring(currentIndex,\n+\t\t\t\t\t\tgroupStart);\n+\t\t\t\theads.addAll(createSimpleHeads(patternPart,\n+\t\t\t\t\t\tinvalidWildgetCharacter));\n+\n+\t\t\t\tfinal int groupEnd = findGroupEnd(groupStart, pattern);\n+\t\t\t\tfinal String groupPart = pattern.substring(groupStart + 1,\n+\t\t\t\t\t\tgroupEnd);\n+\t\t\t\theads.add(new GroupHead(groupPart, pattern));\n+\t\t\t\tcurrentIndex = groupEnd + 1;\n+\t\t\t}\n+\t\t}\n+\t\treturn heads;\n+\t}\n+\n+\tprivate static List<AbstractHead> createSimpleHeads(\n+\t\t\tfinal String patternPart, final Character invalidWildgetCharacter) {\n+\t\tfinal List<AbstractHead> heads = new ArrayList<AbstractHead>(\n+\t\t\t\tpatternPart.length());\n+\t\tfor (int i = 0; i < patternPart.length(); i++) {\n+\t\t\tfinal char c = patternPart.charAt(i);\n+\t\t\tswitch (c) {\n+\t\t\tcase '*': {\n+\t\t\t\tfinal AbstractHead head = createWildCardHead(\n+\t\t\t\t\t\tinvalidWildgetCharacter, true);\n+\t\t\t\theads.add(head);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tcase '?': {\n+\t\t\t\tfinal AbstractHead head = createWildCardHead(\n+\t\t\t\t\t\tinvalidWildgetCharacter, false);\n+\t\t\t\theads.add(head);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t\tdefault:\n+\t\t\t\tfinal CharacterHead head = new CharacterHead(c);\n+\t\t\t\theads.add(head);\n+\t\t\t}\n+\t\t}\n+\t\treturn heads;\n+\t}\n+\n+\tprivate static AbstractHead createWildCardHead(\n+\t\t\tfinal Character invalidWildgetCharacter, final boolean star) {\n+\t\tif (invalidWildgetCharacter != null)\n+\t\t\treturn new RestrictedWildCardHead(invalidWildgetCharacter\n+\t\t\t\t\t.charValue(), star);\n+\t\telse\n+\t\t\treturn new WildCardHead(star);\n+\t}\n+\n+\tprivate void extendStringToMatchByOneCharacter(final char c) {\n+\t\tfinal List<Head> newHeads = listForLocalUseage;\n+\t\tnewHeads.clear();\n+\t\tList<Head> lastAddedHeads = null;\n+\t\tfor (int i = 0; i < heads.size(); i++) {\n+\t\t\tfinal Head head = heads.get(i);\n+\t\t\tfinal List<Head> headsToAdd = head.getNextHeads(c);\n+\t\t\t// Why the next performance optimization isn't wrong:\n+\t\t\t// Some times two heads return the very same list.\n+\t\t\t// We save future effort if we don't add these heads again.\n+\t\t\t// This is the case with the heads \"a\" and \"*\" of \"a*b\" which\n+\t\t\t// both can return the list [\"*\",\"b\"]\n+\t\t\tif (headsToAdd != lastAddedHeads) {\n+\t\t\t\tnewHeads.addAll(headsToAdd);\n+\t\t\t\tlastAddedHeads = headsToAdd;\n+\t\t\t}\n+\t\t}\n+\t\tlistForLocalUseage = heads;\n+\t\theads = newHeads;\n+\t}\n+\n+\t/**\n+\t * \n+\t * @param stringToMatch\n+\t *            extends the string which is matched against the patterns of\n+\t *            this class.\n+\t */\n+\tpublic void append(final String stringToMatch) {\n+\t\tfor (int i = 0; i < stringToMatch.length(); i++) {\n+\t\t\tfinal char c = stringToMatch.charAt(i);\n+\t\t\textendStringToMatchByOneCharacter(c);\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Resets this matcher to it's state right after construction.\n+\t */\n+\tpublic void reset() {\n+\t\theads.clear();\n+\t\theads.addAll(headsStartValue);\n+\t}\n+\n+\t/**\n+\t * \n+\t * @return a {@link FileNameMatcher} instance which uses the same pattern\n+\t *         like this matcher, but has the current state of this matcher as\n+\t *         reset and start point.\n+\t */\n+\tpublic FileNameMatcher createMatcherForSuffix() {\n+\t\tfinal List<Head> copyOfHeads = new ArrayList<Head>(heads.size());\n+\t\tcopyOfHeads.addAll(heads);\n+\t\treturn new FileNameMatcher(copyOfHeads);\n+\t}\n+\n+\t/**\n+\t * \n+\t * @return true, if the string currently being matched does match.\n+\t */\n+\tpublic boolean isMatch() {\n+\t\tfinal ListIterator<Head> headIterator = heads\n+\t\t\t\t.listIterator(heads.size());\n+\t\twhile (headIterator.hasPrevious()) {\n+\t\t\tfinal Head head = headIterator.previous();\n+\t\t\tif (head == LastHead.INSTANCE) {\n+\t\t\t\treturn true;\n+\t\t\t}\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\t/**\n+\t * \n+\t * @return false, if the string being matched will not match when the string\n+\t *         gets extended.\n+\t */\n+\tpublic boolean canAppendMatch() {\n+\t\tfor (int i = 0; i < heads.size(); i++) {\n+\t\t\tif (heads.get(i) != LastHead.INSTANCE) {\n+\t\t\t\treturn true;\n+\t\t\t}\n+\t\t}\n+\t\treturn false;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java\nnew file mode 100644\nindex 0000000..9f72010\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/GroupHead.java\n@@ -0,0 +1,220 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+import java.util.ArrayList;\n+import java.util.List;\n+import java.util.regex.Matcher;\n+import java.util.regex.Pattern;\n+\n+import org.spearce.jgit.errors.InvalidPatternException;\n+\n+final class GroupHead extends AbstractHead {\n+\tprivate final List<CharacterPattern> characterClasses;\n+\n+\tprivate static final Pattern REGEX_PATTERN = Pattern\n+\t\t\t.compile(\"([^-][-][^-]|\\\\[[.:=].*?[.:=]\\\\])\");\n+\n+\tprivate final boolean inverse;\n+\n+\tGroupHead(String pattern, final String wholePattern)\n+\t\t\tthrows InvalidPatternException {\n+\t\tsuper(false);\n+\t\tthis.characterClasses = new ArrayList<CharacterPattern>();\n+\t\tthis.inverse = pattern.startsWith(\"!\");\n+\t\tif (inverse) {\n+\t\t\tpattern = pattern.substring(1);\n+\t\t}\n+\t\tfinal Matcher matcher = REGEX_PATTERN.matcher(pattern);\n+\t\twhile (matcher.find()) {\n+\t\t\tfinal String characterClass = matcher.group(0);\n+\t\t\tif (characterClass.length() == 3 && characterClass.charAt(1) == '-') {\n+\t\t\t\tfinal char start = characterClass.charAt(0);\n+\t\t\t\tfinal char end = characterClass.charAt(2);\n+\t\t\t\tcharacterClasses.add(new CharacterRange(start, end));\n+\t\t\t} else if (characterClass.equals(\"[:alnum:]\")) {\n+\t\t\t\tcharacterClasses.add(LetterPattern.INSTANCE);\n+\t\t\t\tcharacterClasses.add(DigitPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:alpha:]\")) {\n+\t\t\t\tcharacterClasses.add(LetterPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:blank:]\")) {\n+\t\t\t\tcharacterClasses.add(new OneCharacterPattern(' '));\n+\t\t\t\tcharacterClasses.add(new OneCharacterPattern('\\t'));\n+\t\t\t} else if (characterClass.equals(\"[:cntrl:]\")) {\n+\t\t\t\tcharacterClasses.add(new CharacterRange('\\u0000', '\\u001F'));\n+\t\t\t\tcharacterClasses.add(new OneCharacterPattern('\\u007F'));\n+\t\t\t} else if (characterClass.equals(\"[:digit:]\")) {\n+\t\t\t\tcharacterClasses.add(DigitPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:graph:]\")) {\n+\t\t\t\tcharacterClasses.add(new CharacterRange('\\u0021', '\\u007E'));\n+\t\t\t\tcharacterClasses.add(LetterPattern.INSTANCE);\n+\t\t\t\tcharacterClasses.add(DigitPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:lower:]\")) {\n+\t\t\t\tcharacterClasses.add(LowerPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:print:]\")) {\n+\t\t\t\tcharacterClasses.add(new CharacterRange('\\u0020', '\\u007E'));\n+\t\t\t\tcharacterClasses.add(LetterPattern.INSTANCE);\n+\t\t\t\tcharacterClasses.add(DigitPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:punct:]\")) {\n+\t\t\t\tcharacterClasses.add(PunctPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:space:]\")) {\n+\t\t\t\tcharacterClasses.add(WhitespacePattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:upper:]\")) {\n+\t\t\t\tcharacterClasses.add(UpperPattern.INSTANCE);\n+\t\t\t} else if (characterClass.equals(\"[:xdigit:]\")) {\n+\t\t\t\tcharacterClasses.add(new CharacterRange('0', '9'));\n+\t\t\t\tcharacterClasses.add(new CharacterRange('a', 'f'));\n+\t\t\t\tcharacterClasses.add(new CharacterRange('A', 'F'));\n+\t\t\t} else if (characterClass.equals(\"[:word:]\")) {\n+\t\t\t\tcharacterClasses.add(new OneCharacterPattern('_'));\n+\t\t\t\tcharacterClasses.add(LetterPattern.INSTANCE);\n+\t\t\t\tcharacterClasses.add(DigitPattern.INSTANCE);\n+\t\t\t} else {\n+\t\t\t\tfinal String message = String.format(\n+\t\t\t\t\t\t\"The character class %s is not supported.\",\n+\t\t\t\t\t\tcharacterClass);\n+\t\t\t\tthrow new InvalidPatternException(message, wholePattern);\n+\t\t\t}\n+\n+\t\t\tpattern = matcher.replaceFirst(\"\");\n+\t\t\tmatcher.reset(pattern);\n+\t\t}\n+\t\t// pattern contains now no ranges\n+\t\tfor (int i = 0; i < pattern.length(); i++) {\n+\t\t\tfinal char c = pattern.charAt(i);\n+\t\t\tcharacterClasses.add(new OneCharacterPattern(c));\n+\t\t}\n+\t}\n+\n+\t@Override\n+\tprotected final boolean matches(final char c) {\n+\t\tfor (CharacterPattern pattern : characterClasses) {\n+\t\t\tif (pattern.matches(c)) {\n+\t\t\t\treturn !inverse;\n+\t\t\t}\n+\t\t}\n+\t\treturn inverse;\n+\t}\n+\n+\tprivate interface CharacterPattern {\n+\t\t/**\n+\t\t * @param c\n+\t\t *            the character to test\n+\t\t * @return returns true if the character matches a pattern.\n+\t\t */\n+\t\tboolean matches(char c);\n+\t}\n+\n+\tprivate static final class CharacterRange implements CharacterPattern {\n+\t\tprivate final char start;\n+\n+\t\tprivate final char end;\n+\n+\t\tCharacterRange(char start, char end) {\n+\t\t\tthis.start = start;\n+\t\t\tthis.end = end;\n+\t\t}\n+\n+\t\tpublic final boolean matches(char c) {\n+\t\t\treturn start <= c && c <= end;\n+\t\t}\n+\t}\n+\n+\tprivate static final class DigitPattern implements CharacterPattern {\n+\t\tstatic final GroupHead.DigitPattern INSTANCE = new DigitPattern();\n+\n+\t\tpublic final boolean matches(char c) {\n+\t\t\treturn Character.isDigit(c);\n+\t\t}\n+\t}\n+\n+\tprivate static final class LetterPattern implements CharacterPattern {\n+\t\tstatic final GroupHead.LetterPattern INSTANCE = new LetterPattern();\n+\n+\t\tpublic final boolean matches(char c) {\n+\t\t\treturn Character.isLetter(c);\n+\t\t}\n+\t}\n+\n+\tprivate static final class LowerPattern implements CharacterPattern {\n+\t\tstatic final GroupHead.LowerPattern INSTANCE = new LowerPattern();\n+\n+\t\tpublic final boolean matches(char c) {\n+\t\t\treturn Character.isLowerCase(c);\n+\t\t}\n+\t}\n+\n+\tprivate static final class UpperPattern implements CharacterPattern {\n+\t\tstatic final GroupHead.UpperPattern INSTANCE = new UpperPattern();\n+\n+\t\tpublic final boolean matches(char c) {\n+\t\t\treturn Character.isUpperCase(c);\n+\t\t}\n+\t}\n+\n+\tprivate static final class WhitespacePattern implements CharacterPattern {\n+\t\tstatic final GroupHead.WhitespacePattern INSTANCE = new WhitespacePattern();\n+\n+\t\tpublic final boolean matches(char c) {\n+\t\t\treturn Character.isWhitespace(c);\n+\t\t}\n+\t}\n+\n+\tprivate static final class OneCharacterPattern implements CharacterPattern {\n+\t\tprivate char expectedCharacter;\n+\n+\t\tOneCharacterPattern(final char c) {\n+\t\t\tthis.expectedCharacter = c;\n+\t\t}\n+\n+\t\tpublic final boolean matches(char c) {\n+\t\t\treturn this.expectedCharacter == c;\n+\t\t}\n+\t}\n+\n+\tprivate static final class PunctPattern implements CharacterPattern {\n+\t\tstatic final GroupHead.PunctPattern INSTANCE = new PunctPattern();\n+\n+\t\tprivate static String punctCharacters = \"-!\\\"#$%&'()*+,./:;<=>?@[\\\\]_`{|}~\";\n+\n+\t\tpublic boolean matches(char c) {\n+\t\t\treturn punctCharacters.indexOf(c) != -1;\n+\t\t}\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java\nnew file mode 100644\nindex 0000000..498f96c\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/Head.java\n@@ -0,0 +1,50 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+import java.util.List;\n+\n+interface Head {\n+\t/**\n+\t * \n+\t * @param c\n+\t *            the character which decides which heads are returned.\n+\t * @return a list of heads based on the input.\n+\t */\n+\tpublic abstract List<Head> getNextHeads(char c);\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java\nnew file mode 100644\nindex 0000000..d3c9813\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/LastHead.java\n@@ -0,0 +1,56 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+import java.util.List;\n+\n+final class LastHead implements Head {\n+\tstatic final Head INSTANCE = new LastHead();\n+\n+\t/**\n+\t * Don't call this constructor, use {@link #INSTANCE}\n+\t */\n+\tprivate LastHead() {\n+\t\t// defined because of javadoc and visibility modifier.\n+\t}\n+\n+\tpublic List<Head> getNextHeads(char c) {\n+\t\treturn FileNameMatcher.EMPTY_HEAD_LIST;\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java\nnew file mode 100644\nindex 0000000..9d8d277\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/RestrictedWildCardHead.java\n@@ -0,0 +1,52 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+final class RestrictedWildCardHead extends AbstractHead {\n+\tprivate final char excludedCharacter;\n+\n+\tRestrictedWildCardHead(final char excludedCharacter, final boolean star) {\n+\t\tsuper(star);\n+\t\tthis.excludedCharacter = excludedCharacter;\n+\t}\n+\n+\t@Override\n+\tprotected final boolean matches(final char c) {\n+\t\treturn c != excludedCharacter;\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java\nnew file mode 100644\nindex 0000000..570e374\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/fnmatch/WildCardHead.java\n@@ -0,0 +1,49 @@\n+/*\n+ * Copyright (C) 2008, Florian KÃ¶berle <florianskarten@web.de>\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+\n+package org.spearce.jgit.fnmatch;\n+\n+final class WildCardHead extends AbstractHead {\n+\tWildCardHead(boolean star) {\n+\t\tsuper(star);\n+\t}\n+\n+\t@Override\n+\tprotected final boolean matches(final char c) {\n+\t\treturn true;\n+\t}\n+}\n-- \n1.5.4.3\n"}]}