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.errors.CorruptObjectException;
60 import org.eclipse.jgit.lib.Config;
61 import org.eclipse.jgit.lib.ObjectChecker;
62 import org.eclipse.jgit.storage.file.FileBasedConfig;
63 import org.eclipse.jgit.util.time.MonotonicClock;
64 import org.eclipse.jgit.util.time.MonotonicSystemClock;
65
66 /**
67 * Interface to read values from the system.
68 * <p>
69 * When writing unit tests, extending this interface with a custom class
70 * permits to simulate an access to a system variable or property and
71 * permits to control the user's global configuration.
72 * </p>
73 */
74 public abstract class SystemReader {
75 private static final SystemReader DEFAULT;
76
77 private static Boolean isMacOS;
78
79 private static Boolean isWindows;
80
81 static {
82 SystemReader r = new Default();
83 r.init();
84 DEFAULT = r;
85 }
86
87 private static class Default extends SystemReader {
88 private volatile String hostname;
89
90 @Override
91 public String getenv(String variable) {
92 return System.getenv(variable);
93 }
94
95 @Override
96 public String getProperty(String key) {
97 return System.getProperty(key);
98 }
99
100 @Override
101 public FileBasedConfig openSystemConfig(Config parent, FS fs) {
102 File configFile = fs.getGitSystemConfig();
103 if (configFile == null) {
104 return new FileBasedConfig(null, fs) {
105 @Override
106 public void load() {
107 // empty, do not load
108 }
109
110 @Override
111 public boolean isOutdated() {
112 // regular class would bomb here
113 return false;
114 }
115 };
116 }
117 return new FileBasedConfig(parent, configFile, fs);
118 }
119
120 @Override
121 public FileBasedConfig openUserConfig(Config parent, FS fs) {
122 final File home = fs.userHome();
123 return new FileBasedConfig(parent, new File(home, ".gitconfig"), fs); //$NON-NLS-1$
124 }
125
126 @Override
127 public String getHostname() {
128 if (hostname == null) {
129 try {
130 InetAddress localMachine = InetAddress.getLocalHost();
131 hostname = localMachine.getCanonicalHostName();
132 } catch (UnknownHostException e) {
133 // we do nothing
134 hostname = "localhost"; //$NON-NLS-1$
135 }
136 assert hostname != null;
137 }
138 return hostname;
139 }
140
141 @Override
142 public long getCurrentTime() {
143 return System.currentTimeMillis();
144 }
145
146 @Override
147 public int getTimezone(long when) {
148 return getTimeZone().getOffset(when) / (60 * 1000);
149 }
150 }
151
152 private static SystemReader INSTANCE = DEFAULT;
153
154 /** @return the live instance to read system properties. */
155 public static SystemReader getInstance() {
156 return INSTANCE;
157 }
158
159 /**
160 * @param newReader
161 * the new instance to use when accessing properties, or null for
162 * the default instance.
163 */
164 public static void setInstance(SystemReader newReader) {
165 isMacOS = null;
166 isWindows = null;
167 if (newReader == null)
168 INSTANCE = DEFAULT;
169 else {
170 newReader.init();
171 INSTANCE = newReader;
172 }
173 }
174
175 private ObjectChecker platformChecker;
176
177 private void init() {
178 // Creating ObjectChecker must be deferred. Unit tests change
179 // behavior of is{Windows,MacOS} in constructor of subclass.
180 if (platformChecker == null)
181 setPlatformChecker();
182 }
183
184 /**
185 * Should be used in tests when the platform is explicitly changed.
186 *
187 * @since 3.6
188 */
189 protected final void setPlatformChecker() {
190 platformChecker = new ObjectChecker()
191 .setSafeForWindows(isWindows())
192 .setSafeForMacOS(isMacOS());
193 }
194
195 /**
196 * Gets the hostname of the local host. If no hostname can be found, the
197 * hostname is set to the default value "localhost".
198 *
199 * @return the canonical hostname
200 */
201 public abstract String getHostname();
202
203 /**
204 * @param variable system variable to read
205 * @return value of the system variable
206 */
207 public abstract String getenv(String variable);
208
209 /**
210 * @param key of the system property to read
211 * @return value of the system property
212 */
213 public abstract String getProperty(String key);
214
215 /**
216 * @param parent
217 * a config with values not found directly in the returned config
218 * @param fs
219 * the file system abstraction which will be necessary to perform
220 * certain file system operations.
221 * @return the git configuration found in the user home
222 */
223 public abstract FileBasedConfig openUserConfig(Config parent, FS fs);
224
225 /**
226 * @param parent
227 * a config with values not found directly in the returned
228 * config. Null is a reasonable value here.
229 * @param fs
230 * the file system abstraction which will be necessary to perform
231 * certain file system operations.
232 * @return the gitonfig configuration found in the system-wide "etc"
233 * directory
234 */
235 public abstract FileBasedConfig openSystemConfig(Config parent, FS fs);
236
237 /**
238 * @return the current system time
239 */
240 public abstract long getCurrentTime();
241
242 /**
243 * @return clock instance preferred by this system.
244 * @since 4.6
245 */
246 public MonotonicClock getClock() {
247 return new MonotonicSystemClock();
248 }
249
250 /**
251 * @param when TODO
252 * @return the local time zone
253 */
254 public abstract int getTimezone(long when);
255
256 /**
257 * @return system time zone, possibly mocked for testing
258 * @since 1.2
259 */
260 public TimeZone getTimeZone() {
261 return TimeZone.getDefault();
262 }
263
264 /**
265 * @return the locale to use
266 * @since 1.2
267 */
268 public Locale getLocale() {
269 return Locale.getDefault();
270 }
271
272 /**
273 * Returns a simple date format instance as specified by the given pattern.
274 *
275 * @param pattern
276 * the pattern as defined in
277 * {@link SimpleDateFormat#SimpleDateFormat(String)}
278 * @return the simple date format
279 * @since 2.0
280 */
281 public SimpleDateFormat getSimpleDateFormat(String pattern) {
282 return new SimpleDateFormat(pattern);
283 }
284
285 /**
286 * Returns a simple date format instance as specified by the given pattern.
287 *
288 * @param pattern
289 * the pattern as defined in
290 * {@link SimpleDateFormat#SimpleDateFormat(String)}
291 * @param locale
292 * locale to be used for the {@code SimpleDateFormat}
293 * @return the simple date format
294 * @since 3.2
295 */
296 public SimpleDateFormat getSimpleDateFormat(String pattern, Locale locale) {
297 return new SimpleDateFormat(pattern, locale);
298 }
299
300 /**
301 * Returns a date/time format instance for the given styles.
302 *
303 * @param dateStyle
304 * the date style as specified in
305 * {@link DateFormat#getDateTimeInstance(int, int)}
306 * @param timeStyle
307 * the time style as specified in
308 * {@link DateFormat#getDateTimeInstance(int, int)}
309 * @return the date format
310 * @since 2.0
311 */
312 public DateFormat getDateTimeInstance(int dateStyle, int timeStyle) {
313 return DateFormat.getDateTimeInstance(dateStyle, timeStyle);
314 }
315
316 /**
317 * @return true if we are running on a Windows.
318 */
319 public boolean isWindows() {
320 if (isWindows == null) {
321 String osDotName = getOsName();
322 isWindows = Boolean.valueOf(osDotName.startsWith("Windows")); //$NON-NLS-1$
323 }
324 return isWindows.booleanValue();
325 }
326
327 /**
328 * @return true if we are running on Mac OS X
329 */
330 public boolean isMacOS() {
331 if (isMacOS == null) {
332 String osDotName = getOsName();
333 isMacOS = Boolean.valueOf(
334 "Mac OS X".equals(osDotName) || "Darwin".equals(osDotName)); //$NON-NLS-1$ //$NON-NLS-2$
335 }
336 return isMacOS.booleanValue();
337 }
338
339 private String getOsName() {
340 return AccessController.doPrivileged(new PrivilegedAction<String>() {
341 @Override
342 public String run() {
343 return getProperty("os.name"); //$NON-NLS-1$
344 }
345 });
346 }
347
348 /**
349 * Check tree path entry for validity.
350 * <p>
351 * Scans a multi-directory path string such as {@code "src/main.c"}.
352 *
353 * @param path path string to scan.
354 * @throws CorruptObjectException path is invalid.
355 * @since 3.6
356 */
357 public void checkPath(String path) throws CorruptObjectException {
358 platformChecker.checkPath(path);
359 }
360
361 /**
362 * Check tree path entry for validity.
363 * <p>
364 * Scans a multi-directory path string such as {@code "src/main.c"}.
365 *
366 * @param path
367 * path string to scan.
368 * @throws CorruptObjectException
369 * path is invalid.
370 * @since 4.2
371 */
372 public void checkPath(byte[] path) throws CorruptObjectException {
373 platformChecker.checkPath(path, 0, path.length);
374 }
375 }