diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java index 6d07d34d3..d98a7d433 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ConfigTest.java @@ -48,6 +48,11 @@ package org.eclipse.jgit.lib; +import static java.util.concurrent.TimeUnit.DAYS; +import static java.util.concurrent.TimeUnit.HOURS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -64,6 +69,7 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.Set; +import java.util.concurrent.TimeUnit; import org.eclipse.jgit.api.MergeCommand.FastForwardMode; import org.eclipse.jgit.errors.ConfigInvalidException; @@ -849,4 +855,93 @@ private static Config parse(final String content, Config baseConfig) c.fromText(content); return c; } + + @Test + public void testTimeUnit() throws ConfigInvalidException { + assertEquals(0, parseTime("0", MILLISECONDS)); + assertEquals(2, parseTime("2ms", MILLISECONDS)); + assertEquals(200, parseTime("200 milliseconds", MILLISECONDS)); + + assertEquals(0, parseTime("0s", SECONDS)); + assertEquals(2, parseTime("2s", SECONDS)); + assertEquals(231, parseTime("231sec", SECONDS)); + assertEquals(1, parseTime("1second", SECONDS)); + assertEquals(300, parseTime("300 seconds", SECONDS)); + + assertEquals(2, parseTime("2m", MINUTES)); + assertEquals(2, parseTime("2min", MINUTES)); + assertEquals(1, parseTime("1 minute", MINUTES)); + assertEquals(10, parseTime("10 minutes", MINUTES)); + + assertEquals(5, parseTime("5h", HOURS)); + assertEquals(5, parseTime("5hr", HOURS)); + assertEquals(1, parseTime("1hour", HOURS)); + assertEquals(48, parseTime("48hours", HOURS)); + + assertEquals(5, parseTime("5 h", HOURS)); + assertEquals(5, parseTime("5 hr", HOURS)); + assertEquals(1, parseTime("1 hour", HOURS)); + assertEquals(48, parseTime("48 hours", HOURS)); + assertEquals(48, parseTime("48 \t \r hours", HOURS)); + + assertEquals(4, parseTime("4d", DAYS)); + assertEquals(1, parseTime("1day", DAYS)); + assertEquals(14, parseTime("14days", DAYS)); + + assertEquals(7, parseTime("1w", DAYS)); + assertEquals(7, parseTime("1week", DAYS)); + assertEquals(14, parseTime("2w", DAYS)); + assertEquals(14, parseTime("2weeks", DAYS)); + + assertEquals(30, parseTime("1mon", DAYS)); + assertEquals(30, parseTime("1month", DAYS)); + assertEquals(60, parseTime("2mon", DAYS)); + assertEquals(60, parseTime("2months", DAYS)); + + assertEquals(365, parseTime("1y", DAYS)); + assertEquals(365, parseTime("1year", DAYS)); + assertEquals(365 * 2, parseTime("2years", DAYS)); + } + + private long parseTime(String value, TimeUnit unit) + throws ConfigInvalidException { + Config c = parse("[a]\na=" + value + "\n"); + return c.getTimeUnit("a", null, "a", 0, unit); + } + + @Test + public void testTimeUnitDefaultValue() throws ConfigInvalidException { + // value not present + assertEquals(20, parse("[a]\na=0\n").getTimeUnit("a", null, "b", 20, + MILLISECONDS)); + // value is empty + assertEquals(20, parse("[a]\na=\" \"\n").getTimeUnit("a", null, "a", 20, + MILLISECONDS)); + + // value is not numeric + assertEquals(20, parse("[a]\na=test\n").getTimeUnit("a", null, "a", 20, + MILLISECONDS)); + } + + @Test + public void testTimeUnitInvalid() throws ConfigInvalidException { + expectedEx.expect(IllegalArgumentException.class); + expectedEx + .expectMessage("Invalid time unit value: a.a=1 monttthhh"); + parseTime("1 monttthhh", DAYS); + } + + @Test + public void testTimeUnitInvalidWithSection() throws ConfigInvalidException { + Config c = parse("[a \"b\"]\na=1 monttthhh\n"); + expectedEx.expect(IllegalArgumentException.class); + expectedEx.expectMessage("Invalid time unit value: a.b.a=1 monttthhh"); + c.getTimeUnit("a", "b", "a", 0, DAYS); + } + + @Test + public void testTimeUnitNegative() throws ConfigInvalidException { + expectedEx.expect(IllegalArgumentException.class); + parseTime("-1", MILLISECONDS); + } } diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties index b71e48b52..c2d601eed 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/internal/JGitText.properties @@ -361,6 +361,8 @@ invalidShallowObject=invalid shallow object {0}, expected commit invalidStageForPath=Invalid stage {0} for path {1} invalidTagOption=Invalid tag option: {0} invalidTimeout=Invalid timeout: {0} +invalidTimeUnitValue2=Invalid time unit value: {0}.{1}={2} +invalidTimeUnitValue3=Invalid time unit value: {0}.{1}.{2}={3} invalidURL=Invalid URL {0} invalidWildcards=Invalid wildcards {0} invalidRefSpec=Invalid refspec {0} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java index 7c101780a..928cc8605 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/JGitText.java @@ -419,6 +419,8 @@ public static JGitText get() { /***/ public String invalidStageForPath; /***/ public String invalidTagOption; /***/ public String invalidTimeout; + /***/ public String invalidTimeUnitValue2; + /***/ public String invalidTimeUnitValue3; /***/ public String invalidURL; /***/ public String invalidWildcards; /***/ public String invalidRefSpec; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java index b8eba3acc..40829f4f6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Config.java @@ -58,7 +58,10 @@ import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.events.ConfigChangedEvent; @@ -489,6 +492,123 @@ public String[] getStringList(final String section, String subsection, return res; } + /** + * Parse a numerical time unit, such as "1 minute", from the configuration. + * + * @param section + * section the key is in. + * @param subsection + * subsection the key is in, or null if not in a subsection. + * @param name + * the key name. + * @param defaultValue + * default value to return if no value was present. + * @param wantUnit + * the units of {@code defaultValue} and the return value, as + * well as the units to assume if the value does not contain an + * indication of the units. + * @return the value, or {@code defaultValue} if not set, expressed in + * {@code units}. + * @since 4.5 + */ + public long getTimeUnit(String section, String subsection, String name, + long defaultValue, TimeUnit wantUnit) { + String valueString = getString(section, subsection, name); + + if (valueString == null) { + return defaultValue; + } + + String s = valueString.trim(); + if (s.length() == 0) { + return defaultValue; + } + + if (s.startsWith("-")/* negative */) { //$NON-NLS-1$ + throw notTimeUnit(section, subsection, name, valueString); + } + + Matcher m = Pattern.compile("^(0|[1-9][0-9]*)\\s*(.*)$") //$NON-NLS-1$ + .matcher(valueString); + if (!m.matches()) { + return defaultValue; + } + + String digits = m.group(1); + String unitName = m.group(2).trim(); + + TimeUnit inputUnit; + int inputMul; + + if (unitName.isEmpty()) { + inputUnit = wantUnit; + inputMul = 1; + + } else if (match(unitName, "ms", "milliseconds")) { //$NON-NLS-1$ //$NON-NLS-2$ + inputUnit = TimeUnit.MILLISECONDS; + inputMul = 1; + + } else if (match(unitName, "s", "sec", "second", "seconds")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + inputUnit = TimeUnit.SECONDS; + inputMul = 1; + + } else if (match(unitName, "m", "min", "minute", "minutes")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + inputUnit = TimeUnit.MINUTES; + inputMul = 1; + + } else if (match(unitName, "h", "hr", "hour", "hours")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + inputUnit = TimeUnit.HOURS; + inputMul = 1; + + } else if (match(unitName, "d", "day", "days")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 1; + + } else if (match(unitName, "w", "week", "weeks")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 7; + + } else if (match(unitName, "mon", "month", "months")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 30; + + } else if (match(unitName, "y", "year", "years")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + inputUnit = TimeUnit.DAYS; + inputMul = 365; + + } else { + throw notTimeUnit(section, subsection, name, valueString); + } + + try { + return wantUnit.convert(Long.parseLong(digits) * inputMul, + inputUnit); + } catch (NumberFormatException nfe) { + throw notTimeUnit(section, subsection, unitName, valueString); + } + } + + private static boolean match(final String a, final String... cases) { + for (final String b : cases) { + if (b != null && b.equalsIgnoreCase(a)) { + return true; + } + } + return false; + } + + private IllegalArgumentException notTimeUnit(String section, + String subsection, String name, String valueString) { + if (subsection != null) { + return new IllegalArgumentException( + MessageFormat.format(JGitText.get().invalidTimeUnitValue3, + section, subsection, name, valueString)); + } + return new IllegalArgumentException( + MessageFormat.format(JGitText.get().invalidTimeUnitValue2, + section, name, valueString)); + } + /** * @param section * section to search for.