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 TypedConfigGetter} that throws {@link IllegalArgumentException} on
61   * invalid values.
62   *
63   * @since 4.9
64   */
65  public class DefaultTypedConfigGetter implements TypedConfigGetter {
66  
67  	@Override
68  	public boolean getBoolean(Config config, String section, String subsection,
69  			String name, boolean defaultValue) {
70  		String n = config.getRawString(section, subsection, name);
71  		if (n == null) {
72  			return defaultValue;
73  		}
74  		if (Config.MAGIC_EMPTY_VALUE == n) {
75  			return true;
76  		}
77  		try {
78  			return StringUtils.toBoolean(n);
79  		} catch (IllegalArgumentException err) {
80  			throw new IllegalArgumentException(MessageFormat.format(
81  					JGitText.get().invalidBooleanValue, section, name, n));
82  		}
83  	}
84  
85  	@Override
86  	public <T extends Enum<?>> T getEnum(Config config, T[] all, String section,
87  			String subsection, String name, T defaultValue) {
88  		String value = config.getString(section, subsection, name);
89  		if (value == null) {
90  			return defaultValue;
91  		}
92  		if (all[0] instanceof ConfigEnum) {
93  			for (T t : all) {
94  				if (((ConfigEnum) t).matchConfigValue(value)) {
95  					return t;
96  				}
97  			}
98  		}
99  
100 		String n = value.replace(' ', '_');
101 
102 		// Because of c98abc9c0586c73ef7df4172644b7dd21c979e9d being used in
103 		// the real world before its breakage was fully understood, we must
104 		// also accept '-' as though it were ' '.
105 		n = n.replace('-', '_');
106 
107 		T trueState = null;
108 		T falseState = null;
109 		for (T e : all) {
110 			if (StringUtils.equalsIgnoreCase(e.name(), n)) {
111 				return e;
112 			} else if (StringUtils.equalsIgnoreCase(e.name(), "TRUE")) { //$NON-NLS-1$
113 				trueState = e;
114 			} else if (StringUtils.equalsIgnoreCase(e.name(), "FALSE")) { //$NON-NLS-1$
115 				falseState = e;
116 			}
117 		}
118 
119 		// This is an odd little fallback. C Git sometimes allows boolean
120 		// values in a tri-state with other things. If we have both a true
121 		// and a false value in our enumeration, assume its one of those.
122 		//
123 		if (trueState != null && falseState != null) {
124 			try {
125 				return StringUtils.toBoolean(n) ? trueState : falseState;
126 			} catch (IllegalArgumentException err) {
127 				// Fall through and use our custom error below.
128 			}
129 		}
130 
131 		if (subsection != null) {
132 			throw new IllegalArgumentException(
133 					MessageFormat.format(JGitText.get().enumValueNotSupported3,
134 							section, subsection, name, value));
135 		} else {
136 			throw new IllegalArgumentException(
137 					MessageFormat.format(JGitText.get().enumValueNotSupported2,
138 							section, name, value));
139 		}
140 	}
141 
142 	@Override
143 	public int getInt(Config config, String section, String subsection,
144 			String name, int defaultValue) {
145 		long val = config.getLong(section, subsection, name, defaultValue);
146 		if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) {
147 			return (int) val;
148 		}
149 		throw new IllegalArgumentException(MessageFormat
150 				.format(JGitText.get().integerValueOutOfRange, section, name));
151 	}
152 
153 	@Override
154 	public long getLong(Config config, String section, String subsection,
155 			String name, long defaultValue) {
156 		final String str = config.getString(section, subsection, name);
157 		if (str == null) {
158 			return defaultValue;
159 		}
160 		String n = str.trim();
161 		if (n.length() == 0) {
162 			return defaultValue;
163 		}
164 		long mul = 1;
165 		switch (StringUtils.toLowerCase(n.charAt(n.length() - 1))) {
166 		case 'g':
167 			mul = Config.GiB;
168 			break;
169 		case 'm':
170 			mul = Config.MiB;
171 			break;
172 		case 'k':
173 			mul = Config.KiB;
174 			break;
175 		}
176 		if (mul > 1) {
177 			n = n.substring(0, n.length() - 1).trim();
178 		}
179 		if (n.length() == 0) {
180 			return defaultValue;
181 		}
182 		try {
183 			return mul * Long.parseLong(n);
184 		} catch (NumberFormatException nfe) {
185 			throw new IllegalArgumentException(MessageFormat.format(
186 					JGitText.get().invalidIntegerValue, section, name, str));
187 		}
188 	}
189 
190 	@Override
191 	public long getTimeUnit(Config config, String section, String subsection,
192 			String name, long defaultValue, TimeUnit wantUnit) {
193 		String valueString = config.getString(section, subsection, name);
194 
195 		if (valueString == null) {
196 			return defaultValue;
197 		}
198 
199 		String s = valueString.trim();
200 		if (s.length() == 0) {
201 			return defaultValue;
202 		}
203 
204 		if (s.startsWith("-")/* negative */) { //$NON-NLS-1$
205 			throw notTimeUnit(section, subsection, name, valueString);
206 		}
207 
208 		Matcher m = Pattern.compile("^(0|[1-9][0-9]*)\\s*(.*)$") //$NON-NLS-1$
209 				.matcher(valueString);
210 		if (!m.matches()) {
211 			return defaultValue;
212 		}
213 
214 		String digits = m.group(1);
215 		String unitName = m.group(2).trim();
216 
217 		TimeUnit inputUnit;
218 		int inputMul;
219 
220 		if (unitName.isEmpty()) {
221 			inputUnit = wantUnit;
222 			inputMul = 1;
223 
224 		} else if (match(unitName, "ms", "milliseconds")) { //$NON-NLS-1$ //$NON-NLS-2$
225 			inputUnit = TimeUnit.MILLISECONDS;
226 			inputMul = 1;
227 
228 		} else if (match(unitName, "s", "sec", "second", "seconds")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
229 			inputUnit = TimeUnit.SECONDS;
230 			inputMul = 1;
231 
232 		} else if (match(unitName, "m", "min", "minute", "minutes")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
233 			inputUnit = TimeUnit.MINUTES;
234 			inputMul = 1;
235 
236 		} else if (match(unitName, "h", "hr", "hour", "hours")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
237 			inputUnit = TimeUnit.HOURS;
238 			inputMul = 1;
239 
240 		} else if (match(unitName, "d", "day", "days")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
241 			inputUnit = TimeUnit.DAYS;
242 			inputMul = 1;
243 
244 		} else if (match(unitName, "w", "week", "weeks")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
245 			inputUnit = TimeUnit.DAYS;
246 			inputMul = 7;
247 
248 		} else if (match(unitName, "mon", "month", "months")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
249 			inputUnit = TimeUnit.DAYS;
250 			inputMul = 30;
251 
252 		} else if (match(unitName, "y", "year", "years")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
253 			inputUnit = TimeUnit.DAYS;
254 			inputMul = 365;
255 
256 		} else {
257 			throw notTimeUnit(section, subsection, name, valueString);
258 		}
259 
260 		try {
261 			return wantUnit.convert(Long.parseLong(digits) * inputMul,
262 					inputUnit);
263 		} catch (NumberFormatException nfe) {
264 			throw notTimeUnit(section, subsection, unitName, valueString);
265 		}
266 	}
267 
268 	private static boolean match(final String a, final String... cases) {
269 		for (final String b : cases) {
270 			if (b != null && b.equalsIgnoreCase(a)) {
271 				return true;
272 			}
273 		}
274 		return false;
275 	}
276 
277 	private static IllegalArgumentException notTimeUnit(String section,
278 			String subsection, String name, String valueString) {
279 		if (subsection != null) {
280 			return new IllegalArgumentException(
281 					MessageFormat.format(JGitText.get().invalidTimeUnitValue3,
282 							section, subsection, name, valueString));
283 		}
284 		return new IllegalArgumentException(
285 				MessageFormat.format(JGitText.get().invalidTimeUnitValue2,
286 						section, name, valueString));
287 	}
288 
289 	@Override
290 	public @NonNull List<RefSpec> getRefSpecs(Config config, String section,
291 			String subsection, String name) {
292 		String[] values = config.getStringList(section, subsection, name);
293 		List<RefSpec> result = new ArrayList<>(values.length);
294 		for (String spec : values) {
295 			result.add(new RefSpec(spec));
296 		}
297 		return result;
298 	}
299 }