]> source.dussan.org Git - jgit.git/commitdiff
Support core.autocrlf = input 52/1552/1
authorShawn O. Pearce <spearce@spearce.org>
Wed, 8 Sep 2010 00:14:27 +0000 (17:14 -0700)
committerShawn O. Pearce <spearce@spearce.org>
Wed, 8 Sep 2010 00:14:27 +0000 (17:14 -0700)
The core.autocrlf variable can take on three values: false, true,
and input.  Parsing it as a boolean is wrong, we instead need to
parse a tri-state enumeration.

Add support for parsing and setting enum values from Java from and
to the text based configuration file, and use that to handle the
autocrlf variable.

Bug: 301775
Change-Id: I81b9e33087a33d2ef2eac89ba93b9e83b7ecc223
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/AbstractTreeIteratorTest.java
org.eclipse.jgit.test/tst/org/eclipse/jgit/treewalk/FileTreeIteratorWithTimeControl.java
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java
org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeOptions.java

index e12e869ec5c09a4bf69b55a480c4cba8a8bd99de..eb9f03c55037dbefbbbb512cc7e1d3da2927c715 100644 (file)
@@ -229,6 +229,38 @@ public class ConfigTest extends TestCase {
                assertFalse(c.getBoolean("s", "b", true));
        }
 
