1 /* 2 * Copyright (c) 2002-2012, the original author or authors. 3 * 4 * This software is distributable under the BSD license. See the terms of the 5 * BSD license in the documentation provided with this software. 6 * 7 * http://www.opensource.org/licenses/bsd-license.php 8 */ 9 package jline.internal; 10 11 import java.io.BufferedInputStream; 12 import java.io.File; 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.net.URL; 16 import java.nio.charset.Charset; 17 import java.util.Map; 18 import java.util.Properties; 19 20 import static jline.internal.Preconditions.checkNotNull; 21 22 /** 23 * Provides access to configuration values. 24 * 25 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> 26 * @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a> 27 * @since 2.4 28 */ 29 public class Configuration 30 { 31 /** 32 * System property which can point to a file or URL containing configuration properties to load. 33 * 34 * @since 2.7 35 */ 36 public static final String JLINE_CONFIGURATION = "jline.configuration"; 37 38 /** 39 * Default configuration file name loaded from user's home directory. 40 */ 41 public static final String JLINE_RC = ".jline.rc"; 42 43 private static volatile Properties properties; 44 45 private static Properties initProperties() { 46 URL url = determineUrl(); 47 Properties props = new Properties(); 48 try { 49 loadProperties(url, props); 50 } 51 catch (IOException e) { 52 // debug here instead of warn, as this can happen normally if default jline.rc file is missing 53 Log.debug("Unable to read configuration from: ", url, e); 54 } 55 return props; 56 } 57 58 private static void loadProperties(final URL url, final Properties props) throws IOException { 59 Log.debug("Loading properties from: ", url); 60 InputStream input = url.openStream(); 61 try { 62 props.load(new BufferedInputStream(input)); 63 } 64 finally { 65 try { 66 input.close(); 67 } 68 catch (IOException e) { 69 // ignore 70 } 71 } 72 73 if (Log.DEBUG) { 74 Log.debug("Loaded properties:"); 75 for (Map.Entry<Object,Object> entry : props.entrySet()) { 76 Log.debug(" ", entry.getKey(), "=", entry.getValue()); 77 } 78 } 79 } 80 81 private static URL determineUrl() { 82 // See if user has customized the configuration location via sysprop 83 String tmp = System.getProperty(JLINE_CONFIGURATION); 84 if (tmp != null) { 85 return Urls.create(tmp); 86 } 87 else { 88 // Otherwise try the default 89 File file = new File(getUserHome(), JLINE_RC); 90 return Urls.create(file); 91 } 92 } 93 94 /** 95 * @since 2.7 96 */ 97 public static void reset() { 98 Log.debug("Resetting"); 99 properties = null; 100 101 // force new properties to load 102 getProperties(); 103 } 104 105 /** 106 * @since 2.7 107 */ 108 public static Properties getProperties() { 109 // Not sure its worth to guard this with any synchronization, volatile field probably sufficient 110 if (properties == null) { 111 properties = initProperties(); 112 } 113 return properties; 114 } 115 116 public static String getString(final String name, final String defaultValue) { 117 checkNotNull(name); 118 119 String value; 120 121 // Check sysprops first, it always wins 122 value = System.getProperty(name); 123 124 if (value == null) { 125 // Next try userprops 126 value = getProperties().getProperty(name); 127 128 if (value == null) { 129 // else use the default 130 value = defaultValue; 131 } 132 } 133 134 return value; 135 } 136 137 public static String getString(final String name) { 138 return getString(name, null); 139 } 140 141 public static boolean getBoolean(final String name, final boolean defaultValue) { 142 String value = getString(name); 143 if (value == null) { 144 return defaultValue; 145 } 146 return value.length() == 0 147 || value.equalsIgnoreCase("1") 148 || value.equalsIgnoreCase("on") 149 || value.equalsIgnoreCase("true"); 150 } 151 152 /** 153 * @since 2.6 154 */ 155 public static int getInteger(final String name, final int defaultValue) { 156 String str = getString(name); 157 if (str == null) { 158 return defaultValue; 159 } 160 return Integer.parseInt(str); 161 } 162 163 /** 164 * @since 2.6 165 */ 166 public static long getLong(final String name, final long defaultValue) { 167 String str = getString(name); 168 if (str == null) { 169 return defaultValue; 170 } 171 return Long.parseLong(str); 172 } 173 174 // 175 // System property helpers 176 // 177 178 /** 179 * @since 2.7 180 */ 181 public static String getLineSeparator() { 182 return System.getProperty("line.separator"); 183 } 184 185 public static File getUserHome() { 186 return new File(System.getProperty("user.home")); 187 } 188 189 public static String getOsName() { 190 return System.getProperty("os.name").toLowerCase(); 191 } 192 193 /** 194 * @since 2.7 195 */ 196 public static boolean isWindows() { 197 return getOsName().startsWith("windows"); 198 } 199 200 // FIXME: Sort out use of property access of file.encoding in InputStreamReader, consolidate should configuration access here 201 202 public static String getFileEncoding() { 203 return System.getProperty("file.encoding"); 204 } 205 206 /** 207 * Get the default encoding. Will first look at the LC_CTYPE environment variable, then the input.encoding 208 * system property, then the default charset according to the JVM. 209 * 210 * @return The default encoding to use when none is specified. 211 */ 212 public static String getEncoding() { 213 // LC_CTYPE is usually in the form en_US.UTF-8 214 String envEncoding = extractEncodingFromCtype(System.getenv("LC_CTYPE")); 215 if (envEncoding != null) { 216 return envEncoding; 217 } 218 return System.getProperty("input.encoding", Charset.defaultCharset().name()); 219 } 220 221 /** 222 * Parses the LC_CTYPE value to extract the encoding according to the POSIX standard, which says that the LC_CTYPE 223 * environment variable may be of the format <code>[language[_territory][.codeset][@modifier]]</code> 224 * 225 * @param ctype The ctype to parse, may be null 226 * @return The encoding, if one was present, otherwise null 227 */ 228 static String extractEncodingFromCtype(String ctype) { 229 if (ctype != null && ctype.indexOf('.') > 0) { 230 String encodingAndModifier = ctype.substring(ctype.indexOf('.') + 1); 231 if (encodingAndModifier.indexOf('@') > 0) { 232 return encodingAndModifier.substring(0, encodingAndModifier.indexOf('@')); 233 } else { 234 return encodingAndModifier; 235 } 236 } 237 return null; 238 } 239 }