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.MAGIC_EMPTY_VALUE == 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 		} else {
138 			throw new IllegalArgumentException(
139 					MessageFormat.format(JGitText.get().enumValueNotSupported2,
140 							section, name, value));
141 		}
142 	}
143 
144 	/** {@inheritDoc} */
145 	@Override
146 	public int getInt(Config config, String section, String subsection,
147 			String name, int defaultValue) {
148 		long val = config.getLong(section, subsection, name, defaultValue);
149 		if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) {
150 			return (int) val;
151 		}
152 		throw new IllegalArgumentException(MessageFormat
153 				.format(JGitText.get().integerValueOutOfRange, section, name));
154 	}
155 
156 	/** {@inheritDoc} */
157 	@Override
158 	public long getLong(Config config, String section, String subsection,
159 			String name, long defaultValue) {
160 		final String str = config.getString(section, subsection, name);
161 		if (str == null) {
162 			return defaultValue;
163 		}
164 		String n = str.trim();
165 		if (n.length() == 0) {
166 			return defaultValue;
167 		}
168 		long mul = 1;
169 		switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) {
170 		case 'g':
171 			mul = Config.GiB;
172 			break;
173 		case 'm':
174 			mul = Config.MiB;
175 			break;
176 		case 'k':
177 			mul = Config.KiB;
178 			break;
179 		}
180 		if (mul > 1) {
181 			n = n.substring(0, n.length() - 1).trim();
182 		}
183 		if (n.length() == 0) {
184 			return defaultValue;
185 		}
186 		try {
187 			return mul * Long.parseLong(n);
188 		} catch (NumberFormatException nfe) {
189 			throw new IllegalArgumentException(MessageFormat.format(
190 					JGitText.get().invalidIntegerValue, section, name, str));
191 		}
192 	}
193 
194 	/** {@inheritDoc} */
195 	@Override
196 	public long getTimeUnit(Config config, String section, String subsection,
197 			String name, long defaultValue, TimeUnit wantUnit) {
198 		String valueString = config.getString(section, subsection, name);
199 
200 		if (valueString == null) {
201 			return defaultValue;
202 		}
203 
204 		String s = valueString.trim();
205 		if (s.length() == 0) {
206 			return defaultValue;
207 		}
208 
209 		if (s.startsWith("-")/* negative */) { //$NON-NLS-1$
210 			throw notTimeUnit(section, subsection, name, valueString);
211 		}
212 
213 		Matcher m = Pattern.compile("^(0|[1-9][0-9]*)\\s*(.*)$") //$NON-NLS-1$
214 				.matcher(valueString);
215 		if (!m.matches()) {
216 			return defaultValue;
217 		}
218 
219 		String digits = m.group(1);
220 		String unitName = m.group(2).trim();
221 
222 		TimeUnit inputUnit;
223 		int inputMul;
224 
225 		if (unitName.isEmpty()) {
226 			inputUnit = wantUnit;
227 			inputMul = 1;
228 
229 		} else if (match(unitName, "ms", "milliseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
230 			inputUnit = TimeUnit.MILLISECONDS;
231 			inputMul = 1;
232 
233 		} else if (match(unitName, "s", "sec", "second", "seconds")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
234 			inputUnit = TimeUnit.SECONDS;
235 			inputMul = 1;
236 
237 		} else if (match(unitName, "m", "min", "minute", "minutes")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
238 			inputUnit = TimeUnit.MINUTES;
239 			inputMul = 1;
240 
241 		} else if (match(unitName, "h", "hr", "hour", "hours")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
242 			inputUnit = TimeUnit.HOURS;
243 			inputMul = 1;
244 
245 		} else if (match(unitName, "d", "day", "days")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
246 			inputUnit = TimeUnit.DAYS;
247 			inputMul = 1;
248 
249 		} else if (match(unitName, "w", "week", "weeks")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
250 			inputUnit = TimeUnit.DAYS;
251 			inputMul = 7;
252 
253 		} else if (match(unitName, "mon", "month", "months")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
254 			inputUnit = TimeUnit.DAYS;
255 			inputMul = 30;
256 
257 		} else if (match(unitName, "y", "year", "years")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
258 			inputUnit = TimeUnit.DAYS;
259 			inputMul = 365;
260 
261 		} else {
262 			throw notTimeUnit(section, subsection, name, valueString);
263 		}
264 
265 		try {
266 			return wantUnit.convert(Long.parseLong(digits) * inputMul,
267 					inputUnit);
268 		} catch (NumberFormatException nfe) {
269 			throw notTimeUnit(section, subsection, unitName, valueString);
270 		}
271 	}
272 
273 	private static boolean match(String a, String... cases) {
274 		for (String b : cases) {
275 			if (b != null && b.equalsIgnoreCase(a)) {
276 				return true;
277 			}
278 		}
279 		return false;
280 	}
281 
282 	private static IllegalArgumentException notTimeUnit(String section,
283 			String subsection, String name, String valueString) {
284 		if (subsection != null) {
285 			return new IllegalArgumentException(
286 					MessageFormat.format(JGitText.get().invalidTimeUnitValue3,
287 							section, subsection, name, valueString));
288 		}
289 		return new IllegalArgumentException(
290 				MessageFormat.format(JGitText.get().invalidTimeUnitValue2,
291 						section, name, valueString));
292 	}
293 
294 	/** {@inheritDoc} */
295 	@Override
296 	public @NonNull List<RefSpec> getRefSpecs(Config config, String section,
297 			String subsection, String name) {
298 		String[] values = config.getStringList(section, subsection, name);
299 		List<RefSpec> result = new ArrayList<>(values.length);
300 		for (String spec : values) {
301 			result.add(new RefSpec(spec));
302 		}
303 		return result;
304 	}
305 }