+       static enum TestEnum {
+               ONE_TWO;
+       }
+
+       public void testGetEnum() throws ConfigInvalidException {
+               Config c = parse("[s]\na = ON\nb = input\nc = true\nd = off\n");
+               assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "a",
+                               CoreConfig.AutoCRLF.FALSE));
+
+               assertSame(CoreConfig.AutoCRLF.INPUT, c.getEnum("s", null, "b",
+                               CoreConfig.AutoCRLF.FALSE));
+
+               assertSame(CoreConfig.AutoCRLF.TRUE, c.getEnum("s", null, "c",
+                               CoreConfig.AutoCRLF.FALSE));
+
+               assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
+                               CoreConfig.AutoCRLF.TRUE));
+
+               c = new Config();
+               assertSame(CoreConfig.AutoCRLF.FALSE, c.getEnum("s", null, "d",
+                               CoreConfig.AutoCRLF.FALSE));
+
+               c = parse("[s \"b\"]\n\tc = one two\n");
+               assertSame(TestEnum.ONE_TWO, c.getEnum("s", "b", "c", TestEnum.ONE_TWO));
+       }
+
+       public void testSetEnum() {
+               final Config c = new Config();
+               c.setEnum("s", "b", "c", TestEnum.ONE_TWO);
+               assertEquals("[s \"b\"]\n\tc = one two\n", c.toText());
+       }
+
        public void testReadLong() throws ConfigInvalidException {
                assertReadLong(1L);
                assertReadLong(-1L);
index 72354e4e95a8c9bfe6d342d3390c73f0b05a2f69..cc7d0ad9ef10e22a6bd1d1845dab8676f2e2eddc 100644 (file)
@@ -52,6 +52,7 @@ import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 import org.eclipse.jgit.lib.Constants;
 import org.eclipse.jgit.lib.FileMode;
 import org.eclipse.jgit.lib.ObjectReader;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 
 
 public class AbstractTreeIteratorTest extends TestCase {
@@ -62,7 +63,7 @@ public class AbstractTreeIteratorTest extends TestCase {
 
        public class FakeTreeIterator extends WorkingTreeIterator {
                public FakeTreeIterator(String pathName, FileMode fileMode) {
-                       super(prefix(pathName), new WorkingTreeOptions(false));
+                       super(prefix(pathName), new WorkingTreeOptions(AutoCRLF.FALSE));
                        mode = fileMode.getBits();
 
                        final int s = pathName.lastIndexOf('/');
index 58fb5297a2e38dce3ef3c99aaeb13942ae794cb8..f15454ba07cfc2b59ae5416ad998656d6e673244 100644 (file)
@@ -48,6 +48,7 @@ import java.util.TreeSet;
 
 import org.eclipse.jgit.lib.ObjectReader;
 import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 import org.eclipse.jgit.util.FS;
 
 /**
@@ -87,7 +88,7 @@ public class FileTreeIteratorWithTimeControl extends FileTreeIterator {
 
        public FileTreeIteratorWithTimeControl(File f, FS fs,
                        TreeSet<Long> modTimes) {
-               super(f, fs, new WorkingTreeOptions(false));
+               super(f, fs, new WorkingTreeOptions(AutoCRLF.FALSE));
                this.modTimes = modTimes;
        }
 
index cd932c6d83d757df529c9c7d17059dfcda5029a1..e8ec148bdd8514363b890ede8e550acdb9487454 100644 (file)
@@ -136,6 +136,9 @@ emptyPathNotPermitted=Empty path not permitted.
 encryptionError=Encryption error: {0}
 endOfFileInEscape=End of file in escape
 entryNotFoundByPath=Entry not found by path: {0}
+enumValueNotSupported2=Invalid value: {0}.{1}={2}
+enumValueNotSupported3=Invalid value: {0}.{1}.{2}={3}
+enumValuesNotAvailable=Enumerated values of type {0} not available
 errorDecodingFromFile=Error decoding from file {0}
 errorEncodingFromFile=Error encoding from file {0}
 errorInBase64CodeReadingStream=Error in Base64 code reading stream.
index 7fa265847bbaf2b120dda59b18f88bbade2df045..c7b5a9ee6314fcc470dbcc67b14ab730b1cb48af 100644 (file)
@@ -196,6 +196,9 @@ public class JGitText extends TranslationBundle {
        /***/ public String encryptionError;
        /***/ public String endOfFileInEscape;
        /***/ public String entryNotFoundByPath;
+       /***/ public String enumValueNotSupported2;
+       /***/ public String enumValueNotSupported3;
+       /***/ public String enumValuesNotAvailable;
        /***/ public String errorDecodingFromFile;
        /***/ public String errorEncodingFromFile;
        /***/ public String errorInBase64CodeReadingStream;
index 884f49845c978d4e65e8ae27c41a0a79832640d8..daad67e29c68888b9739a4e7787f70579c21a624 100644 (file)
@@ -333,6 +333,95 @@ public class Config {
                }
        }
 
+       /**
+        * Parse an enumeration from the configuration.
+        *
+        * @param <T>
+        *            type of the enumeration object.
+        * @param section
+        *            section the key is grouped within.
+        * @param subsection
+        *            subsection name, such a remote or branch name.
+        * @param name
+        *            name of the key to get.
+        * @param defaultValue
+        *            default value to return if no value was present.
+        * @return the selected enumeration value, or {@code defaultValue}.
+        */
+       public <T extends Enum<?>> T getEnum(final String section,
+                       final String subsection, final String name, final T defaultValue) {
+               final T[] all = allValuesOf(defaultValue);
+               return getEnum(all, section, subsection, name, defaultValue);
+       }
+
+       @SuppressWarnings("unchecked")
+       private static <T> T[] allValuesOf(final T value) {
+               try {
+                       return (T[]) value.getClass().getMethod("values").invoke(null);
+               } catch (Exception err) {
+                       String typeName = value.getClass().getName();
+                       String msg = MessageFormat.format(
+                                       JGitText.get().enumValuesNotAvailable, typeName);
+                       throw new IllegalArgumentException(msg, err);
+               }
+       }
+
+       /**
+        * Parse an enumeration from the configuration.
+        *
+        * @param <T>
+        *            type of the enumeration object.
+        * @param all
+        *            all possible values in the enumeration which should be
+        *            recognized. Typically {@code EnumType.values()}.
+        * @param section
+        *            section the key is grouped within.
+        * @param subsection
+        *            subsection name, such a remote or branch name.
+        * @param name
+        *            name of the key to get.
+        * @param defaultValue
+        *            default value to return if no value was present.
+        * @return the selected enumeration value, or {@code defaultValue}.
+        */
+       public <T extends Enum<?>> T getEnum(final T[] all, final String section,
+                       final String subsection, final String name, final T defaultValue) {
+               String value = getString(section, subsection, name);
+               if (value == null)
+                       return defaultValue;
+
+               String n = value.replace(' ', '_');
+               T trueState = null;
+               T falseState = null;
+               for (T e : all) {
+                       if (StringUtils.equalsIgnoreCase(e.name(), n))
+                               return e;
+                       else if (StringUtils.equalsIgnoreCase(e.name(), "TRUE"))
+                               trueState = e;
+                       else if (StringUtils.equalsIgnoreCase(e.name(), "FALSE"))
+                               falseState = e;
+               }
+
+               // This is an odd little fallback. C Git sometimes allows boolean
+               // values in a tri-state with other things. If we have both a true
+               // and a false value in our enumeration, assume its one of those.
+               //
+               if (trueState != null && falseState != null) {
+                       try {
+                               return StringUtils.toBoolean(n) ? trueState : falseState;
+                       } catch (IllegalArgumentException err) {
+                               // Fall through and use our custom error below.
+                       }
+               }
+
+               if (subsection != null)
+                       throw new IllegalArgumentException(MessageFormat.format(JGitText
+                                       .get().enumValueNotSupported3, section, name, value));
+               else
+                       throw new IllegalArgumentException(MessageFormat.format(JGitText
+                                       .get().enumValueNotSupported2, section, name, value));
+       }
+
        /**
         * Get string value
         *
@@ -625,6 +714,32 @@ public class Config {
                setString(section, subsection, name, value ? "true" : "false");
        }
 
+       /**
+        * Add or modify a configuration value. The parameters will result in a
+        * configuration entry like this.
+        *
+        * <pre>
+        * [section &quot;subsection&quot;]
+        *         name = value
+        * </pre>
+        *
+        * @param <T>
+        *            type of the enumeration object.
+        * @param section
+        *            section name, e.g "branch"
+        * @param subsection
+        *            optional subsection value, e.g. a branch name
+        * @param name
+        *            parameter name, e.g. "filemode"
+        * @param value
+        *            parameter value
+        */
+       public <T extends Enum<?>> void setEnum(final String section,
+                       final String subsection, final String name, final T value) {
+               String n = value.name().toLowerCase().replace('_', ' ');
+               setString(section, subsection, name, n);
+       }
+
        /**
         * Add or modify a configuration value. The parameters will result in a
         * configuration entry like this.
index 249b95ec3ce067eba2bc5742c07474ea7ca6bea5..f644d2c69768e7dd62c046e5db3625b0ba0ebc02 100644 (file)
@@ -61,19 +61,31 @@ public class CoreConfig {
                }
        };
 
+       /** Permissible values for {@code core.autocrlf}. */
+       public static enum AutoCRLF {
+               /** Automatic CRLF->LF conversion is disabled. */
+               FALSE,
+
+               /** Automatic CRLF->LF conversion is enabled. */
+               TRUE,
+
+               /** CRLF->LF performed, but no LF->CRLF. */
+               INPUT;
+       }
+
        private final int compression;
 
        private final int packIndexVersion;
 
        private final boolean logAllRefUpdates;
 
-       private final boolean autoCRLF;
+       private final AutoCRLF autoCRLF;
 
        private CoreConfig(final Config rc) {
                compression = rc.getInt("core", "compression", DEFAULT_COMPRESSION);
                packIndexVersion = rc.getInt("pack", "indexversion", 2);
                logAllRefUpdates = rc.getBoolean("core", "logallrefupdates", true);
-               autoCRLF = rc.getBoolean("core", "autocrlf", false);
+               autoCRLF = rc.getEnum("core", null, "autocrlf", AutoCRLF.FALSE);
        }
 
        /**
@@ -101,7 +113,7 @@ public class CoreConfig {
        /**
         * @return whether automatic CRLF conversion has been configured
         */
-       public boolean isAutoCRLF() {
+       public AutoCRLF getAutoCRLF() {
                return autoCRLF;
        }
 }
index 5cc061bbb4d20f6d5cf2ea1fd903feb3cc6c6db7..b292c3cdd34564d675394db43064029cdb2bde81 100644 (file)
@@ -299,7 +299,15 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
        }
 
        private boolean mightNeedCleaning(Entry entry) {
-               return options.isAutoCRLF();
+               switch (options.getAutoCRLF()) {
+               case FALSE:
+               default:
+                       return false;
+
+               case TRUE:
+               case INPUT:
+                       return true;
+               }
        }
 
        private boolean isBinary(Entry entry, byte[] content, int sz) {
index 50da3302d6943bb52e22f6013f8460da82aae327..6929046c25b46bb9d0ad13a686d5a0a481404c35 100644 (file)
@@ -44,6 +44,7 @@ package org.eclipse.jgit.treewalk;
 
 import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.CoreConfig;
+import org.eclipse.jgit.lib.CoreConfig.AutoCRLF;
 
 /**
  * Contains options used by the WorkingTreeIterator.
@@ -57,7 +58,7 @@ public class WorkingTreeOptions {
         * @return created working tree options
         */
        public static WorkingTreeOptions createDefaultInstance() {
-               return new WorkingTreeOptions(false);
+               return new WorkingTreeOptions(AutoCRLF.FALSE);
        }
 
        /**
@@ -69,14 +70,14 @@ public class WorkingTreeOptions {
         * @return created working tree options
         */
        public static WorkingTreeOptions createConfigurationInstance(Config config) {
-               return new WorkingTreeOptions(config.get(CoreConfig.KEY).isAutoCRLF());
+               return new WorkingTreeOptions(config.get(CoreConfig.KEY).getAutoCRLF());
        }
 
        /**
         * Indicates whether EOLs of text files should be converted to '\n' before
         * calculating the blob ID.
         **/
-       private final boolean autoCRLF;
+       private final AutoCRLF autoCRLF;
 
        /**
         * Creates new options.
@@ -85,7 +86,7 @@ public class WorkingTreeOptions {
         *            indicates whether EOLs of text files should be converted to
         *            '\n' before calculating the blob ID.
         */
-       public WorkingTreeOptions(boolean autoCRLF) {
+       public WorkingTreeOptions(AutoCRLF autoCRLF) {
                this.autoCRLF = autoCRLF;
        }
 
@@ -95,7 +96,7 @@ public class WorkingTreeOptions {
         *
         * @return true if EOLs should be canonicalized.
         */
-       public boolean isAutoCRLF() {
+       public AutoCRLF getAutoCRLF() {
                return autoCRLF;
        }
 }