/*
* Copyright (c) 2002-2016, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
*
* http://www.opensource.org/licenses/bsd-license.php
*/
package jline.internal;
import java.io.
BufferedInputStream;
import java.io.
File;
import java.io.
IOException;
import java.io.
InputStream;
import java.io.
FileNotFoundException;
import java.net.
URL;
import java.nio.charset.
Charset;
import java.nio.charset.
IllegalCharsetNameException;
import java.util.
Map;
import java.util.
Properties;
import static jline.internal.
Preconditions.checkNotNull;
/**
* Provides access to configuration values.
*
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
* @since 2.4
*/
public class
Configuration
{
/**
* System property which can point to a file or URL containing configuration properties to load.
*
* @since 2.7
*/
public static final
String JLINE_CONFIGURATION = "jline.configuration";
/**
* Default configuration file name loaded from user's home directory.
*/
public static final
String JLINE_RC = ".jline.rc";
private static volatile
Properties properties;
private static
Properties initProperties() {
URL url =
determineUrl();
Properties props = new
Properties();
try {
loadProperties(
url,
props);
}
catch (
FileNotFoundException e) {
// debug here and no stack trace, as this can happen normally if default jline.rc file is missing
Log.
debug("Unable to read configuration: ",
e.
toString());
}
catch (
IOException e) {
Log.
warn("Unable to read configuration from: ",
url,
e);
}
return
props;
}
private static void
loadProperties(final
URL url, final
Properties props) throws
IOException {
Log.
debug("Loading properties from: ",
url);
InputStream input =
url.
openStream();
try {
props.
load(new
BufferedInputStream(
input));
}
finally {
try {
input.
close();
}
catch (
IOException e) {
// ignore
}
}
if (
Log.
DEBUG) {
Log.
debug("Loaded properties:");
for (
Map.
Entry<
Object,
Object>
entry :
props.
entrySet()) {
Log.
debug(" ",
entry.
getKey(), "=",
entry.
getValue());
}
}
}
private static
URL determineUrl() {
// See if user has customized the configuration location via sysprop
String tmp =
System.
getProperty(
JLINE_CONFIGURATION);
if (
tmp != null) {
return
Urls.
create(
tmp);
}
else {
// Otherwise try the default
File file = new
File(
getUserHome(),
JLINE_RC);
return
Urls.
create(
file);
}
}
/**
* @since 2.7
*/
public static void
reset() {
Log.
debug("Resetting");
properties = null;
// force new properties to load
getProperties();
}
/**
* @since 2.7
*/
public static
Properties getProperties() {
// Not sure its worth to guard this with any synchronization, volatile field probably sufficient
if (
properties == null) {
properties =
initProperties();
}
return
properties;
}
public static
String getString(final
String name, final
String defaultValue) {
checkNotNull(
name);
String value;
// Check sysprops first, it always wins
value =
System.
getProperty(
name);
if (
value == null) {
// Next try userprops
value =
getProperties().
getProperty(
name);
if (
value == null) {
// else use the default
value =
defaultValue;
}
}
return
value;
}
public static
String getString(final
String name) {
return
getString(
name, null);
}
public static boolean
getBoolean(final
String name) {
return
getBoolean(
name, false);
}
public static boolean
getBoolean(final
String name, final boolean
defaultValue) {
String value =
getString(
name);
if (
value == null) {
return
defaultValue;
}
return
value.
length() == 0
||
value.
equalsIgnoreCase("1")
||
value.
equalsIgnoreCase("on")
||
value.
equalsIgnoreCase("true");
}
/**
* @since 2.6
*/
public static int
getInteger(final
String name, final int
defaultValue) {
String str =
getString(
name);
if (
str == null) {
return
defaultValue;
}
return
Integer.
parseInt(
str);
}
/**
* @since 2.6
*/
public static long
getLong(final
String name, final long
defaultValue) {
String str =
getString(
name);
if (
str == null) {
return
defaultValue;
}
return
Long.
parseLong(
str);
}
//
// System property helpers
//
/**
* @since 2.7
*/
public static
String getLineSeparator() {
return
System.
getProperty("line.separator");
}
public static
File getUserHome() {
return new
File(
System.
getProperty("user.home"));
}
public static
String getOsName() {
return
System.
getProperty("os.name").
toLowerCase();
}
/**
* @since 2.7
*/
public static boolean
isWindows() {
return
getOsName().
startsWith("windows");
}
public static boolean
isHpux() {
return
getOsName().
startsWith("hp");
}
// FIXME: Sort out use of property access of file.encoding in InputStreamReader, should consolidate configuration access here
public static
String getFileEncoding() {
return
System.
getProperty("file.encoding");
}
/**
* Get the default encoding. Will first look at the LC_ALL, LC_CTYPE, and LANG environment variables, then the input.encoding
* system property, then the default charset according to the JVM.
*
* @return The default encoding to use when none is specified.
*/
public static
String getEncoding() {
// Check for standard locale environment variables, in order of precedence, first.
// See http://www.gnu.org/s/libc/manual/html_node/Locale-Categories.html
for (
String envOption : new
String[]{"LC_ALL", "LC_CTYPE", "LANG"}) {
String envEncoding =
extractEncodingFromCtype(
System.
getenv(
envOption));
if (
envEncoding != null) {
try {
if (
Charset.
isSupported(
envEncoding)) {
return
envEncoding;
}
} catch (
IllegalCharsetNameException e) {
continue;
}
}
}
return
getString("input.encoding",
Charset.
defaultCharset().
name());
}
/**
* Parses the LC_CTYPE value to extract the encoding according to the POSIX standard, which says that the LC_CTYPE
* environment variable may be of the format <code>[language[_territory][.codeset][@modifier]]</code>
*
* @param ctype The ctype to parse, may be null
* @return The encoding, if one was present, otherwise null
*/
static
String extractEncodingFromCtype(
String ctype) {
if (
ctype != null &&
ctype.
indexOf('.') > 0) {
String encodingAndModifier =
ctype.
substring(
ctype.
indexOf('.') + 1);
if (
encodingAndModifier.
indexOf('@') > 0) {
return
encodingAndModifier.
substring(0,
encodingAndModifier.
indexOf('@'));
} else {
return
encodingAndModifier;
}
}
return null;
}
}