Don't rely on an implicit default character set

JEP 400 (Java 18) will change the default character set to UTF-8
unconditionally.[1] Introduce SystemReader.getDefaultCharset() that
provides the locale-dependent charset the way JEP 400 recommends.

Change all code locations using Charset.defaultCharset() to use the
new SystemReader method instead.

[1] https://openjdk.java.net/jeps/400

Change-Id: I986f97a410d2fc70748b6f93228a2d45ff100b2c
Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
This commit is contained in:
Thomas Wolf 2021-09-03 19:49:27 +02:00 committed by Matthias Sohn
parent ff3c3d8ff5
commit fc6fe793ce
9 changed files with 54 additions and 17 deletions

View File

@ -19,7 +19,6 @@
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
@ -182,7 +181,7 @@ public void testReadPipePosixCommandFailure()
FS.readPipe(fs.userHome(), FS.readPipe(fs.userHome(),
new String[] { "/bin/sh", "-c", "exit 1" }, new String[] { "/bin/sh", "-c", "exit 1" },
Charset.defaultCharset().name()); SystemReader.getInstance().getDefaultCharset().name());
} }
@Test(expected = CommandFailedException.class) @Test(expected = CommandFailedException.class)
@ -192,7 +191,7 @@ public void testReadPipeCommandStartFailure()
FS.readPipe(fs.userHome(), FS.readPipe(fs.userHome(),
new String[] { "this-command-does-not-exist" }, new String[] { "this-command-does-not-exist" },
Charset.defaultCharset().name()); SystemReader.getInstance().getDefaultCharset().name());
} }
@Test @Test

View File

@ -440,6 +440,7 @@ lockOnNotHeld=Lock on {0} not held.
lockStreamClosed=Output to lock on {0} already closed lockStreamClosed=Output to lock on {0} already closed
lockStreamMultiple=Output to lock on {0} already opened lockStreamMultiple=Output to lock on {0} already opened
logInconsistentFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: {} > {}, but diff = {}. Aborting measurement at resolution {}. logInconsistentFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: {} > {}, but diff = {}. Aborting measurement at resolution {}.
logInvalidDefaultCharset=System property "native.encoding" specifies unknown character set: {}
logLargerFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: diff = {} > {} (last good value). Aborting measurement. logLargerFiletimeDiff={}: inconsistent duration from file timestamps on {}, {}: diff = {} > {} (last good value). Aborting measurement.
logSmallerFiletime={}: got smaller file timestamp on {}, {}: {} < {}. Aborting measurement at resolution {}. logSmallerFiletime={}: got smaller file timestamp on {}, {}: {} < {}. Aborting measurement at resolution {}.
logXDGConfigHomeInvalid=Environment variable XDG_CONFIG_HOME contains an invalid path {} logXDGConfigHomeInvalid=Environment variable XDG_CONFIG_HOME contains an invalid path {}

View File

@ -12,13 +12,13 @@
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import org.eclipse.jgit.api.errors.AbortedByHookException; import org.eclipse.jgit.api.errors.AbortedByHookException;
import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.util.FS; import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.ProcessResult; import org.eclipse.jgit.util.ProcessResult;
import org.eclipse.jgit.util.SystemReader;
import org.eclipse.jgit.util.io.TeeOutputStream; import org.eclipse.jgit.util.io.TeeOutputStream;
/** /**
@ -171,7 +171,8 @@ protected void doRun() throws AbortedByHookException, IOException {
getStdinArgs()); getStdinArgs());
if (result.isExecutedWithError()) { if (result.isExecutedWithError()) {
handleError(new String(errorByteArray.toByteArray(), handleError(new String(errorByteArray.toByteArray(),
Charset.defaultCharset().name()), result); SystemReader.getInstance().getDefaultCharset().name()),
result);
} }
} }

View File

@ -468,6 +468,7 @@ public static JGitText get() {
/***/ public String lockStreamClosed; /***/ public String lockStreamClosed;
/***/ public String lockStreamMultiple; /***/ public String lockStreamMultiple;
/***/ public String logInconsistentFiletimeDiff; /***/ public String logInconsistentFiletimeDiff;
/***/ public String logInvalidDefaultCharset;
/***/ public String logLargerFiletimeDiff; /***/ public String logLargerFiletimeDiff;
/***/ public String logSmallerFiletime; /***/ public String logSmallerFiletime;
/***/ public String logXDGConfigHomeInvalid; /***/ public String logXDGConfigHomeInvalid;

