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 }