View Javadoc
1   /*
2    * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch> and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.lib;
12  
13  import java.text.MessageFormat;
14  import java.util.ArrayList;
15  import java.util.List;
16  import java.util.concurrent.TimeUnit;
17  import java.util.regex.Matcher;
18  import java.util.regex.Pattern;
19  
20  import org.eclipse.jgit.annotations.NonNull;
21  import org.eclipse.jgit.internal.JGitText;
22  import org.eclipse.jgit.lib.Config.ConfigEnum;
23  import org.eclipse.jgit.transport.RefSpec;
24  import org.eclipse.jgit.util.StringUtils;
25  
26  /**
27   * An {@link org.eclipse.jgit.lib.TypedConfigGetter} that throws
28   * {@link java.lang.IllegalArgumentException} on invalid values.
29   *
30   * @since 4.9
31   */
32  public class DefaultTypedConfigGetter implements TypedConfigGetter {
33  
34  	/** {@inheritDoc} */
35  	@Override
36  	public boolean getBoolean(Config config, String section, String subsection,
37  			String name, boolean defaultValue) {
38  		String n = config.getRawString(section, subsection, name);
39  		if (n == null) {
40  			return defaultValue;
41  		}
42  		if (Config.isMissing(n)) {
43  			return true;
44  		}
45  		try {
46  			return StringUtils.toBoolean(n);
47  		} catch (IllegalArgumentException err) {
48  			throw new IllegalArgumentException(MessageFormat.format(
49  					JGitText.get().invalidBooleanValue, section, name, n), err);
50  		}
51  	}
52  
53  	/** {@inheritDoc} */
54  	@Override
55  	public <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
56  			String subsection, String name, T defaultValue) {
57  		String value = config.getString(section, subsection, name);
58  		if (value == null) {
59  			return defaultValue;
60  		}
61  		if (all[0] instanceof ConfigEnum) {
62  			for (T t : all) {
63  				if (((ConfigEnum) t).matchConfigValue(value)) {
64  					return t;
65  				}
66  			}
67  		}
68  
69  		String n = value.replace(' ', '_');
70  
71  		// Because of c98abc9c0586c73ef7df4172644b7dd21c979e9d being used in
72  		// the real world before its breakage was fully understood, we must
73  		// also accept '-' as though it were ' '.
74  		n = n.replace('-', '_');
75  
76  		T trueState = null;
77  		T falseState = null;
78  		for (T e : all) {
79  			if (StringUtils.equalsIgnoreCase(e.name(), n)) {
80  				return e;
81  			} else if (StringUtils.equalsIgnoreCase(e.name(), "TRUE")) { //$NON-NLS-1$
82  				trueState = e;
83  			} else if (StringUtils.equalsIgnoreCase(e.name(), "FALSE")) { //$NON-NLS-1$
84  				falseState = e;
85  			}
86  		}
87  
88  		// This is an odd little fallback. C Git sometimes allows boolean
89  		// values in a tri-state with other things. If we have both a true
90  		// and a false value in our enumeration, assume its one of those.
91  		//
92  		if (trueState != null && falseState != null) {
93  			try {
94  				return StringUtils.toBoolean(n) ? trueState : falseState;
95  			} catch (IllegalArgumentException err) {
96  				// Fall through and use our custom error below.
97  			}
98  		}
99  
100 		if (subsection != null) {
101 			throw new IllegalArgumentException(
102 					MessageFormat.format(JGitText.get().enumValueNotSupported3,
103 							section, subsection, name, value));
104 		}
105 		throw new IllegalArgumentException(MessageFormat.format(
106 				JGitText.get().enumValueNotSupported2, section, name, value));
107 	}
108 
109 	/** {@inheritDoc} */
110 	@Override
111 	public int getInt(Config config, String section, String subsection,
112 			String name, int defaultValue) {
113 		long val = config.getLong(section, subsection, name, defaultValue);
114 		if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) {
115 			return (int) val;
116 		}
117 		throw new IllegalArgumentException(MessageFormat
118 				.format(JGitText.get().integerValueOutOfRange, section, name));
119 	}
120 
121 	/** {@inheritDoc} */
122 	@Override
123 	public long getLong(Config config, String section, String subsection,
124 			String name, long defaultValue) {
125 		final String str = config.getString(section, subsection, name);
126 		if (str == null) {
127 			return defaultValue;
128 		}
129 		String n = str.trim();
130 		if (n.length() == 0) {
131 			return defaultValue;
132 		}
133 		long mul = 1;
134 		switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) {
135 		case 'g':
136 			mul = Config.GiB;
137 			break;
138 		case 'm':
139 			mul = Config.MiB;
140 			break;
141 		case 'k':
142 			mul = Config.KiB;
143 			break;
144 		}
145 		if (mul > 1) {
146 			n = n.substring(0, n.length() - 1).trim();
147 		}
148 		if (n.length() == 0) {
149 			return defaultValue;
150 		}
151 		try {
152 			return mul * Long.parseLong(n);
153 		} catch (NumberFormatException nfe) {
154 			throw new IllegalArgumentException(MessageFormat.format(
155 					JGitText.get().invalidIntegerValue, section, name, str),
156 					nfe);
157 		}
158 	}
159 
160 	/** {@inheritDoc} */
161 	@Override
162 	public long getTimeUnit(Config config, String section, String subsection,
163 			String name, long defaultValue, TimeUnit wantUnit) {
164 		String valueString = config.getString(section, subsection, name);
165 
166 		if (valueString == null) {
167 			return defaultValue;
168 		}
169 
170 		String s = valueString.trim();
171 		if (s.length() == 0) {
172 			return defaultValue;
173 		}
174 
175 		if (s.startsWith("-")/* negative */) { //$NON-NLS-1$
176 			throw notTimeUnit(section, subsection, name, valueString);
177 		}
178 
179 		Matcher m = Pattern.compile("^(0|[1-9][0-9]*)\\s*(.*)$") //$NON-NLS-1$
180 				.matcher(valueString);
181 		if (!m.matches()) {
182 			return defaultValue;
183 		}
184 
185 		String digits = m.group(1);
186 		String unitName = m.group(2).trim();
187 
188 		TimeUnit inputUnit;
189 		int inputMul;
190 
191 		if (unitName.isEmpty()) {
192 			inputUnit = wantUnit;
193 			inputMul = 1;
194 
195 		} else if (match(unitName, "ns", "nanoseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
196 			inputUnit = TimeUnit.NANOSECONDS;
197 			inputMul = 1;
198 
199 		} else if (match(unitName, "us", "microseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
200 			inputUnit = TimeUnit.MICROSECONDS;
201 			inputMul = 1;
202 
203 		} else if (match(unitName, "ms", "milliseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
204 			inputUnit = TimeUnit.MILLISECONDS;
205 			inputMul = 1;
206 
207 		} else if (match(unitName, "s", "sec", "second", "seconds")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
208 			inputUnit = TimeUnit.SECONDS;
209 			inputMul = 1;
210 
211 		} else if (match(unitName, "m", "min", "minute", "minutes")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
212 			inputUnit = TimeUnit.MINUTES;
213 			inputMul = 1;
214 
215 		} else if (match(unitName, "h", "hr", "hour", "hours")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
216 			inputUnit = TimeUnit.HOURS;
217 			inputMul = 1;
218 
219 		} else if (match(unitName, "d", "day", "days")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
220 			inputUnit = TimeUnit.DAYS;
221 			inputMul = 1;
222 
223 		} else if (match(unitName, "w", "week", "weeks")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
224 			inputUnit = TimeUnit.DAYS;
225 			inputMul = 7;
226 
227 		} else if (match(unitName, "mon", "month", "months")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
228 			inputUnit = TimeUnit.DAYS;
229 			inputMul = 30;
230 
231 		} else if (match(unitName, "y", "year", "years")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
232 			inputUnit = TimeUnit.DAYS;
233 			inputMul = 365;
234 
235 		} else {
236 			throw notTimeUnit(section, subsection, name, valueString);
237 		}
238 
239 		try {
240 			return wantUnit.convert(Long.parseLong(digits) * inputMul,
241 					inputUnit);
242 		} catch (NumberFormatException nfe) {
243 			IllegalArgumentException iae = notTimeUnit(section, subsection,
244 					unitName, valueString);
245 			iae.initCause(nfe);
246 			throw iae;
247 		}
248 	}
249 
250 	private static boolean match(String a, String... cases) {
251 		for (String b : cases) {
252 			if (b != null && b.equalsIgnoreCase(a)) {
253 				return true;
254 			}
255 		}
256 		return false;
257 	}
258 
259 	private static IllegalArgumentException notTimeUnit(String section,
260 			String subsection, String name, String valueString) {
261 		if (subsection != null) {
262 			return new IllegalArgumentException(
263 					MessageFormat.format(JGitText.get().invalidTimeUnitValue3,
264 							section, subsection, name, valueString));
265 		}
266 		return new IllegalArgumentException(
267 				MessageFormat.format(JGitText.get().invalidTimeUnitValue2,
268 						section, name, valueString));
269 	}
270 
271 	/** {@inheritDoc} */
272 	@Override
273 	@NonNull
274 	public List<RefSpec> getRefSpecs(Config config, String section,
275 			String subsection, String name) {
276 		String[] values = config.getStringList(section, subsection, name);
277 		List<RefSpec> result = new ArrayList<>(values.length);
278 		for (String spec : values) {
279 			result.add(new RefSpec(spec));
280 		}
281 		return result;
282 	}
283 }