View Javadoc
1   /*
2    * Copyright (C) 2009, Google Inc.
3    * Copyright (C) 2009, Robin Rosenberg <robin.rosenberg@dewire.com>
4    * Copyright (C) 2009, Yann Simon <yann.simon.fr@gmail.com>
5    * Copyright (C) 2012, Daniel Megert <daniel_megert@ch.ibm.com>
6    * and other copyright owners as documented in the project's IP log.
7    *
8    * This program and the accompanying materials are made available
9    * under the terms of the Eclipse Distribution License v1.0 which
10   * accompanies this distribution, is reproduced below, and is
11   * available at http://www.eclipse.org/org/documents/edl-v10.php
12   *
13   * All rights reserved.
14   *
15   * Redistribution and use in source and binary forms, with or
16   * without modification, are permitted provided that the following
17   * conditions are met:
18   *
19   * - Redistributions of source code must retain the above copyright
20   *   notice, this list of conditions and the following disclaimer.
21   *
22   * - Redistributions in binary form must reproduce the above
23   *   copyright notice, this list of conditions and the following
24   *   disclaimer in the documentation and/or other materials provided
25   *   with the distribution.
26   *
27   * - Neither the name of the Eclipse Foundation, Inc. nor the
28   *   names of its contributors may be used to endorse or promote
29   *   products derived from this software without specific prior
30   *   written permission.
31   *
32   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
33   * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
34   * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
36   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
39   * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
40   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
41   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
42   * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
43   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
44   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45   */
46  
47  package org.eclipse.jgit.util;
48  
49  import java.io.File;
50  import java.net.InetAddress;
51  import java.net.UnknownHostException;
52  import java.security.AccessController;
53  import java.security.PrivilegedAction;
54  import java.text.DateFormat;
55  import java.text.SimpleDateFormat;
56  import java.util.Locale;
57  import java.util.TimeZone;
58  
59  import org.eclipse.jgit.storage.file.FileBasedConfig;
60  import org.eclipse.jgit.errors.CorruptObjectException;
61  import org.eclipse.jgit.lib.Config;
62  import org.eclipse.jgit.lib.ObjectChecker;
63  
64  /**
65   * Interface to read values from the system.
66   * <p>
67   * When writing unit tests, extending this interface with a custom class
68   * permits to simulate an access to a system variable or property and
69   * permits to control the user's global configuration.
70   * </p>
71   */
72  public abstract class SystemReader {
73  	private static final SystemReader DEFAULT;
74  
75  	private static Boolean isMacOS;
76  
77  	private static Boolean isWindows;
78  
79  	static {
80  		SystemReader r = new Default();
81  		r.init();
82  		DEFAULT = r;
83  	}
84  
85  	private static class Default extends SystemReader {
86  		private volatile String hostname;
87  
88  		public String getenv(String variable) {
89  			return System.getenv(variable);
90  		}
91  
92  		public String getProperty(String key) {
93  			return System.getProperty(key);
94  		}
95  
96  		public FileBasedConfig openSystemConfig(Config parent, FS fs) {
97  			File configFile = fs.getGitSystemConfig();
98  			if (configFile == null) {
99  				return new FileBasedConfig(null, fs) {
100 					public void load() {
101 						// empty, do not load
102 					}
103 
104 					public boolean isOutdated() {
105 						// regular class would bomb here
106 						return false;
107 					}
108 				};
109 			}
110 			return new FileBasedConfig(parent, configFile, fs);
111 		}
112 
113 		public FileBasedConfig openUserConfig(Config parent, FS fs) {
114 			final File home = fs.userHome();
115 			return new FileBasedConfig(parent, new File(home, ".gitconfig"), fs); //$NON-NLS-1$
116 		}
117 
118 		public String getHostname() {
119 			if (hostname == null) {
120 				try {
121 					InetAddress localMachine = InetAddress.getLocalHost();
122 					hostname = localMachine.getCanonicalHostName();
123 				} catch (UnknownHostException e) {
124 					// we do nothing
125 					hostname = "localhost"; //$NON-NLS-1$
126 				}
127 				assert hostname != null;
128 			}
129 			return hostname;
130 		}
131 
132 		@Override
133 		public long getCurrentTime() {
134 			return System.currentTimeMillis();
135 		}
136 
137 		@Override
138 		public int getTimezone(long when) {
139 			return getTimeZone().getOffset(when) / (60 * 1000);
140 		}
141 	}
142 
143 	private static SystemReader INSTANCE = DEFAULT;
144 
145 	/** @return the live instance to read system properties. */
146 	public static SystemReader getInstance() {
147 		return INSTANCE;
148 	}
149 
150 	/**
151 	 * @param newReader
152 	 *            the new instance to use when accessing properties, or null for
153 	 *            the default instance.
154 	 */
155 	public static void setInstance(SystemReader newReader) {
156 		isMacOS = null;
157 		isWindows = null;
158 		if (newReader == null)
159 			INSTANCE = DEFAULT;
160 		else {
161 			newReader.init();
162 			INSTANCE = newReader;
163 		}
164 	}
165 
166 	private ObjectChecker platformChecker;
167 
168 	private void init() {
169 		// Creating ObjectChecker must be deferred. Unit tests change
170 		// behavior of is{Windows,MacOS} in constructor of subclass.
171 		if (platformChecker == null)
172 			setPlatformChecker();
173 	}
174 
175 	/**
176 	 * Should be used in tests when the platform is explicitly changed.
177 	 *
178 	 * @since 3.6
179 	 */
180 	protected final void setPlatformChecker() {
181 		platformChecker = new ObjectChecker()
182 			.setSafeForWindows(isWindows())
183 			.setSafeForMacOS(isMacOS());
184 	}
185 
186 	/**
187 	 * Gets the hostname of the local host. If no hostname can be found, the
188 	 * hostname is set to the default value "localhost".
189 	 *
190 	 * @return the canonical hostname
191 	 */
192 	public abstract String getHostname();
193 
194 	/**
195 	 * @param variable system variable to read
196 	 * @return value of the system variable
197 	 */
198 	public abstract String getenv(String variable);
199 
200 	/**
201 	 * @param key of the system property to read
202 	 * @return value of the system property
203 	 */
204 	public abstract String getProperty(String key);
205 
206 	/**
207 	 * @param parent
208 	 *            a config with values not found directly in the returned config
209 	 * @param fs
210 	 *            the file system abstraction which will be necessary to perform
211 	 *            certain file system operations.
212 	 * @return the git configuration found in the user home
213 	 */
214 	public abstract FileBasedConfig openUserConfig(Config parent, FS fs);
215 
216 	/**
217 	 * @param parent
218 	 *            a config with values not found directly in the returned
219 	 *            config. Null is a reasonable value here.
220 	 * @param fs
221 	 *            the file system abstraction which will be necessary to perform
222 	 *            certain file system operations.
223 	 * @return the gitonfig configuration found in the system-wide "etc"
224 	 *         directory
225 	 */
226 	public abstract FileBasedConfig openSystemConfig(Config parent, FS fs);
227 
228 	/**
229 	 * @return the current system time
230 	 */
231 	public abstract long getCurrentTime();
232 
233 	/**
234 	 * @param when TODO
235 	 * @return the local time zone
236 	 */
237 	public abstract int getTimezone(long when);
238 
239 	/**
240 	 * @return system time zone, possibly mocked for testing
241 	 * @since 1.2
242 	 */
243 	public TimeZone getTimeZone() {
244 		return TimeZone.getDefault();
245 	}
246 
247 	/**
248 	 * @return the locale to use
249 	 * @since 1.2
250 	 */
251 	public Locale getLocale() {
252 		return Locale.getDefault();
253 	}
254 
255 	/**
256 	 * Returns a simple date format instance as specified by the given pattern.
257 	 *
258 	 * @param pattern
259 	 *            the pattern as defined in
260 	 *            {@link SimpleDateFormat#SimpleDateFormat(String)}
261 	 * @return the simple date format
262 	 * @since 2.0
263 	 */
264 	public SimpleDateFormat getSimpleDateFormat(String pattern) {
265 		return new SimpleDateFormat(pattern);
266 	}
267 
268 	/**
269 	 * Returns a simple date format instance as specified by the given pattern.
270 	 *
271 	 * @param pattern
272 	 *            the pattern as defined in
273 	 *            {@link SimpleDateFormat#SimpleDateFormat(String)}
274 	 * @param locale
275 	 *            locale to be used for the {@code SimpleDateFormat}
276 	 * @return the simple date format
277 	 * @since 3.2
278 	 */
279 	public SimpleDateFormat getSimpleDateFormat(String pattern, Locale locale) {
280 		return new SimpleDateFormat(pattern, locale);
281 	}
282 
283 	/**
284 	 * Returns a date/time format instance for the given styles.
285 	 *
286 	 * @param dateStyle
287 	 *            the date style as specified in
288 	 *            {@link DateFormat#getDateTimeInstance(int, int)}
289 	 * @param timeStyle
290 	 *            the time style as specified in
291 	 *            {@link DateFormat#getDateTimeInstance(int, int)}
292 	 * @return the date format
293 	 * @since 2.0
294 	 */
295 	public DateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
296 		return DateFormat.getDateTimeInstance(dateStyle, timeStyle);
297 	}
298 
299 	/**
300 	 * @return true if we are running on a Windows.
301 	 */
302 	public boolean isWindows() {
303 		if (isWindows == null) {
304 			String osDotName = getOsName();
305 			isWindows = Boolean.valueOf(osDotName.startsWith("Windows")); //$NON-NLS-1$
306 		}
307 		return isWindows.booleanValue();
308 	}
309 
310 	/**
311 	 * @return true if we are running on Mac OS X
312 	 */
313 	public boolean isMacOS() {
314 		if (isMacOS == null) {
315 			String osDotName = getOsName();
316 			isMacOS = Boolean.valueOf(
317 					"Mac OS X".equals(osDotName) || "Darwin".equals(osDotName)); //$NON-NLS-1$ //$NON-NLS-2$
318 		}
319 		return isMacOS.booleanValue();
320 	}
321 
322 	private String getOsName() {
323 		return AccessController.doPrivileged(new PrivilegedAction<String>() {
324 			public String run() {
325 				return getProperty("os.name"); //$NON-NLS-1$
326 			}
327 		});
328 	}
329 
330 	/**
331 	 * Check tree path entry for validity.
332 	 * <p>
333 	 * Scans a multi-directory path string such as {@code "src/main.c"}.
334 	 *
335 	 * @param path path string to scan.
336 	 * @throws CorruptObjectException path is invalid.
337 	 * @since 3.6
338 	 */
339 	public void checkPath(String path) throws CorruptObjectException {
340 		platformChecker.checkPath(path);
341 	}
342 }