View Javadoc
1   /*
2    * Copyright (C) 2017, Thomas Wolf <thomas.wolf@paranor.ch>
3    * and other copyright owners as documented in the project's IP log.
4    *
5    * This program and the accompanying materials are made available
6    * under the terms of the Eclipse Distribution License v1.0 which
7    * accompanies this distribution, is reproduced below, and is
8    * available at http://www.eclipse.org/org/documents/edl-v10.php
9    *
10   * All rights reserved.
11   *
12   * Redistribution and use in source and binary forms, with or
13   * without modification, are permitted provided that the following
14   * conditions are met:
15   *
16   * - Redistributions of source code must retain the above copyright
17   *   notice, this list of conditions and the following disclaimer.
18   *
19   * - Redistributions in binary form must reproduce the above
20   *   copyright notice, this list of conditions and the following
21   *   disclaimer in the documentation and/or other materials provided
22   *   with the distribution.
23   *
24   * - Neither the name of the Eclipse Foundation, Inc. nor the
25   *   names of its contributors may be used to endorse or promote
26   *   products derived from this software without specific prior
27   *   written permission.
28   *
29   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
30   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
31   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
32   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
33   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
34   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
37   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
38   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
39   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
40   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
41   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
42   */
43  
44  package org.eclipse.jgit.lib;
45  
46  import java.text.MessageFormat;
47  import java.util.ArrayList;
48  import java.util.List;
49  import java.util.concurrent.TimeUnit;
50  import java.util.regex.Matcher;
51  import java.util.regex.Pattern;
52  
53  import org.eclipse.jgit.annotations.NonNull;
54  import org.eclipse.jgit.internal.JGitText;
55  import org.eclipse.jgit.lib.Config.ConfigEnum;
56  import org.eclipse.jgit.transport.RefSpec;
57  import org.eclipse.jgit.util.StringUtils;
58  
59  /**
60   * An {@link org.eclipse.jgit.lib.TypedConfigGetter} that throws
61   * {@link java.lang.IllegalArgumentException} on invalid values.
62   *
63   * @since 4.9
64   */
65  public class DefaultTypedConfigGetter implements TypedConfigGetter {
66  
67  	/** {@inheritDoc} */
68  	@Override
69  	public boolean getBoolean(Config config, String section, String subsection,
70  			String name, boolean defaultValue) {
71  		String n = config.getRawString(section, subsection, name);
72  		if (n == null) {
73  			return defaultValue;
74  		}
75  		if (Config.isMissing(n)) {
76  			return true;
77  		}
78  		try {
79  			return StringUtils.toBoolean(n);
80  		} catch (IllegalArgumentException err) {
81  			throw new IllegalArgumentException(MessageFormat.format(
82  					JGitText.get().invalidBooleanValue, section, name, n));
83  		}
84  	}
85  
86  	/** {@inheritDoc} */
87  	@Override
88  	public <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
89  			String subsection, String name, T defaultValue) {
90  		String value = config.getString(section, subsection, name);
91  		if (value == null) {
92  			return defaultValue;
93  		}
94  		if (all[0] instanceof ConfigEnum) {
95  			for (T t : all) {
96  				if (((ConfigEnum) t).matchConfigValue(value)) {
97  					return t;
98  				}
99  			}
100 		}
101 
102 		String n = value.replace(' ', '_');
103 
104 		// Because of c98abc9c0586c73ef7df4172644b7dd21c979e9d being used in
105 		// the real world before its breakage was fully understood, we must
106 		// also accept '-' as though it were ' '.
107 		n = n.replace('-', '_');
108 
109 		T trueState = null;
110 		T falseState = null;
111 		for (T e : all) {
112 			if (StringUtils.equalsIgnoreCase(e.name(), n)) {
113 				return e;
114 			} else if (StringUtils.equalsIgnoreCase(e.name(), "TRUE")) { //$NON-NLS-1$
115 				trueState = e;
116 			} else if (StringUtils.equalsIgnoreCase(e.name(), "FALSE")) { //$NON-NLS-1$
117 				falseState = e;
118 			}
119 		}
120 
121 		// This is an odd little fallback. C Git sometimes allows boolean
122 		// values in a tri-state with other things. If we have both a true
123 		// and a false value in our enumeration, assume its one of those.
124 		//
125 		if (trueState != null && falseState != null) {
126 			try {
127 				return StringUtils.toBoolean(n) ? trueState : falseState;
128 			} catch (IllegalArgumentException err) {
129 				// Fall through and use our custom error below.
130 			}
131 		}
132 
133 		if (subsection != null) {
134 			throw new IllegalArgumentException(
135 					MessageFormat.format(JGitText.get().enumValueNotSupported3,
136 							section, subsection, name, value));
137 		}
138 		throw new IllegalArgumentException(MessageFormat.format(
139 				JGitText.get().enumValueNotSupported2, section, name, value));
140 	}
141 
142 	/** {@inheritDoc} */
143 	@Override
144 	public int getInt(Config config, String section, String subsection,
145 			String name, int defaultValue) {
146 		long val = config.getLong(section, subsection, name, defaultValue);
147 		if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) {
148 			return (int) val;
149 		}
150 		throw new IllegalArgumentException(MessageFormat
151 				.format(JGitText.get().integerValueOutOfRange, section, name));
152 	}
153 
154 	/** {@inheritDoc} */
155 	@Override
156 	public long getLong(Config config, String section, String subsection,
157 			String name, long defaultValue) {
158 		final String str = config.getString(section, subsection, name);
159 		if (str == null) {
160 			return defaultValue;
161 		}
162 		String n = str.trim();
163 		if (n.length() == 0) {
164 			return defaultValue;
165 		}
166 		long mul = 1;
167 		switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) {
168 		case 'g':
169 			mul = Config.GiB;
170 			break;
171 		case 'm':
172 			mul = Config.MiB;
173 			break;
174 		case 'k':
175 			mul = Config.KiB;
176 			break;
177 		}
178 		if (mul > 1) {
179 			n = n.substring(0, n.length() - 1).trim();
180 		}
181 		if (n.length() == 0) {
182 			return defaultValue;
183 		}
184 		try {
185 			return mul * Long.parseLong(n);
186 		} catch (NumberFormatException nfe) {
187 			throw new IllegalArgumentException(MessageFormat.format(
188 					JGitText.get().invalidIntegerValue, section, name, str));
189 		}
190 	}
191 
192 	/** {@inheritDoc} */
193 	@Override
194 	public long getTimeUnit(Config config, String section, String subsection,
195 			String name, long defaultValue, TimeUnit wantUnit) {
196 		String valueString = config.getString(section, subsection, name);
197 
198 		if (valueString == null) {
199 			return defaultValue;
200 		}
201 
202 		String s = valueString.trim();
203 		if (s.length() == 0) {
204 			return defaultValue;
205 		}
206 
207 		if (s.startsWith("-")/* negative */) { //$NON-NLS-1$
208 			throw notTimeUnit(section, subsection, name, valueString);
209 		}
210 
211 		Matcher m = Pattern.compile("^(0|[1-9][0-9]*)\\s*(.*)$") //$NON-NLS-1$
212 				.matcher(valueString);
213 		if (!m.matches()) {
214 			return defaultValue;
215 		}
216 
217 		String digits = m.group(1);
218 		String unitName = m.group(2).trim();
219 
220 		TimeUnit inputUnit;
221 		int inputMul;
222 
223 		if (unitName.isEmpty()) {
224 			inputUnit = wantUnit;
225 			inputMul = 1;
226 
227 		} else if (match(unitName, "ns", "nanoseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
228 			inputUnit = TimeUnit.NANOSECONDS;
229 			inputMul = 1;
230 
231 		} else if (match(unitName, "us", "microseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
232 			inputUnit = TimeUnit.MICROSECONDS;
233 			inputMul = 1;
234 
235 		} else if (match(unitName, "ms", "milliseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
236 			inputUnit = TimeUnit.MILLISECONDS;
237 			inputMul = 1;
238 
239 		} else if (match(unitName, "s", "sec", "second", "seconds")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
240 			inputUnit = TimeUnit.SECONDS;
241 			inputMul = 1;
242 
243 		} else if (match(unitName, "m", "min", "minute", "minutes")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
244 			inputUnit = TimeUnit.MINUTES;
245 			inputMul = 1;
246 
247 		} else if (match(unitName, "h", "hr", "hour", "hours")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
248 			inputUnit = TimeUnit.HOURS;
249 			inputMul = 1;
250 
251 		} else if (match(unitName, "d", "day", "days")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
252 			inputUnit = TimeUnit.DAYS;
253 			inputMul = 1;
254 
255 		} else if (match(unitName, "w", "week", "weeks")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
256 			inputUnit = TimeUnit.DAYS;
257 			inputMul = 7;
258 
259 		} else if (match(unitName, "mon", "month", "months")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
260 			inputUnit = TimeUnit.DAYS;
261 			inputMul = 30;
262 
263 		} else if (match(unitName, "y", "year", "years")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
264 			inputUnit = TimeUnit.DAYS;
265 			inputMul = 365;
266 
267 		} else {
268 			throw notTimeUnit(section, subsection, name, valueString);
269 		}
270 
271 		try {
272 			return wantUnit.convert(Long.parseLong(digits) * inputMul,
273 					inputUnit);
274 		} catch (NumberFormatException nfe) {
275 			throw notTimeUnit(section, subsection, unitName, valueString);
276 		}
277 	}
278 
279 	private static boolean match(String a, String... cases) {
280 		for (String b : cases) {
281 			if (b != null && b.equalsIgnoreCase(a)) {
282 				return true;
283 			}
284 		}
285 		return false;
286 	}
287 
288 	private static IllegalArgumentException notTimeUnit(String section,
289 			String subsection, String name, String valueString) {
290 		if (subsection != null) {
291 			return new IllegalArgumentException(
292 					MessageFormat.format(JGitText.get().invalidTimeUnitValue3,
293 							section, subsection, name, valueString));
294 		}
295 		return new IllegalArgumentException(
296 				MessageFormat.format(JGitText.get().invalidTimeUnitValue2,
297 						section, name, valueString));
298 	}
299 
300 	/** {@inheritDoc} */
301 	@Override
302 	@NonNull
303 	public List<RefSpec> getRefSpecs(Config config, String section,
304 			String subsection, String name) {
305 		String[] values = config.getStringList(section, subsection, name);
306 		List<RefSpec> result = new ArrayList<>(values.length);
307 		for (String spec : values) {
308 			result.add(new RefSpec(spec));
309 		}
310 		return result;
311 	}
312 }