View File

@ -23,7 +23,6 @@
import java.io.OutputStream; import java.io.OutputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.Writer; import java.io.Writer;
import java.nio.charset.Charset;
import java.nio.file.AccessDeniedException; import java.nio.file.AccessDeniedException;
import java.nio.file.FileStore; import java.nio.file.FileStore;
import java.nio.file.Files; import java.nio.file.Files;
@ -1507,7 +1506,7 @@ protected File discoverGitSystemConfig() {
try { try {
v = readPipe(gitExe.getParentFile(), v = readPipe(gitExe.getParentFile(),
new String[] { gitExe.getPath(), "--version" }, //$NON-NLS-1$ new String[] { gitExe.getPath(), "--version" }, //$NON-NLS-1$
Charset.defaultCharset().name()); SystemReader.getInstance().getDefaultCharset().name());
} catch (CommandFailedException e) { } catch (CommandFailedException e) {
LOG.warn(e.getMessage()); LOG.warn(e.getMessage());
return null; return null;
@ -1527,7 +1526,7 @@ protected File discoverGitSystemConfig() {
w = readPipe(gitExe.getParentFile(), w = readPipe(gitExe.getParentFile(),
new String[] { gitExe.getPath(), "config", "--system", //$NON-NLS-1$ //$NON-NLS-2$ new String[] { gitExe.getPath(), "config", "--system", //$NON-NLS-1$ //$NON-NLS-2$
"--edit" }, //$NON-NLS-1$ "--edit" }, //$NON-NLS-1$
Charset.defaultCharset().name(), env); SystemReader.getInstance().getDefaultCharset().name(), env);
} catch (CommandFailedException e) { } catch (CommandFailedException e) {
LOG.warn(e.getMessage()); LOG.warn(e.getMessage());
return null; return null;

View File

@ -17,7 +17,6 @@
import java.io.IOException; import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.file.FileAlreadyExistsException; import java.nio.file.FileAlreadyExistsException;
import java.nio.file.FileStore; import java.nio.file.FileStore;
import java.nio.file.FileSystemException; import java.nio.file.FileSystemException;
@ -119,8 +118,8 @@ private static int readUmask() {
new String[] { "sh", "-c", "umask" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ new String[] { "sh", "-c", "umask" }, //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
null, null); null, null);
try (BufferedReader lineRead = new BufferedReader( try (BufferedReader lineRead = new BufferedReader(
new InputStreamReader(p.getInputStream(), Charset new InputStreamReader(p.getInputStream(), SystemReader
.defaultCharset().name()))) { .getInstance().getDefaultCharset().name()))) {
if (p.waitFor() == 0) { if (p.waitFor() == 0) {
String s = lineRead.readLine(); String s = lineRead.readLine();
if (s != null && s.matches("0?\\d{3}")) { //$NON-NLS-1$ if (s != null && s.matches("0?\\d{3}")) { //$NON-NLS-1$
@ -150,7 +149,8 @@ protected File discoverGitExe() {
try { try {
String w = readPipe(userHome(), String w = readPipe(userHome(),
new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
Charset.defaultCharset().name()); SystemReader.getInstance().getDefaultCharset()
.name());
if (!StringUtils.isEmptyOrNull(w)) { if (!StringUtils.isEmptyOrNull(w)) {
gitExe = new File(w); gitExe = new File(w);
} }
@ -168,7 +168,8 @@ protected File discoverGitExe() {
try { try {
String w = readPipe(userHome(), String w = readPipe(userHome(),
new String[] { "xcode-select", "-p" }, //$NON-NLS-1$ //$NON-NLS-2$ new String[] { "xcode-select", "-p" }, //$NON-NLS-1$ //$NON-NLS-2$
Charset.defaultCharset().name()); SystemReader.getInstance().getDefaultCharset()
.name());
if (StringUtils.isEmptyOrNull(w)) { if (StringUtils.isEmptyOrNull(w)) {
gitExe = null; gitExe = null;
} else { } else {

View File

@ -13,7 +13,6 @@
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.FileVisitOption; import java.nio.file.FileVisitOption;
import java.nio.file.FileVisitResult; import java.nio.file.FileVisitResult;
import java.nio.file.Files; import java.nio.file.Files;
@ -150,8 +149,10 @@ protected File discoverGitExe() {
String w; String w;
try { try {
w = readPipe(userHome(), w = readPipe(userHome(),
new String[]{"bash", "--login", "-c", "which git"}, // //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ new String[] { "bash", "--login", "-c", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
Charset.defaultCharset().name()); "which git" }, // //$NON-NLS-1$
SystemReader.getInstance().getDefaultCharset()
.name());
} catch (CommandFailedException e) { } catch (CommandFailedException e) {
LOG.warn(e.getMessage()); LOG.warn(e.getMessage());
return null; return null;

View File

@ -1160,7 +1160,7 @@ public static String decodeNoFallback(final Charset cs,
// Try the default character set. A small group of people // Try the default character set. A small group of people
// might actually use the same (or very similar) locale. // might actually use the same (or very similar) locale.
Charset defcs = Charset.defaultCharset(); Charset defcs = SystemReader.getInstance().getDefaultCharset();
if (!defcs.equals(cs) && !defcs.equals(UTF_8)) { if (!defcs.equals(cs) && !defcs.equals(UTF_8)) {
try { try {
return decode(b, defcs); return decode(b, defcs);

View File

@ -17,6 +17,9 @@
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.nio.charset.UnsupportedCharsetException;
import java.nio.file.InvalidPathException; import java.nio.file.InvalidPathException;
import java.nio.file.Path; import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
@ -198,6 +201,8 @@ public static void setInstance(SystemReader newReader) {
private AtomicReference<FileBasedConfig> jgitConfig = new AtomicReference<>(); private AtomicReference<FileBasedConfig> jgitConfig = new AtomicReference<>();
private volatile Charset defaultCharset;
private void init() { private void init() {
// Creating ObjectChecker must be deferred. Unit tests change // Creating ObjectChecker must be deferred. Unit tests change
// behavior of is{Windows,MacOS} in constructor of subclass. // behavior of is{Windows,MacOS} in constructor of subclass.
@ -438,6 +443,35 @@ public Locale getLocale() {
return Locale.getDefault(); return Locale.getDefault();
} }
/**
* Retrieves the default {@link Charset} depending on the system locale.
*
* @return the {@link Charset}
* @since 6.0
* @see <a href="https://openjdk.java.net/jeps/400">JEP 400</a>
*/
public Charset getDefaultCharset() {
Charset result = defaultCharset;
if (result == null) {
// JEP 400: Java 18 populates this system property.
String encoding = getProperty("native.encoding"); //$NON-NLS-1$
try {
if (!StringUtils.isEmptyOrNull(encoding)) {
result = Charset.forName(encoding);
}
} catch (IllegalCharsetNameException
| UnsupportedCharsetException e) {
LOG.error(JGitText.get().logInvalidDefaultCharset, encoding);
}
if (result == null) {
// This is always UTF-8 on Java >= 18.
result = Charset.defaultCharset();
}
defaultCharset = result;
}
return result;
}
/** /**
* Returns a simple date format instance as specified by the given pattern. * Returns a simple date format instance as specified by the given pattern.
* *