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