{"thread":{"id":"20186","subject":"[JGIT PATCH 00/12] Cleanup Config class","startedAt":"2009-07-21T20:19:18Z","lastAt":"2009-07-24T21:34:16Z","messageCount":18,"participants":["Shawn O. Pearce","Robin Rosenberg","Constantine Plotnikov"],"isPatch":true,"patchVersion":1,"patchTotal":12},"messages":[{"id":"118403","messageId":"1248207570-13880-1-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":null,"subject":"[JGIT PATCH 00/12] Cleanup Config class","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:18Z","receivedAt":"2009-07-21T20:19:18Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Misc. cleanups of the Config class (aka the base class for\nRepositoryConfig) to make the code easier to follow.  I came\nabout this series because I started to do improvements to the\nConfig class to better support submodule introspection from a bare\nrepository... and I realized this code was a mess.\n\nThis series (or at least the first patch of it) applies on top of\nmy prior 11 patch series for alternate object database improvements.\n\nShawn O. Pearce (12):\n  Use NB.readFully(File) to slurp complete file contents\n  Correct name of fileRead member of Config class\n  Add setLong to Config\n  Fix Config setInt(..., 0) to store \"0\" not \"0 g\"\n  Rename Config.unsetString to just unset()\n  Remove pointless null assignments in Config\n  Clarify section and subsection values in Config code\n  Don't subclass PrintWriter when writing the Config\n  Use a Java 5 style iteration over the Config entries list\n  Match config subsection names using case sensitive search\n  Cleanup Config's MAGIC_EMPTY_VALUE to be more safe\n  Remove unreferenced REMOTE_SECTION from RepositoryConfig\n\n .../src/org/spearce/jgit/lib/Config.java           |  196 +++++++++++---------\n .../src/org/spearce/jgit/lib/FileBasedConfig.java  |    7 +-\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   15 +-\n .../src/org/spearce/jgit/lib/ReflogReader.java     |   32 +---\n .../src/org/spearce/jgit/lib/RepositoryCache.java  |   20 +-\n .../src/org/spearce/jgit/lib/RepositoryConfig.java |    4 -\n .../org/spearce/jgit/lib/UnpackedObjectLoader.java |   15 +--\n .../org/spearce/jgit/transport/RemoteConfig.java   |    2 +-\n org.spearce.jgit/src/org/spearce/jgit/util/NB.java |   52 +++++\n 9 files changed, 195 insertions(+), 148 deletions(-)\n"},{"id":"118409","messageId":"1248207570-13880-2-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-1-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 01/12] Use NB.readFully(File) to slurp complete file contents","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:19Z","receivedAt":"2009-07-21T20:19:19Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"When we want the entire file contents and its likely a very small\nfile (e.g. a ref, configuration file) NB.readFully(File) is an\neasier method to get the content then using FileInputStream from\napplication code.\n\nWhen looking at a file that we believe should contain only a SHA-1\nor be a symbolic ref to another file within this repository,\nwe use a maximum read cap of 4096 bytes for the slurp, so that\nwe don't accidentally allocate a very large buffer and blow the\nJVM heap up.  This reasonableness cap is particularly important\nfor the RepositoryCache.FileKey.isGitRepository() test logic, in\ncase it comes across a file called HEAD that isn't really part of\na Git repository.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n\n Due to the patch to RepositoryCache here, this applies on top of\n my other 11 patch series that introduced the class.\n\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   15 +++--\n .../src/org/spearce/jgit/lib/ReflogReader.java     |   32 ++++--------\n .../src/org/spearce/jgit/lib/RepositoryCache.java  |   20 +++----\n .../org/spearce/jgit/lib/UnpackedObjectLoader.java |   15 +-----\n org.spearce.jgit/src/org/spearce/jgit/util/NB.java |   52 ++++++++++++++++++++\n 5 files changed, 81 insertions(+), 53 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 383877f..f7751c4 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -52,6 +52,8 @@\n import org.spearce.jgit.errors.ObjectWritingException;\n import org.spearce.jgit.lib.Ref.Storage;\n import org.spearce.jgit.util.FS;\n+import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n \n class RefDatabase {\n \tprivate static final String REFS_SLASH = \"refs/\";\n@@ -494,12 +496,13 @@ protected void writeFile(String name, byte[] content) throws IOException {\n \n \tprivate static String readLine(final File file)\n \t\t\tthrows FileNotFoundException, IOException {\n-\t\tfinal BufferedReader br = openReader(file);\n-\t\ttry {\n-\t\t\treturn br.readLine();\n-\t\t} finally {\n-\t\t\tbr.close();\n-\t\t}\n+\t\tfinal byte[] buf = NB.readFully(file, 4096);\n+\t\tint n = buf.length;\n+\t\tif (n == 0)\n+\t\t\treturn null;\n+\t\tif (buf[n - 1] == '\\n')\n+\t\t\tn--;\n+\t\treturn RawParseUtils.decode(buf, 0, n);\n \t}\n \n \tprivate static BufferedReader openReader(final File fileLocation)\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\nindex e86a723..5f2d28c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n@@ -37,7 +37,6 @@\n package org.spearce.jgit.lib;\n \n import java.io.File;\n-import java.io.FileInputStream;\n import java.io.FileNotFoundException;\n import java.io.IOException;\n import java.util.ArrayList;\n@@ -157,31 +156,20 @@ public Entry getLastEntry() throws IOException {\n \t * @throws IOException\n \t */\n \tpublic List<Entry> getReverseEntries(int max) throws IOException {\n-\t\tFileInputStream fileInputStream;\n+\t\tfinal byte[] log;\n \t\ttry {\n-\t\t\tfileInputStream = new FileInputStream(logName);\n+\t\t\tlog = NB.readFully(logName);\n \t\t} catch (FileNotFoundException e) {\n \t\t\treturn Collections.emptyList();\n \t\t}\n-\t\ttry {\n-\t\t\tlong logSize = fileInputStream.getChannel().size();\n-\t\t\tif (logSize > Integer.MAX_VALUE) {\n-\t\t\t\t// implementation limit, will suck with smaller files too\n-\t\t\t\tthrow new IOException(\"Cannot handle reflog larger than \"\n-\t\t\t\t\t\t+ Integer.MAX_VALUE + \" bytes\");\n-\t\t\t}\n-\t\t\tbyte[] log = new byte[(int) logSize];\n-\t\t\tNB.readFully(fileInputStream, log, 0, log.length);\n-\t\t\tint rs = RawParseUtils.prevLF(log, log.length);\n-\t\t\tList<Entry> ret = new ArrayList<Entry>();\n-\t\t\twhile (rs >= 0 && max-- > 0) {\n-\t\t\t\trs = RawParseUtils.prevLF(log, rs);\n-\t\t\t\tEntry entry = new Entry(log, rs < 0 ? 0 : rs + 2);\n-\t\t\t\tret.add(entry);\n-\t\t\t}\n-\t\t\treturn ret;\n-\t\t} finally {\n-\t\t\tfileInputStream.close();\n+\n+\t\tint rs = RawParseUtils.prevLF(log, log.length);\n+\t\tList<Entry> ret = new ArrayList<Entry>();\n+\t\twhile (rs >= 0 && max-- > 0) {\n+\t\t\trs = RawParseUtils.prevLF(log, rs);\n+\t\t\tEntry entry = new Entry(log, rs < 0 ? 0 : rs + 2);\n+\t\t\tret.add(entry);\n \t\t}\n+\t\treturn ret;\n \t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java\nindex 50b4330..5f90b55 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryCache.java\n@@ -37,11 +37,8 @@\n \n package org.spearce.jgit.lib;\n \n-import java.io.BufferedReader;\n import java.io.File;\n-import java.io.FileInputStream;\n import java.io.IOException;\n-import java.io.InputStreamReader;\n import java.lang.ref.Reference;\n import java.lang.ref.WeakReference;\n import java.util.Iterator;\n@@ -50,6 +47,8 @@\n \n import org.spearce.jgit.errors.RepositoryNotFoundException;\n import org.spearce.jgit.util.FS;\n+import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /** Cache of active {@link Repository} instances. */\n public class RepositoryCache {\n@@ -341,14 +340,13 @@ private static boolean isValidHead(final File head) {\n \n \t\tprivate static String readFirstLine(final File head) {\n \t\t\ttry {\n-\t\t\t\tfinal BufferedReader br = new BufferedReader(\n-\t\t\t\t\t\tnew InputStreamReader(new FileInputStream(head),\n-\t\t\t\t\t\t\t\tConstants.CHARSET));\n-\t\t\t\ttry {\n-\t\t\t\t\treturn br.readLine();\n-\t\t\t\t} finally {\n-\t\t\t\t\tbr.close();\n-\t\t\t\t}\n+\t\t\t\tfinal byte[] buf = NB.readFully(head, 4096);\n+\t\t\t\tint n = buf.length;\n+\t\t\t\tif (n == 0)\n+\t\t\t\t\treturn null;\n+\t\t\t\tif (buf[n - 1] == '\\n')\n+\t\t\t\t\tn--;\n+\t\t\t\treturn RawParseUtils.decode(buf, 0, n);\n \t\t\t} catch (IOException e) {\n \t\t\t\treturn null;\n \t\t\t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java\nindex e2de41c..b5abeb9 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/UnpackedObjectLoader.java\n@@ -39,7 +39,6 @@\n package org.spearce.jgit.lib;\n \n import java.io.File;\n-import java.io.FileInputStream;\n import java.io.FileNotFoundException;\n import java.io.IOException;\n import java.util.zip.DataFormatException;\n@@ -74,19 +73,7 @@\n \t */\n \tpublic UnpackedObjectLoader(final File path, final AnyObjectId id)\n \t\t\tthrows IOException {\n-\t\tthis(readCompressed(path), id);\n-\t}\n-\n-\tprivate static byte[] readCompressed(final File path)\n-\t\t\tthrows FileNotFoundException, IOException {\n-\t\tfinal FileInputStream in = new FileInputStream(path);\n-\t\ttry {\n-\t\t\tfinal byte[] compressed = new byte[(int) in.getChannel().size()];\n-\t\t\tNB.readFully(in, compressed, 0, compressed.length);\n-\t\t\treturn compressed;\n-\t\t} finally {\n-\t\t\tin.close();\n-\t\t}\n+\t\tthis(NB.readFully(path), id);\n \t}\n \n \t/**\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/NB.java b/org.spearce.jgit/src/org/spearce/jgit/util/NB.java\nindex 032997f..17b3398 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/NB.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/NB.java\n@@ -38,6 +38,9 @@\n package org.spearce.jgit.util;\n \n import java.io.EOFException;\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n import java.io.IOException;\n import java.io.InputStream;\n import java.nio.ByteBuffer;\n@@ -46,6 +49,55 @@\n /** Conversion utilities for network byte order handling. */\n public final class NB {\n \t/**\n+\t * Read an entire local file into memory as a byte array.\n+\t *\n+\t * @param path\n+\t *            location of the file to read.\n+\t * @return complete contents of the requested local file.\n+\t * @throws FileNotFoundException\n+\t *             the file does not exist.\n+\t * @throws IOException\n+\t *             the file exists, but its contents cannot be read.\n+\t */\n+\tpublic static final byte[] readFully(final File path)\n+\t\t\tthrows FileNotFoundException, IOException {\n+\t\treturn readFully(path, Integer.MAX_VALUE);\n+\t}\n+\n+\t/**\n+\t * Read an entire local file into memory as a byte array.\n+\t *\n+\t * @param path\n+\t *            location of the file to read.\n+\t * @param max\n+\t *            maximum number of bytes to read, if the file is larger than\n+\t *            this limit an IOException is thrown.\n+\t * @return complete contents of the requested local file.\n+\t * @throws FileNotFoundException\n+\t *             the file does not exist.\n+\t * @throws IOException\n+\t *             the file exists, but its contents cannot be read.\n+\t */\n+\tpublic static final byte[] readFully(final File path, final int max)\n+\t\t\tthrows FileNotFoundException, IOException {\n+\t\tfinal FileInputStream in = new FileInputStream(path);\n+\t\ttry {\n+\t\t\tfinal long sz = in.getChannel().size();\n+\t\t\tif (sz > max)\n+\t\t\t\tthrow new IOException(\"File is too large: \" + path);\n+\t\t\tfinal byte[] buf = new byte[(int) sz];\n+\t\t\treadFully(in, buf, 0, buf.length);\n+\t\t\treturn buf;\n+\t\t} finally {\n+\t\t\ttry {\n+\t\t\t\tin.close();\n+\t\t\t} catch (IOException ignored) {\n+\t\t\t\t// ignore any close errors, this was a read only stream\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/**\n \t * Read the entire byte array into memory, or throw an exception.\n \t * \n \t * @param fd\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118406","messageId":"1248207570-13880-3-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-2-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 02/12] Correct name of fileRead member of Config class","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:20Z","receivedAt":"2009-07-21T20:19:20Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The setter method is called setFileRead() so the member should\nhave been called fileRead, not readFile.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |    9 ++++-----\n 1 files changed, 4 insertions(+), 5 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex c2d5c6e..a31796c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -60,8 +60,7 @@\n  * file.\n  */\n public abstract class Config {\n-\n-\tprivate boolean readFile;\n+\tprivate boolean fileRead;\n \n \tprivate List<Entry> entries;\n \n@@ -96,7 +95,7 @@ protected Config(Config base) {\n \t *            true if file does not need loading\n \t */\n \tprotected void setFileRead(boolean ok) {\n-\t\treadFile = ok;\n+\t\tfileRead = ok;\n \t}\n \n \t/**\n@@ -404,7 +403,7 @@ private String getRawString(final String section, final String subsection,\n \t}\n \n \tprivate void ensureLoaded() {\n-\t\tif (!readFile) {\n+\t\tif (!fileRead) {\n \t\t\ttry {\n \t\t\t\tload();\n \t\t\t} catch (FileNotFoundException err) {\n@@ -703,7 +702,7 @@ protected void printConfig(final PrintWriter r) {\n \t */\n \tpublic void load() throws IOException {\n \t\tclear();\n-\t\treadFile = true;\n+\t\tfileRead = true;\n \t\tfinal BufferedReader r = new BufferedReader(new InputStreamReader(\n \t\t\t\topenInputStream(), Constants.CHARSET));\n \t\ttry {\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118405","messageId":"1248207570-13880-4-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-3-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 03/12] Add setLong to Config","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:21Z","receivedAt":"2009-07-21T20:19:21Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We already support getLong(), but didn't support setting one.  The\nsetInt() method can be trivially expressed in terms of setLong().\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |   23 ++++++++++++++++++++\n 1 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex a31796c..6fbdab9 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -449,6 +449,29 @@ private Object getRawEntry(final String section, final String subsection,\n \t */\n \tpublic void setInt(final String section, final String subsection,\n \t\t\tfinal String name, final int value) {\n+\t\tsetLong(section, subsection, name, value);\n+\t}\n+\n+\t/**\n+\t * Add or modify a configuration value. The parameters will result in a\n+\t * configuration entry like this.\n+\t *\n+\t * <pre>\n+\t * [section &quot;subsection&quot;]\n+\t *         name = value\n+\t * </pre>\n+\t *\n+\t * @param section\n+\t *            section name, e.g \"branch\"\n+\t * @param subsection\n+\t *            optional subsection value, e.g. a branch name\n+\t * @param name\n+\t *            parameter name, e.g. \"filemode\"\n+\t * @param value\n+\t *            parameter value\n+\t */\n+\tpublic void setLong(final String section, final String subsection,\n+\t\t\tfinal String name, final long value) {\n \t\tfinal String s;\n \n \t\tif ((value % (1024 * 1024 * 1024)) == 0)\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118404","messageId":"1248207570-13880-5-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-4-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 04/12] Fix Config setInt(..., 0) to store \"0\" not \"0 g\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:22Z","receivedAt":"2009-07-21T20:19:22Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If the value is less than the unit's minimum value, we don't want to\nstore the value with the unit suffix, but instead must try the next\nlower unit's value.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |   24 +++++++++++--------\n 1 files changed, 14 insertions(+), 10 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex 6fbdab9..20f42c4 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -60,6 +60,10 @@\n  * file.\n  */\n public abstract class Config {\n+\tprivate static final long KiB = 1024;\n+\tprivate static final long MiB = 1024 * KiB;\n+\tprivate static final long GiB = 1024 * MiB;\n+\n \tprivate boolean fileRead;\n \n \tprivate List<Entry> entries;\n@@ -230,13 +234,13 @@ public long getLong(final String section, String subsection,\n \t\tlong mul = 1;\n \t\tswitch (Character.toLowerCase(n.charAt(n.length() - 1))) {\n \t\tcase 'g':\n-\t\t\tmul = 1024 * 1024 * 1024;\n+\t\t\tmul = GiB;\n \t\t\tbreak;\n \t\tcase 'm':\n-\t\t\tmul = 1024 * 1024;\n+\t\t\tmul = MiB;\n \t\t\tbreak;\n \t\tcase 'k':\n-\t\t\tmul = 1024;\n+\t\t\tmul = KiB;\n \t\t\tbreak;\n \t\t}\n \t\tif (mul > 1)\n@@ -473,13 +477,13 @@ public void setInt(final String section, final String subsection,\n \tpublic void setLong(final String section, final String subsection,\n \t\t\tfinal String name, final long value) {\n \t\tfinal String s;\n-\n-\t\tif ((value % (1024 * 1024 * 1024)) == 0)\n-\t\t\ts = String.valueOf(value / (1024 * 1024 * 1024)) + \" g\";\n-\t\telse if ((value % (1024 * 1024)) == 0)\n-\t\t\ts = String.valueOf(value / (1024 * 1024)) + \" m\";\n-\t\telse if ((value % 1024) == 0)\n-\t\t\ts = String.valueOf(value / 1024) + \" k\";\n+\t\t\n+\t\tif (value >= GiB && (value % GiB) == 0)\n+\t\t\ts = String.valueOf(value / GiB) + \" g\";\n+\t\telse if (value >= MiB && (value % MiB) == 0)\n+\t\t\ts = String.valueOf(value / MiB) + \" m\";\n+\t\telse if (value >= KiB && (value % KiB) == 0)\n+\t\t\ts = String.valueOf(value / KiB) + \" k\";\n \t\telse\n \t\t\ts = String.valueOf(value);\n \n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118414","messageId":"1248207570-13880-6-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-5-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 05/12] Rename Config.unsetString to just unset()","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:23Z","receivedAt":"2009-07-21T20:19:23Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Removing a configuration option means deleting it from the file,\nimplying a value type during the deletion makes no sense.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |    2 +-\n .../org/spearce/jgit/transport/RemoteConfig.java   |    2 +-\n 2 files changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex 20f42c4..4a60199 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -547,7 +547,7 @@ public void setString(final String section, final String subsection,\n \t * @param name\n \t *            parameter name, e.g. \"filemode\"\n \t */\n-\tpublic void unsetString(final String section, final String subsection,\n+\tpublic void unset(final String section, final String subsection,\n \t\t\tfinal String name) {\n \t\tsetStringList(section, subsection, name, Collections\n \t\t\t\t.<String> emptyList());\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\nindex fac04f6..93a5873 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteConfig.java\n@@ -247,7 +247,7 @@ private void set(final RepositoryConfig rc, final String key,\n \t}\n \n \tprivate void unset(final RepositoryConfig rc, final String key) {\n-\t\trc.unsetString(SECTION, getName(), key);\n+\t\trc.unset(SECTION, getName(), key);\n \t}\n \n \t/**\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118407","messageId":"1248207570-13880-7-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-6-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 06/12] Remove pointless null assignments in Config","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:24Z","receivedAt":"2009-07-21T20:19:24Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The new instance already initialized these fields to null.  We\ndon't set them elsewhere unless we want them to be non-null,\nso we shouldn't set them here either.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |    4 ----\n 1 files changed, 0 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex 4a60199..4940cc2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -639,8 +639,6 @@ else if (values.size() == 1) {\n \t\t\t\t// so we must create a new section header at the end.\n \t\t\t\t//\n \t\t\t\tfinal Entry e = new Entry();\n-\t\t\t\te.prefix = null;\n-\t\t\t\te.suffix = null;\n \t\t\t\te.base = section;\n \t\t\t\te.extendedBase = subsection;\n \t\t\t\tentries.add(e);\n@@ -648,8 +646,6 @@ else if (values.size() == 1) {\n \t\t\t}\n \t\t\twhile (valueIndex < values.size()) {\n \t\t\t\tfinal Entry e = new Entry();\n-\t\t\t\te.prefix = null;\n-\t\t\t\te.suffix = null;\n \t\t\t\te.base = section;\n \t\t\t\te.extendedBase = subsection;\n \t\t\t\te.name = name;\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118410","messageId":"1248207570-13880-8-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-7-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 07/12] Clarify section and subsection values in Config code","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:25Z","receivedAt":"2009-07-21T20:19:25Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"When I first wrote this code I wasn't clear in my intent; the section\nname was sometimes called \"base\" and the subsection name was somtimes\ncalled \"extendedBase\".  This is nuts, everywhere else in our code we\nuse \"section\" and \"subsection\".  Renaming the fields makes the code\nmuch easier to follow.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |   84 ++++++++++----------\n 1 files changed, 43 insertions(+), 41 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex 4940cc2..a2934a2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -374,8 +374,8 @@ public String getString(final String section, String subsection,\n \t\tensureLoaded();\n \n \t\tfor (final Entry e : entries) {\n-\t\t\tif (section.equalsIgnoreCase(e.base) && e.extendedBase != null)\n-\t\t\t\tresult.add(e.extendedBase);\n+\t\t\tif (section.equalsIgnoreCase(e.section) && e.subsection != null)\n+\t\t\t\tresult.add(e.subsection);\n \t\t}\n \t\tif (baseConfig != null)\n \t\t\tresult.addAll(baseConfig.getSubsections(section));\n@@ -582,8 +582,8 @@ public void setStringList(final String section, final String subsection,\n \t\t\tbyName.remove(key);\n \t\telse if (values.size() == 1) {\n \t\t\tfinal Entry e = new Entry();\n-\t\t\te.base = section;\n-\t\t\te.extendedBase = subsection;\n+\t\t\te.section = section;\n+\t\t\te.subsection = subsection;\n \t\t\te.name = name;\n \t\t\te.value = values.get(0);\n \t\t\tbyName.put(key, e);\n@@ -591,8 +591,8 @@ else if (values.size() == 1) {\n \t\t\tfinal ArrayList<Entry> eList = new ArrayList<Entry>(values.size());\n \t\t\tfor (final String v : values) {\n \t\t\t\tfinal Entry e = new Entry();\n-\t\t\t\te.base = section;\n-\t\t\t\te.extendedBase = subsection;\n+\t\t\t\te.section = section;\n+\t\t\t\te.subsection = subsection;\n \t\t\t\te.name = name;\n \t\t\t\te.value = v;\n \t\t\t\teList.add(e);\n@@ -639,15 +639,15 @@ else if (values.size() == 1) {\n \t\t\t\t// so we must create a new section header at the end.\n \t\t\t\t//\n \t\t\t\tfinal Entry e = new Entry();\n-\t\t\t\te.base = section;\n-\t\t\t\te.extendedBase = subsection;\n+\t\t\t\te.section = section;\n+\t\t\t\te.subsection = subsection;\n \t\t\t\tentries.add(e);\n \t\t\t\tinsertPosition = entries.size();\n \t\t\t}\n \t\t\twhile (valueIndex < values.size()) {\n \t\t\t\tfinal Entry e = new Entry();\n-\t\t\t\te.base = section;\n-\t\t\t\te.extendedBase = subsection;\n+\t\t\t\te.section = section;\n+\t\t\t\te.subsection = subsection;\n \t\t\t\te.name = name;\n \t\t\t\te.value = values.get(valueIndex++);\n \t\t\t\tentries.add(insertPosition++, e);\n@@ -686,17 +686,17 @@ protected void printConfig(final PrintWriter r) {\n \t\t\tif (e.prefix != null) {\n \t\t\t\tr.print(e.prefix);\n \t\t\t}\n-\t\t\tif (e.base != null && e.name == null) {\n+\t\t\tif (e.section != null && e.name == null) {\n \t\t\t\tr.print('[');\n-\t\t\t\tr.print(e.base);\n-\t\t\t\tif (e.extendedBase != null) {\n+\t\t\t\tr.print(e.section);\n+\t\t\t\tif (e.subsection != null) {\n \t\t\t\t\tr.print(' ');\n \t\t\t\t\tr.print('\"');\n-\t\t\t\t\tr.print(escapeValue(e.extendedBase));\n+\t\t\t\t\tr.print(escapeValue(e.subsection));\n \t\t\t\t\tr.print('\"');\n \t\t\t\t}\n \t\t\t\tr.print(']');\n-\t\t\t} else if (e.base != null && e.name != null) {\n+\t\t\t} else if (e.section != null && e.name != null) {\n \t\t\t\tif (e.prefix == null || \"\".equals(e.prefix)) {\n \t\t\t\t\tr.print('\\t');\n \t\t\t\t}\n@@ -740,7 +740,7 @@ public void load() throws IOException {\n \t\t\t\t} else if ('\\n' == in) {\n \t\t\t\t\t// End of this entry.\n \t\t\t\t\tadd(e);\n-\t\t\t\t\tif (e.base != null) {\n+\t\t\t\t\tif (e.section != null) {\n \t\t\t\t\t\tlast = e;\n \t\t\t\t\t}\n \t\t\t\t\te = new Entry();\n@@ -750,18 +750,18 @@ public void load() throws IOException {\n \t\t\t\t} else if (';' == in || '#' == in) {\n \t\t\t\t\t// The rest of this line is a comment; put into suffix.\n \t\t\t\t\te.suffix = String.valueOf(in);\n-\t\t\t\t} else if (e.base == null && Character.isWhitespace(in)) {\n+\t\t\t\t} else if (e.section == null && Character.isWhitespace(in)) {\n \t\t\t\t\t// Save the leading whitespace (if any).\n \t\t\t\t\tif (e.prefix == null) {\n \t\t\t\t\t\te.prefix = \"\";\n \t\t\t\t\t}\n \t\t\t\t\te.prefix += in;\n \t\t\t\t} else if ('[' == in) {\n-\t\t\t\t\t// This is a group header line.\n-\t\t\t\t\te.base = readBase(r);\n+\t\t\t\t\t// This is a section header.\n+\t\t\t\t\te.section = readSectionName(r);\n \t\t\t\t\tinput = r.read();\n \t\t\t\t\tif ('\"' == input) {\n-\t\t\t\t\t\te.extendedBase = readValue(r, true, '\"');\n+\t\t\t\t\t\te.subsection = readValue(r, true, '\"');\n \t\t\t\t\t\tinput = r.read();\n \t\t\t\t\t}\n \t\t\t\t\tif (']' != input) {\n@@ -770,10 +770,10 @@ public void load() throws IOException {\n \t\t\t\t\te.suffix = \"\";\n \t\t\t\t} else if (last != null) {\n \t\t\t\t\t// Read a value.\n-\t\t\t\t\te.base = last.base;\n-\t\t\t\t\te.extendedBase = last.extendedBase;\n+\t\t\t\t\te.section = last.section;\n+\t\t\t\t\te.subsection = last.subsection;\n \t\t\t\t\tr.reset();\n-\t\t\t\t\te.name = readName(r);\n+\t\t\t\t\te.name = readKeyName(r);\n \t\t\t\t\tif (e.name.endsWith(\"\\n\")) {\n \t\t\t\t\t\te.name = e.name.substring(0, e.name.length() - 1);\n \t\t\t\t\t\te.value = MAGIC_EMPTY_VALUE;\n@@ -809,11 +809,11 @@ protected void clear() {\n \t@SuppressWarnings(\"unchecked\")\n \tprivate void add(final Entry e) {\n \t\tentries.add(e);\n-\t\tif (e.base != null) {\n-\t\t\tfinal String b = e.base.toLowerCase();\n+\t\tif (e.section != null) {\n+\t\t\tfinal String b = e.section.toLowerCase();\n \t\t\tfinal String group;\n-\t\t\tif (e.extendedBase != null) {\n-\t\t\t\tgroup = b + \".\" + e.extendedBase;\n+\t\t\tif (e.subsection != null) {\n+\t\t\t\tgroup = b + \".\" + e.subsection;\n \t\t\t} else {\n \t\t\t\tgroup = b;\n \t\t\t}\n@@ -835,8 +835,9 @@ private void add(final Entry e) {\n \t\t}\n \t}\n \n-\tprivate static String readBase(final BufferedReader r) throws IOException {\n-\t\tfinal StringBuffer base = new StringBuffer();\n+\tprivate static String readSectionName(final BufferedReader r)\n+\t\t\tthrows IOException {\n+\t\tfinal StringBuffer name = new StringBuffer();\n \t\tfor (;;) {\n \t\t\tr.mark(1);\n \t\t\tint c = r.read();\n@@ -857,22 +858,23 @@ private static String readBase(final BufferedReader r) throws IOException {\n \t\t\t\t\t} else if (' ' == c || '\\t' == c) {\n \t\t\t\t\t\t// Skipped...\n \t\t\t\t\t} else {\n-\t\t\t\t\t\tthrow new IOException(\"Bad base entry. : \" + base + \",\"\n-\t\t\t\t\t\t\t\t+ c);\n+\t\t\t\t\t\tthrow new IOException(\"Bad section entry. : \" + name\n+\t\t\t\t\t\t\t\t+ \",\" + c);\n \t\t\t\t\t}\n \t\t\t\t}\n \t\t\t\tbreak;\n \t\t\t} else if (Character.isLetterOrDigit((char) c) || '.' == c\n \t\t\t\t\t|| '-' == c) {\n-\t\t\t\tbase.append((char) c);\n+\t\t\t\tname.append((char) c);\n \t\t\t} else {\n-\t\t\t\tthrow new IOException(\"Bad base entry. : \" + base + \", \" + c);\n+\t\t\t\tthrow new IOException(\"Bad section entry. : \" + name + \", \" + c);\n \t\t\t}\n \t\t}\n-\t\treturn base.toString();\n+\t\treturn name.toString();\n \t}\n \n-\tprivate static String readName(final BufferedReader r) throws IOException {\n+\tprivate static String readKeyName(final BufferedReader r)\n+\t\t\tthrows IOException {\n \t\tfinal StringBuffer name = new StringBuffer();\n \t\tfor (;;) {\n \t\t\tr.mark(1);\n@@ -1001,12 +1003,12 @@ private static String readValue(final BufferedReader r, boolean quote,\n \t\t/**\n \t\t * The section name for the entry\n \t\t */\n-\t\tString base;\n+\t\tString section;\n \n \t\t/**\n \t\t * Subsection name\n \t\t */\n-\t\tString extendedBase;\n+\t\tString subsection;\n \n \t\t/**\n \t\t * The key name\n@@ -1023,10 +1025,10 @@ private static String readValue(final BufferedReader r, boolean quote,\n \t\t */\n \t\tString suffix;\n \n-\t\tboolean match(final String aBase, final String aExtendedBase,\n-\t\t\t\tfinal String aName) {\n-\t\t\treturn eq(base, aBase) && eq(extendedBase, aExtendedBase)\n-\t\t\t\t\t&& eq(name, aName);\n+\t\tboolean match(final String aSection, final String aSubsection,\n+\t\t\t\tfinal String aKey) {\n+\t\t\treturn eq(section, aSection) && eq(subsection, aSubsection)\n+\t\t\t\t\t&& eq(name, aKey);\n \t\t}\n \n \t\tprivate static boolean eq(final String a, final String b) {\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118415","messageId":"1248207570-13880-9-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-8-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 08/12] Don't subclass PrintWriter when writing the Config","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:26Z","receivedAt":"2009-07-21T20:19:26Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Instead of subclassing PrintWriter to override the println() method\nwe can replace the one println() call we do make with print('\\n'),\nwhich ensures we only produce an LF and never a CRLF file.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |    4 ++--\n .../src/org/spearce/jgit/lib/FileBasedConfig.java  |    7 +------\n 2 files changed, 3 insertions(+), 8 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex a2934a2..8eb2e2a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -677,7 +677,7 @@ private int findSectionEnd(final String section, final String subsection) {\n \t * Print configuration file to the PrintWriter\n \t *\n \t * @param r\n-\t *             the print writer (it must use '\\n' as new line separator).\n+\t *             stream to write the configuration to.\n \t */\n \tprotected void printConfig(final PrintWriter r) {\n \t\tfinal Iterator<Entry> i = entries.iterator();\n@@ -714,7 +714,7 @@ protected void printConfig(final PrintWriter r) {\n \t\t\tif (e.suffix != null) {\n \t\t\t\tr.print(e.suffix);\n \t\t\t}\n-\t\t\tr.println();\n+\t\t\tr.print('\\n');\n \t\t}\n \t}\n \ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/FileBasedConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/FileBasedConfig.java\nindex 75e88f6..aa1dbee 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/FileBasedConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/FileBasedConfig.java\n@@ -80,12 +80,7 @@ public void save() throws IOException {\n \t\t\t\t+ \".lock\");\n \t\tfinal PrintWriter r = new PrintWriter(new BufferedWriter(\n \t\t\t\tnew OutputStreamWriter(new FileOutputStream(tmp),\n-\t\t\t\t\t\tConstants.CHARSET))) {\n-\t\t\t@Override\n-\t\t\tpublic void println() {\n-\t\t\t\tprint('\\n');\n-\t\t\t}\n-\t\t};\n+\t\t\t\t\t\tConstants.CHARSET)));\n \t\tboolean ok = false;\n \t\ttry {\n \t\t\tprintConfig(r);\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118411","messageId":"1248207570-13880-10-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-9-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 09/12] Use a Java 5 style iteration over the Config entries list","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:27Z","receivedAt":"2009-07-21T20:19:27Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |    5 +----\n 1 files changed, 1 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex 8eb2e2a..e379c37 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -50,7 +50,6 @@\n import java.util.Collections;\n import java.util.HashMap;\n import java.util.HashSet;\n-import java.util.Iterator;\n import java.util.List;\n import java.util.Map;\n import java.util.Set;\n@@ -680,9 +679,7 @@ private int findSectionEnd(final String section, final String subsection) {\n \t *             stream to write the configuration to.\n \t */\n \tprotected void printConfig(final PrintWriter r) {\n-\t\tfinal Iterator<Entry> i = entries.iterator();\n-\t\twhile (i.hasNext()) {\n-\t\t\tfinal Entry e = i.next();\n+\t\tfor (final Entry e : entries) {\n \t\t\tif (e.prefix != null) {\n \t\t\t\tr.print(e.prefix);\n \t\t\t}\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118413","messageId":"1248207570-13880-11-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-10-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 10/12] Match config subsection names using case sensitive search","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:28Z","receivedAt":"2009-07-21T20:19:28Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The subsection name is case sensitive, and should be matched as such.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |   19 ++++++++++++++-----\n 1 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex e379c37..974ffea 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -4,6 +4,7 @@\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n  * Copyright (C) 2008, Thad Hughes <thadh@thad.corp.google.com>\n  * Copyright (C) 2009, JetBrains s.r.o.\n+ * Copyright (C) 2009, Google, Inc.\n  *\n  * All rights reserved.\n  *\n@@ -1024,17 +1025,25 @@ private static String readValue(final BufferedReader r, boolean quote,\n \n \t\tboolean match(final String aSection, final String aSubsection,\n \t\t\t\tfinal String aKey) {\n-\t\t\treturn eq(section, aSection) && eq(subsection, aSubsection)\n-\t\t\t\t\t&& eq(name, aKey);\n+\t\t\treturn eqIgnoreCase(section, aSection)\n+\t\t\t\t\t&& eqSameCase(subsection, aSubsection)\n+\t\t\t\t\t&& eqIgnoreCase(name, aKey);\n \t\t}\n \n-\t\tprivate static boolean eq(final String a, final String b) {\n+\t\tprivate static boolean eqIgnoreCase(final String a, final String b) {\n \t\t\tif (a == null && b == null)\n \t\t\t\treturn true;\n \t\t\tif (a == null || b == null)\n \t\t\t\treturn false;\n \t\t\treturn a.equalsIgnoreCase(b);\n \t\t}\n-\t}\n \n-}\n\\ No newline at end of file\n+\t\tprivate static boolean eqSameCase(final String a, final String b) {\n+\t\t\tif (a == null && b == null)\n+\t\t\t\treturn true;\n+\t\t\tif (a == null || b == null)\n+\t\t\t\treturn false;\n+\t\t\treturn a.equals(b);\n+\t\t}\n+\t}\n+}\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118412","messageId":"1248207570-13880-12-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-11-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 11/12] Cleanup Config's MAGIC_EMPTY_VALUE to be more safe","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:29Z","receivedAt":"2009-07-21T20:19:29Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The magic value \"%%magic%%empty%%\" is just too magic; if it ever\ndid appear as a value in a key string Config would have treated\nit as a true value instead of as a string value.  We also had to\nspecial case conversions of it to the empty string in a string\ncontext.  Instead we create a special String object using the\nempty string as a template, and use reference equality against\nthat to indicate the magic empty value.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/Config.java           |   28 +++++++++----------\n 1 files changed, 13 insertions(+), 15 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex 974ffea..e4528b1 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -76,9 +76,14 @@\n \tprivate Map<String, Object> byName;\n \n \t/**\n-\t * Magic value indicating a missing entry\n+\t * Magic value indicating a missing entry.\n+\t * <p>\n+\t * This value is tested for reference equality in some contexts, so we\n+\t * must ensure it is a special copy of the empty string.  It also must\n+\t * be treated like the empty string.\n \t */\n-\tprivate static final String MAGIC_EMPTY_VALUE = \"%%magic%%empty%%\";\n+\tprivate static final String MAGIC_EMPTY_VALUE = new StringBuilder(0)\n+\t\t\t.toString();\n \n \t/**\n \t * The constructor for configuration file\n@@ -293,7 +298,7 @@ public boolean getBoolean(final String section, String subsection,\n \t\tif (n == null)\n \t\t\treturn defaultValue;\n \n-\t\tif (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equalsIgnoreCase(n)\n+\t\tif (MAGIC_EMPTY_VALUE == n || \"yes\".equalsIgnoreCase(n)\n \t\t\t\t|| \"true\".equalsIgnoreCase(n) || \"1\".equals(n)\n \t\t\t\t|| \"on\".equalsIgnoreCase(n)) {\n \t\t\treturn true;\n@@ -321,11 +326,7 @@ public boolean getBoolean(final String section, String subsection,\n \t */\n \tpublic String getString(final String section, String subsection,\n \t\t\tfinal String name) {\n-\t\tString val = getRawString(section, subsection, name);\n-\t\tif (MAGIC_EMPTY_VALUE.equals(val)) {\n-\t\t\treturn \"\";\n-\t\t}\n-\t\treturn val;\n+\t\treturn getRawString(section, subsection, name);\n \t}\n \n \t/**\n@@ -345,16 +346,13 @@ public String getString(final String section, String subsection,\n \t\tif (o instanceof List) {\n \t\t\tfinal List lst = (List) o;\n \t\t\tfinal String[] r = new String[lst.size()];\n-\t\t\tfor (int i = 0; i < r.length; i++) {\n-\t\t\t\tfinal String val = ((Entry) lst.get(i)).value;\n-\t\t\t\tr[i] = MAGIC_EMPTY_VALUE.equals(val) ? \"\" : val;\n-\t\t\t}\n+\t\t\tfor (int i = 0; i < r.length; i++)\n+\t\t\t\tr[i] = ((Entry) lst.get(i)).value;\n \t\t\treturn r;\n \t\t}\n \n \t\tif (o instanceof Entry) {\n-\t\t\tfinal String val = ((Entry) o).value;\n-\t\t\treturn new String[] { MAGIC_EMPTY_VALUE.equals(val) ? \"\" : val };\n+\t\t\treturn new String[] { ((Entry) o).value };\n \t\t}\n \n \t\tif (baseConfig != null)\n@@ -700,7 +698,7 @@ protected void printConfig(final PrintWriter r) {\n \t\t\t\t}\n \t\t\t\tr.print(e.name);\n \t\t\t\tif (e.value != null) {\n-\t\t\t\t\tif (!MAGIC_EMPTY_VALUE.equals(e.value)) {\n+\t\t\t\t\tif (MAGIC_EMPTY_VALUE != e.value) {\n \t\t\t\t\t\tr.print(\" = \");\n \t\t\t\t\t\tr.print(escapeValue(e.value));\n \t\t\t\t\t}\n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118408","messageId":"1248207570-13880-13-git-send-email-spearce@spearce.org","threadId":"20186","inReplyTo":"1248207570-13880-12-git-send-email-spearce@spearce.org","subject":"[JGIT PATCH 12/12] Remove unreferenced REMOTE_SECTION from RepositoryConfig","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T20:19:30Z","receivedAt":"2009-07-21T20:19:30Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"All callers should be using a RemoteConfig instead.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/RepositoryConfig.java |    4 ----\n 1 files changed, 0 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\nindex 9d2c9a3..85e8738 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n@@ -66,10 +66,6 @@ public static RepositoryConfig openUserConfig() {\n \t\treturn systemReader.openUserConfig();\n \t}\n \n-\n-\t/** Section name for a remote configuration */\n-\tpublic static final String REMOTE_SECTION = \"remote\";\n-\n \t/** Section name for a branch configuration. */\n \tpublic static final String BRANCH_SECTION = \"branch\";\n \n-- \n1.6.4.rc1.186.g60aa0c\n"},{"id":"118431","messageId":"200907212351.21760.robin.rosenberg@dewire.com","threadId":"20186","inReplyTo":"1248207570-13880-12-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH 11/12] Cleanup Config's MAGIC_EMPTY_VALUE to be more safe","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-07-21T21:51:21Z","receivedAt":"2009-07-21T21:51:21Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"tisdag 21 juli 2009 22:19:29 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> The magic value \"%%magic%%empty%%\" is just too magic; if it ever\n> did appear as a value in a key string Config would have treated\n> it as a true value instead of as a string value.  We also had to\n> special case conversions of it to the empty string in a string\n> context.  Instead we create a special String object using the\n> empty string as a template, and use reference equality against\n> that to indicate the magic empty value.\n> \n> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n> ---\n>  .../src/org/spearce/jgit/lib/Config.java           |   28 +++++++++----------\n>  1 files changed, 13 insertions(+), 15 deletions(-)\n> \n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n> index 974ffea..e4528b1 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n> @@ -76,9 +76,14 @@\n>  \tprivate Map<String, Object> byName;\n>  \n>  \t/**\n> -\t * Magic value indicating a missing entry\n> +\t * Magic value indicating a missing entry.\n> +\t * <p>\n> +\t * This value is tested for reference equality in some contexts, so we\n> +\t * must ensure it is a special copy of the empty string.  It also must\n> +\t * be treated like the empty string.\n>  \t */\n> -\tprivate static final String MAGIC_EMPTY_VALUE = \"%%magic%%empty%%\";\n> +\tprivate static final String MAGIC_EMPTY_VALUE = new StringBuilder(0)\n> +\t\t\t.toString();\n\nCan we be sure an implementation doesn't \"optimize\" toString() here? But an\nexplicit new String() shouldn't be..?\n\n-- robin\n\n\n\n>  \t/**\n>  \t * The constructor for configuration file\n> @@ -293,7 +298,7 @@ public boolean getBoolean(final String section, String subsection,\n>  \t\tif (n == null)\n>  \t\t\treturn defaultValue;\n>  \n> -\t\tif (MAGIC_EMPTY_VALUE.equals(n) || \"yes\".equalsIgnoreCase(n)\n> +\t\tif (MAGIC_EMPTY_VALUE == n || \"yes\".equalsIgnoreCase(n)\n>  \t\t\t\t|| \"true\".equalsIgnoreCase(n) || \"1\".equals(n)\n>  \t\t\t\t|| \"on\".equalsIgnoreCase(n)) {\n>  \t\t\treturn true;\n> @@ -321,11 +326,7 @@ public boolean getBoolean(final String section, String subsection,\n>  \t */\n>  \tpublic String getString(final String section, String subsection,\n>  \t\t\tfinal String name) {\n> -\t\tString val = getRawString(section, subsection, name);\n> -\t\tif (MAGIC_EMPTY_VALUE.equals(val)) {\n> -\t\t\treturn \"\";\n> -\t\t}\n> -\t\treturn val;\n> +\t\treturn getRawString(section, subsection, name);\n>  \t}\n>  \n>  \t/**\n> @@ -345,16 +346,13 @@ public String getString(final String section, String subsection,\n>  \t\tif (o instanceof List) {\n>  \t\t\tfinal List lst = (List) o;\n>  \t\t\tfinal String[] r = new String[lst.size()];\n> -\t\t\tfor (int i = 0; i < r.length; i++) {\n> -\t\t\t\tfinal String val = ((Entry) lst.get(i)).value;\n> -\t\t\t\tr[i] = MAGIC_EMPTY_VALUE.equals(val) ? \"\" : val;\n> -\t\t\t}\n> +\t\t\tfor (int i = 0; i < r.length; i++)\n> +\t\t\t\tr[i] = ((Entry) lst.get(i)).value;\n>  \t\t\treturn r;\n>  \t\t}\n>  \n>  \t\tif (o instanceof Entry) {\n> -\t\t\tfinal String val = ((Entry) o).value;\n> -\t\t\treturn new String[] { MAGIC_EMPTY_VALUE.equals(val) ? \"\" : val };\n> +\t\t\treturn new String[] { ((Entry) o).value };\n>  \t\t}\n>  \n>  \t\tif (baseConfig != null)\n> @@ -700,7 +698,7 @@ protected void printConfig(final PrintWriter r) {\n>  \t\t\t\t}\n>  \t\t\t\tr.print(e.name);\n>  \t\t\t\tif (e.value != null) {\n> -\t\t\t\t\tif (!MAGIC_EMPTY_VALUE.equals(e.value)) {\n> +\t\t\t\t\tif (MAGIC_EMPTY_VALUE != e.value) {\n>  \t\t\t\t\t\tr.print(\" = \");\n>  \t\t\t\t\t\tr.print(escapeValue(e.value));\n>  \t\t\t\t\t}\n"},{"id":"118432","messageId":"20090721215451.GU11191@spearce.org","threadId":"20186","inReplyTo":"200907212351.21760.robin.rosenberg@dewire.com","subject":"Re: [JGIT PATCH 11/12] Cleanup Config's MAGIC_EMPTY_VALUE to be more safe","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-21T21:54:51Z","receivedAt":"2009-07-21T21:54:51Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> tisdag 21 juli 2009 22:19:29 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> > The magic value \"%%magic%%empty%%\" is just too magic; \n...\n> > -\tprivate static final String MAGIC_EMPTY_VALUE = \"%%magic%%empty%%\";\n> > +\tprivate static final String MAGIC_EMPTY_VALUE = new StringBuilder(0)\n> > +\t\t\t.toString();\n> \n> Can we be sure an implementation doesn't \"optimize\" toString() here? But an\n> explicit new String() shouldn't be..?\n\n*sigh*\n\nI used this form because FindBugs dislikes new String(\"\").  But yea,\nat least with new String() we know its a new string object and\nthere is no pooling of the empty string going on under the covers.\n\nI can't imagine it being a common enough case to convert a\nStringBuilder to an empty string that a runtime implementation would\nwaste instructions over checking for it and reusing a pooled copy,\nbut eh, yea, maybe this should be new String(\"\") and include a\nFindBugs entry to disable the warning here.\n\n-- \nShawn.\n"},{"id":"118457","messageId":"85647ef50907220411w356000bcuda21e9318eab094@mail.gmail.com","threadId":"20186","inReplyTo":"1248207570-13880-11-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH 10/12] Match config subsection names using case sensitive search","fromName":"Constantine Plotnikov","fromEmail":"constantine.plotnikov@gmail.com","sentAt":"2009-07-22T11:11:07Z","receivedAt":"2009-07-22T11:11:07Z","isPatch":true,"sender":{"key":"constantine.plotnikov@gmail.com","avatar":null},"body":"This patch is incomplete. The method getRawEntry(...) and\nsetStringList(...) should be fixed as part of this patch too. There is\nsubsection is converted to lowercase. I was planning to submit it as\nseparate patch.\n\nAlso I'm somewhat bothered by usage of toLowerCase() without locale\nspecified and equalsIgnoreCase(). When turkish locale is default one\nthere could be surprising results with the letter \"I\".  The program:\n\nimport java.util.Locale;\npublic class Test {\n\tpublic static void main(String[] args) {\n\t\tLocale tr_TR = new Locale(\"tr\", \"TR\");\n\t\tSystem.out.printf(\"i = U+%04x LC(I, tr_TR) = U+%04x\\n\", (int)'i',\n(int)\"I\".toLowerCase(tr_TR).charAt(0));\n\t\tSystem.out.printf(\"I = U+%04x UC(i, tr_TR) = U+%04x\\n\", (int)'I',\n(int)\"i\".toUpperCase(tr_TR).charAt(0));\n\t}\n}\n\nGives the following output:\n\ni = U+0069 LC(I, tr_TR) = U+0131\nI = U+0049 UC(i, tr_TR) = U+0130\n\nSo I suggest to explicitly use Locale.US for all toLowerCase()\ninvocation in Config class just in case and to replace\nequalsIgnoreCase() with something else. But this possibly should be\nsome other patch series. I do not know what C git doing in case\nturkish locale and whether it is a bug or \"feature\".\n\nRegards,\nConstantine\n\nOn Wed, Jul 22, 2009 at 12:19 AM, Shawn O. Pearce<spearce@spearce.org> wrote:\n> The subsection name is case sensitive, and should be matched as such.\n>\n> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n> ---\n>  .../src/org/spearce/jgit/lib/Config.java           |   19 ++++++++++++++-----\n>  1 files changed, 14 insertions(+), 5 deletions(-)\n>\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n> index e379c37..974ffea 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n> @@ -4,6 +4,7 @@\n>  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n>  * Copyright (C) 2008, Thad Hughes <thadh@thad.corp.google.com>\n>  * Copyright (C) 2009, JetBrains s.r.o.\n> + * Copyright (C) 2009, Google, Inc.\n>  *\n>  * All rights reserved.\n>  *\n> @@ -1024,17 +1025,25 @@ private static String readValue(final BufferedReader r, boolean quote,\n>\n>                boolean match(final String aSection, final String aSubsection,\n>                                final String aKey) {\n> -                       return eq(section, aSection) && eq(subsection, aSubsection)\n> -                                       && eq(name, aKey);\n> +                       return eqIgnoreCase(section, aSection)\n> +                                       && eqSameCase(subsection, aSubsection)\n> +                                       && eqIgnoreCase(name, aKey);\n>                }\n>\n> -               private static boolean eq(final String a, final String b) {\n> +               private static boolean eqIgnoreCase(final String a, final String b) {\n>                        if (a == null && b == null)\n>                                return true;\n>                        if (a == null || b == null)\n>                                return false;\n>                        return a.equalsIgnoreCase(b);\n>                }\n> -       }\n>\n> -}\n> \\ No newline at end of file\n> +               private static boolean eqSameCase(final String a, final String b) {\n> +                       if (a == null && b == null)\n> +                               return true;\n> +                       if (a == null || b == null)\n> +                               return false;\n> +                       return a.equals(b);\n> +               }\n> +       }\n> +}\n> --\n> 1.6.4.rc1.186.g60aa0c\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n>\n"},{"id":"118512","messageId":"200907222337.56238.robin.rosenberg@dewire.com","threadId":"20186","inReplyTo":"85647ef50907220411w356000bcuda21e9318eab094@mail.gmail.com","subject":"Re: [JGIT PATCH 10/12] Match config subsection names using case sensitive search","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-07-22T21:37:55Z","receivedAt":"2009-07-22T21:37:55Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"onsdag 22 juli 2009 13:11:07 skrev Constantine Plotnikov <constantine.plotnikov@gmail.com>:\n> This patch is incomplete. The method getRawEntry(...) and\n> setStringList(...) should be fixed as part of this patch too. There is\n> subsection is converted to lowercase. I was planning to submit it as\n> separate patch.\n> \n> Also I'm somewhat bothered by usage of toLowerCase() without locale\n> specified and equalsIgnoreCase(). When turkish locale is default one\n> there could be surprising results with the letter \"I\".  The program:\n> \n> import java.util.Locale;\n> public class Test {\n> \tpublic static void main(String[] args) {\n> \t\tLocale tr_TR = new Locale(\"tr\", \"TR\");\n> \t\tSystem.out.printf(\"i = U+%04x LC(I, tr_TR) = U+%04x\\n\", (int)'i',\n> (int)\"I\".toLowerCase(tr_TR).charAt(0));\n> \t\tSystem.out.printf(\"I = U+%04x UC(i, tr_TR) = U+%04x\\n\", (int)'I',\n> (int)\"i\".toUpperCase(tr_TR).charAt(0));\n> \t}\n> }\n> \n> Gives the following output:\n> \n> i = U+0069 LC(I, tr_TR) = U+0131\n> I = U+0049 UC(i, tr_TR) = U+0130\n> \n> So I suggest to explicitly use Locale.US for all toLowerCase()\n> invocation in Config class just in case and to replace\n> equalsIgnoreCase() with something else. But this possibly should be\n> some other patch series. I do not know what C git doing in case\n> turkish locale and whether it is a bug or \"feature\".\n\nGood catch. C Git operates in \"C\" locale (default for C/C++) so it should\nnot be a problem. We might have other places where this could be a problem.\nUsing Locale.US should be the right choice here since unicode is not allowed\nin sections and keys anyway.  (Btw, java.lang.String.regionMatches has some info\non case insensitive matching for those interested, serving a reminder for\nwhy home-brewed logic isn't always the best.)\n\nAs you say we should make this a separate patch.\n\n--  robin\n"},{"id":"118660","messageId":"20090724213416.GX11191@spearce.org","threadId":"20186","inReplyTo":"200907222337.56238.robin.rosenberg@dewire.com","subject":"[PATCH] Ensure Config readers handle case insensitive names correctly","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-07-24T21:34:16Z","receivedAt":"2009-07-24T21:34:16Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"In some locales (e.g. Turkish) String toLowerCase() and\nequalsIgnoreCase() can produce different results.  For example,\nin that locale:\n\n  i = U+0069   LC(I, tr_TR) = U+0131\n  I = U+0049   UC(i, tr_TR) = U+0130\n\nC Git and network protocols prefer to operate in the \"C\" locale,\nwhich does not translate to different codepoints like this, but\ninstead stays within the US-ASCII character set, U+0069 and U+0049.\nWhen reading a Git configuration file in a Turkish locale we should\nhonor the US-ASCII interpretation as C Git would, rather than the\nnative Turkish locale's rules.\n\nBy using our own toLowerCase and equalsIgnoreCase implementations we\ncan ensure we always use the US-ASCII (aka \"C\" locale) definition of\nthis 'i' <-> 'I' translation.  A custom table based translation is\nused instead of relying on Locale.US to avoid accidental translation\nof codepoints > U+007f, as these are not typically translated in the\n\"C\" locale but might still be translated in Java's en_US locale.\n\nCC: Constantine Plotnikov <constantine.plotnikov@gmail.com>\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n > onsdag 22 juli 2009 13:11:07 skrev Constantine Plotnikov <constantine.plotnikov@gmail.com>:\n > > Also I'm somewhat bothered by usage of toLowerCase() without locale\n > > specified and equalsIgnoreCase(). When turkish locale is default one\n > > there could be surprising results with the letter \"I\".  The program:\n ...\n > Good catch.\n ...\n > As you say we should make this a separate patch.\n \n And here is a patch!  :-)\n \n .../tst/org/spearce/jgit/util/StringUtilsTest.java |   78 +++++++++++++\n .../findBugs/FindBugsExcludeFilter.xml             |   18 +++-\n .../src/org/spearce/jgit/lib/Config.java           |   25 +++--\n .../src/org/spearce/jgit/transport/AmazonS3.java   |   15 ++--\n .../org/spearce/jgit/transport/OpenSshConfig.java  |   19 ++--\n .../src/org/spearce/jgit/util/FS_Win32.java        |    2 +-\n .../org/spearce/jgit/util/RawSubStringPattern.java |    2 +-\n .../src/org/spearce/jgit/util/StringUtils.java     |  115 ++++++++++++++++++++\n 8 files changed, 243 insertions(+), 31 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/util/StringUtilsTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/util/StringUtils.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/util/StringUtilsTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/util/StringUtilsTest.java\nnew file mode 100644\nindex 0000000..0710844\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/util/StringUtilsTest.java\n@@ -0,0 +1,78 @@\n+/*\n+ * Copyright (C) 2009, Google Inc.\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.util;\n+\n+import junit.framework.TestCase;\n+\n+public class StringUtilsTest extends TestCase {\n+\tpublic void testToLowerCaseChar() {\n+\t\tassertEquals('a', StringUtils.toLowerCase('A'));\n+\t\tassertEquals('z', StringUtils.toLowerCase('Z'));\n+\n+\t\tassertEquals('a', StringUtils.toLowerCase('a'));\n+\t\tassertEquals('z', StringUtils.toLowerCase('z'));\n+\n+\t\tassertEquals((char) 0, StringUtils.toLowerCase((char) 0));\n+\t\tassertEquals((char) 0xffff, StringUtils.toLowerCase((char) 0xffff));\n+\t}\n+\n+\tpublic void testToLowerCaseString() {\n+\t\tassertEquals(\"\\n abcdefghijklmnopqrstuvwxyz\\n\", StringUtils\n+\t\t\t\t.toLowerCase(\"\\n ABCDEFGHIJKLMNOPQRSTUVWXYZ\\n\"));\n+\t}\n+\n+\tpublic void testEqualsIgnoreCase1() {\n+\t\tfinal String a = \"FOO\";\n+\t\tassertTrue(StringUtils.equalsIgnoreCase(a, a));\n+\t}\n+\n+\tpublic void testEqualsIgnoreCase2() {\n+\t\tassertFalse(StringUtils.equalsIgnoreCase(\"a\", \"\"));\n+\t}\n+\n+\tpublic void testEqualsIgnoreCase3() {\n+\t\tassertFalse(StringUtils.equalsIgnoreCase(\"a\", \"b\"));\n+\t\tassertFalse(StringUtils.equalsIgnoreCase(\"ac\", \"ab\"));\n+\t}\n+\n+\tpublic void testEqualsIgnoreCase4() {\n+\t\tassertTrue(StringUtils.equalsIgnoreCase(\"a\", \"a\"));\n+\t\tassertTrue(StringUtils.equalsIgnoreCase(\"A\", \"a\"));\n+\t\tassertTrue(StringUtils.equalsIgnoreCase(\"a\", \"A\"));\n+\t}\n+}\ndiff --git a/org.spearce.jgit/findBugs/FindBugsExcludeFilter.xml b/org.spearce.jgit/findBugs/FindBugsExcludeFilter.xml\nindex 598197c..6809568 100644\n--- a/org.spearce.jgit/findBugs/FindBugsExcludeFilter.xml\n+++ b/org.spearce.jgit/findBugs/FindBugsExcludeFilter.xml\n@@ -8,10 +8,20 @@\n        <Method name=\"mmap\" />\n        <Bug pattern=\"DM_GC\" />\n      </Match>\n-\t <!-- Silence the construction of our magic String instance. \n-\t  -->\n+\n+\t   <!-- Silence the construction of our magic String instance. \n+\t     -->\n      <Match>\n-     \t<Class name=\"org.spearce.jgit.lib.Config\" />\n-     \t<Bug pattern=\"DM_STRING_VOID_CTOR\"/>\n+     \t <Class name=\"org.spearce.jgit.lib.Config\" />\n+     \t <Bug pattern=\"DM_STRING_VOID_CTOR\"/>\n+     </Match>\n+\n+     <!-- Silence comparison of string by == or !=.  This class is built\n+          only to provide compare of string values, we won't make a mistake\n+          here with == assuming .equals() style equality.\n+       -->\n+     <Match>\n+       <Class name=\"org.spearce.jgit.lib.util.StringUtils\" />\n+       <Bug pattern=\"ES_COMPARING_PARAMETER_STRING_WITH_EQ\" />\n      </Match>\n </FindBugsFilter>\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\nindex fc1a2a3..258dba5 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Config.java\n@@ -55,6 +55,8 @@\n import java.util.Map;\n import java.util.Set;\n \n+import org.spearce.jgit.util.StringUtils;\n+\n /**\n  * The configuration file stored in the format similar to the \".git/config\"\n  * file.\n@@ -236,7 +238,7 @@ public long getLong(final String section, String subsection,\n \t\t\treturn defaultValue;\n \n \t\tlong mul = 1;\n-\t\tswitch (Character.toLowerCase(n.charAt(n.length() - 1))) {\n+\t\tswitch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) {\n \t\tcase 'g':\n \t\t\tmul = GiB;\n \t\t\tbreak;\n@@ -297,13 +299,16 @@ public boolean getBoolean(final String section, String subsection,\n \t\tif (n == null)\n \t\t\treturn defaultValue;\n \n-\t\tif (MAGIC_EMPTY_VALUE == n || \"yes\".equalsIgnoreCase(n)\n-\t\t\t\t|| \"true\".equalsIgnoreCase(n) || \"1\".equals(n)\n-\t\t\t\t|| \"on\".equalsIgnoreCase(n)) {\n+\t\tif (MAGIC_EMPTY_VALUE == n || StringUtils.equalsIgnoreCase(\"yes\", n)\n+\t\t\t\t|| StringUtils.equalsIgnoreCase(\"true\", n)\n+\t\t\t\t|| StringUtils.equalsIgnoreCase(\"1\", n)\n+\t\t\t\t|| StringUtils.equalsIgnoreCase(\"on\", n)) {\n \t\t\treturn true;\n \n-\t\t} else if (\"no\".equalsIgnoreCase(n) || \"false\".equalsIgnoreCase(n)\n-\t\t\t\t|| \"0\".equals(n) || \"off\".equalsIgnoreCase(n)) {\n+\t\t} else if (StringUtils.equalsIgnoreCase(\"no\", n)\n+\t\t\t\t|| StringUtils.equalsIgnoreCase(\"false\", n)\n+\t\t\t\t|| StringUtils.equalsIgnoreCase(\"0\", n)\n+\t\t\t\t|| StringUtils.equalsIgnoreCase(\"off\", n)) {\n \t\t\treturn false;\n \n \t\t} else {\n@@ -371,7 +376,8 @@ public String getString(final String section, String subsection,\n \t\tensureLoaded();\n \n \t\tfor (final Entry e : entries) {\n-\t\t\tif (section.equalsIgnoreCase(e.section) && e.subsection != null)\n+\t\t\tif (StringUtils.equalsIgnoreCase(section, e.section)\n+\t\t\t\t\t&& e.subsection != null)\n \t\t\t\tresult.add(e.subsection);\n \t\t}\n \t\tif (baseConfig != null)\n@@ -441,7 +447,8 @@ private static String concatenateKey(final String section,\n \t\t\tss = \".\" + subsection;\n \t\telse\n \t\t\tss = \"\";\n-\t\treturn section.toLowerCase() + ss + \".\" + name.toLowerCase();\n+\t\treturn StringUtils.toLowerCase(section) + ss + \".\"\n+\t\t\t\t+ StringUtils.toLowerCase(name);\n \t}\n \n \t/**\n@@ -1033,7 +1040,7 @@ private static boolean eqIgnoreCase(final String a, final String b) {\n \t\t\t\treturn true;\n \t\t\tif (a == null || b == null)\n \t\t\t\treturn false;\n-\t\t\treturn a.equalsIgnoreCase(b);\n+\t\t\treturn StringUtils.equalsIgnoreCase(a, b);\n \t\t}\n \n \t\tprivate static boolean eqSameCase(final String a, final String b) {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java b/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java\nindex 3d8bdca..d27f37d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/AmazonS3.java\n@@ -79,6 +79,7 @@\n import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.util.Base64;\n import org.spearce.jgit.util.HttpSupport;\n+import org.spearce.jgit.util.StringUtils;\n import org.spearce.jgit.util.TemporaryBuffer;\n import org.xml.sax.Attributes;\n import org.xml.sax.InputSource;\n@@ -122,7 +123,7 @@\n \t}\n \n \tprivate static boolean isSignedHeader(final String name) {\n-\t\tfinal String nameLC = name.toLowerCase();\n+\t\tfinal String nameLC = StringUtils.toLowerCase(name);\n \t\treturn SIGNED_HEADERS.contains(nameLC) || nameLC.startsWith(\"x-amz-\");\n \t}\n \n@@ -214,13 +215,13 @@ public AmazonS3(final Properties props) {\n \t\tprivateKey = new SecretKeySpec(Constants.encodeASCII(secret), HMAC);\n \n \t\tfinal String pacl = props.getProperty(\"acl\", \"PRIVATE\");\n-\t\tif (\"PRIVATE\".equalsIgnoreCase(pacl))\n+\t\tif (StringUtils.equalsIgnoreCase(\"PRIVATE\", pacl))\n \t\t\tacl = \"private\";\n-\t\telse if (\"PUBLIC\".equalsIgnoreCase(pacl))\n+\t\telse if (StringUtils.equalsIgnoreCase(\"PUBLIC\", pacl))\n \t\t\tacl = \"public-read\";\n-\t\telse if (\"PUBLIC-READ\".equalsIgnoreCase(pacl))\n+\t\telse if (StringUtils.equalsIgnoreCase(\"PUBLIC-READ\", pacl))\n \t\t\tacl = \"public-read\";\n-\t\telse if (\"PUBLIC_READ\".equalsIgnoreCase(pacl))\n+\t\telse if (StringUtils.equalsIgnoreCase(\"PUBLIC_READ\", pacl))\n \t\t\tacl = \"public-read\";\n \t\telse\n \t\t\tthrow new IllegalArgumentException(\"Invalid acl: \" + pacl);\n@@ -575,7 +576,7 @@ private void authorize(final HttpURLConnection c) throws IOException {\n \t\tfor (final Map.Entry<String, List<String>> entry : reqHdr.entrySet()) {\n \t\t\tfinal String hdr = entry.getKey();\n \t\t\tif (isSignedHeader(hdr))\n-\t\t\t\tsigHdr.put(hdr.toLowerCase(), toCleanString(entry.getValue()));\n+\t\t\t\tsigHdr.put(StringUtils.toLowerCase(hdr), toCleanString(entry.getValue()));\n \t\t}\n \n \t\tfinal StringBuilder s = new StringBuilder();\n@@ -781,7 +782,7 @@ public void endElement(final String uri, final String name,\n \t\t\tif (\"Key\".equals(name))\n \t\t\t\tentries.add(data.toString().substring(prefix.length()));\n \t\t\telse if (\"IsTruncated\".equals(name))\n-\t\t\t\ttruncated = \"true\".equalsIgnoreCase(data.toString());\n+\t\t\t\ttruncated = StringUtils.equalsIgnoreCase(\"true\", data.toString());\n \t\t\tdata = null;\n \t\t}\n \t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java b/org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java\nindex 92a1bfc..acd1a45 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/OpenSshConfig.java\n@@ -55,6 +55,7 @@\n import org.spearce.jgit.errors.InvalidPatternException;\n import org.spearce.jgit.fnmatch.FileNameMatcher;\n import org.spearce.jgit.util.FS;\n+import org.spearce.jgit.util.StringUtils;\n \n /**\n  * Simple configuration parser for the OpenSSH ~/.ssh/config file.\n@@ -176,7 +177,7 @@ public Host lookup(final String hostName) {\n \t\t\tfinal String keyword = parts[0].trim();\n \t\t\tfinal String argValue = parts[1].trim();\n \n-\t\t\tif (\"Host\".equalsIgnoreCase(keyword)) {\n+\t\t\tif (StringUtils.equalsIgnoreCase(\"Host\", keyword)) {\n \t\t\t\tcurrent.clear();\n \t\t\t\tfor (final String pattern : argValue.split(\"[ \\t]\")) {\n \t\t\t\t\tfinal String name = dequote(pattern);\n@@ -197,15 +198,15 @@ public Host lookup(final String hostName) {\n \t\t\t\tcontinue;\n \t\t\t}\n \n-\t\t\tif (\"HostName\".equalsIgnoreCase(keyword)) {\n+\t\t\tif (StringUtils.equalsIgnoreCase(\"HostName\", keyword)) {\n \t\t\t\tfor (final Host c : current)\n \t\t\t\t\tif (c.hostName == null)\n \t\t\t\t\t\tc.hostName = dequote(argValue);\n-\t\t\t} else if (\"User\".equalsIgnoreCase(keyword)) {\n+\t\t\t} else if (StringUtils.equalsIgnoreCase(\"User\", keyword)) {\n \t\t\t\tfor (final Host c : current)\n \t\t\t\t\tif (c.user == null)\n \t\t\t\t\t\tc.user = dequote(argValue);\n-\t\t\t} else if (\"Port\".equalsIgnoreCase(keyword)) {\n+\t\t\t} else if (StringUtils.equalsIgnoreCase(\"Port\", keyword)) {\n \t\t\t\ttry {\n \t\t\t\t\tfinal int port = Integer.parseInt(dequote(argValue));\n \t\t\t\t\tfor (final Host c : current)\n@@ -214,19 +215,19 @@ public Host lookup(final String hostName) {\n \t\t\t\t} catch (NumberFormatException nfe) {\n \t\t\t\t\t// Bad port number. Don't set it.\n \t\t\t\t}\n-\t\t\t} else if (\"IdentityFile\".equalsIgnoreCase(keyword)) {\n+\t\t\t} else if (StringUtils.equalsIgnoreCase(\"IdentityFile\", keyword)) {\n \t\t\t\tfor (final Host c : current)\n \t\t\t\t\tif (c.identityFile == null)\n \t\t\t\t\t\tc.identityFile = toFile(dequote(argValue));\n-\t\t\t} else if (\"PreferredAuthentications\".equalsIgnoreCase(keyword)) {\n+\t\t\t} else if (StringUtils.equalsIgnoreCase(\"PreferredAuthentications\", keyword)) {\n \t\t\t\tfor (final Host c : current)\n \t\t\t\t\tif (c.preferredAuthentications == null)\n \t\t\t\t\t\tc.preferredAuthentications = nows(dequote(argValue));\n-\t\t\t} else if (\"BatchMode\".equalsIgnoreCase(keyword)) {\n+\t\t\t} else if (StringUtils.equalsIgnoreCase(\"BatchMode\", keyword)) {\n \t\t\t\tfor (final Host c : current)\n \t\t\t\t\tif (c.batchMode == null)\n \t\t\t\t\t\tc.batchMode = yesno(dequote(argValue));\n-\t\t\t} else if (\"StrictHostKeyChecking\".equalsIgnoreCase(keyword)) {\n+\t\t\t} else if (StringUtils.equalsIgnoreCase(\"StrictHostKeyChecking\", keyword)) {\n \t\t\t\tString value = dequote(argValue);\n \t\t\t\tfor (final Host c : current)\n \t\t\t\t\tif (c.strictHostKeyChecking == null)\n@@ -268,7 +269,7 @@ private static String nows(final String value) {\n \t}\n \n \tprivate static Boolean yesno(final String value) {\n-\t\tif (\"yes\".equalsIgnoreCase(value))\n+\t\tif (StringUtils.equalsIgnoreCase(\"yes\", value))\n \t\t\treturn Boolean.TRUE;\n \t\treturn Boolean.FALSE;\n \t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/FS_Win32.java b/org.spearce.jgit/src/org/spearce/jgit/util/FS_Win32.java\nindex ef589bc..009b338 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/FS_Win32.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/FS_Win32.java\n@@ -50,7 +50,7 @@ public String run() {\n \t\t\t\t\t}\n \t\t\t\t});\n \t\treturn osDotName != null\n-\t\t\t\t&& osDotName.toLowerCase().indexOf(\"windows\") != -1;\n+\t\t\t\t&& StringUtils.toLowerCase(osDotName).indexOf(\"windows\") != -1;\n \t}\n \n \tpublic boolean supportsExecute() {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawSubStringPattern.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawSubStringPattern.java\nindex a81bf7f..5ed071c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawSubStringPattern.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawSubStringPattern.java\n@@ -117,7 +117,7 @@ private static final boolean neq(final byte a, final byte b) {\n \t}\n \n \tprivate static final byte lc(final byte q) {\n-\t\treturn (byte) Character.toLowerCase((char) (q & 0xff));\n+\t\treturn (byte) StringUtils.toLowerCase((char) (q & 0xff));\n \t}\n \n \t/**\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/StringUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/StringUtils.java\nnew file mode 100644\nindex 0000000..81d293a\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/StringUtils.java\n@@ -0,0 +1,115 @@\n+/*\n+ * Copyright (C) 2009, Google Inc.\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.util;\n+\n+/** Miscellaneous string comparison utility methods. */\n+public final class StringUtils {\n+\tprivate static final char[] LC;\n+\n+\tstatic {\n+\t\tLC = new char['Z' + 1];\n+\t\tfor (char c = 0; c < LC.length; c++)\n+\t\t\tLC[c] = c;\n+\t\tfor (char c = 'A'; c <= 'Z'; c++)\n+\t\t\tLC[c] = (char) ('a' + (c - 'A'));\n+\t}\n+\n+\t/**\n+\t * Convert the input to lowercase.\n+\t * <p>\n+\t * This method does not honor the JVM locale, but instead always behaves as\n+\t * though it is in the US-ASCII locale. Only characters in the range 'A'\n+\t * through 'Z' are converted. All other characters are left as-is, even if\n+\t * they otherwise would have a lowercase character equivilant.\n+\t * \n+\t * @param c\n+\t *            the input character.\n+\t * @return lowercase version of the input.\n+\t */\n+\tpublic static char toLowerCase(final char c) {\n+\t\treturn c <= 'Z' ? LC[c] : c;\n+\t}\n+\n+\t/**\n+\t * Convert the input string to lower case, according to the \"C\" locale.\n+\t * <p>\n+\t * This method does not honor the JVM locale, but instead always behaves as\n+\t * though it is in the US-ASCII locale. Only characters in the range 'A'\n+\t * through 'Z' are converted, all other characters are left as-is, even if\n+\t * they otherwise would have a lowercase character equivilant.\n+\t * \n+\t * @param in\n+\t *            the input string. Must not be null.\n+\t * @return a copy of the input string, after converting characters in the\n+\t *         range 'A'..'Z' to 'a'..'z'.\n+\t */\n+\tpublic static String toLowerCase(final String in) {\n+\t\tfinal StringBuilder r = new StringBuilder(in.length());\n+\t\tfor (int i = 0; i < in.length(); i++)\n+\t\t\tr.append(toLowerCase(in.charAt(i)));\n+\t\treturn r.toString();\n+\t}\n+\n+\t/**\n+\t * Test if two strings are equal, ignoring case.\n+\t * <p>\n+\t * This method does not honor the JVM locale, but instead always behaves as\n+\t * though it is in the US-ASCII locale.\n+\t * \n+\t * @param a\n+\t *            first string to compare.\n+\t * @param b\n+\t *            second string to compare.\n+\t * @return true if a equals b\n+\t */\n+\tpublic static boolean equalsIgnoreCase(final String a, final String b) {\n+\t\tif (a == b)\n+\t\t\treturn true;\n+\t\tif (a.length() != b.length())\n+\t\t\treturn false;\n+\t\tfor (int i = 0; i < a.length(); i++) {\n+\t\t\tif (toLowerCase(a.charAt(i)) != toLowerCase(b.charAt(i)))\n+\t\t\t\treturn false;\n+\t\t}\n+\t\treturn true;\n+\t}\n+\n+\tprivate StringUtils() {\n+\t\t// Do not create instances\n+\t}\n+}\n-- \n1.6.4.rc2.182.g24de1\n"}]}