Skip to content

Commit e06da6c

Browse files
gnodetclaude
andcommitted
refactor: Replace reflection-based codepage detection with TerminalProvider method
Add getConsoleCodepage() default method to TerminalProvider interface, with overrides in JNI and FFM providers that call Kernel32.GetConsoleOutputCP(). This eliminates the fragile reflection-based approach in TerminalBuilder that coupled it to provider internals. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3ad1234 commit e06da6c

File tree

4 files changed

+50
-38
lines changed

4 files changed

+50
-38
lines changed

terminal-ffm/src/main/java/org/jline/terminal/impl/ffm/FfmTerminalProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,14 @@ public String name() {
4343
return TerminalBuilder.PROP_PROVIDER_FFM;
4444
}
4545

46+
@Override
47+
public int getConsoleCodepage() {
48+
if (OSUtils.IS_WINDOWS) {
49+
return Kernel32.GetConsoleOutputCP();
50+
}
51+
return -1;
52+
}
53+
4654
@Override
4755
public Terminal sysTerminal(
4856
String name,

terminal-jni/src/main/java/org/jline/terminal/impl/jni/JniTerminalProvider.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ public String name() {
9797
return TerminalBuilder.PROP_PROVIDER_JNI;
9898
}
9999

100+
@Override
101+
public int getConsoleCodepage() {
102+
if (OSUtils.IS_WINDOWS) {
103+
return org.jline.nativ.Kernel32.GetConsoleOutputCP();
104+
}
105+
return -1;
106+
}
107+
100108
public Pty current(SystemStream systemStream) throws IOException {
101109
String osName = System.getProperty("os.name");
102110
if (osName.startsWith("Linux")) {

terminal/src/main/java/org/jline/terminal/TerminalBuilder.java

Lines changed: 19 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -732,10 +732,6 @@ private Terminal doBuild() throws IOException {
732732
if (name == null) {
733733
name = "JLine terminal";
734734
}
735-
Charset encoding = computeEncoding();
736-
Charset stdinEncoding = computeStdinEncoding();
737-
Charset stdoutEncoding = computeStdoutEncoding();
738-
Charset stderrEncoding = computeStderrEncoding();
739735
String type = computeType();
740736

741737
String provider = this.provider;
@@ -752,6 +748,20 @@ private Terminal doBuild() throws IOException {
752748
}
753749
IllegalStateException exception = new IllegalStateException("Unable to create a terminal");
754750
List<TerminalProvider> providers = getProviders(provider, exception);
751+
752+
// Query providers for console codepage (Windows auto-detection)
753+
int consoleCodepage = -1;
754+
for (TerminalProvider prov : providers) {
755+
consoleCodepage = prov.getConsoleCodepage();
756+
if (consoleCodepage >= 0) {
757+
break;
758+
}
759+
}
760+
761+
Charset encoding = computeEncoding(consoleCodepage);
762+
Charset stdinEncoding = computeStdinEncoding();
763+
Charset stdoutEncoding = computeStdoutEncoding();
764+
Charset stderrEncoding = computeStderrEncoding();
755765
Terminal terminal = null;
756766
if ((system != null && system) || (system == null && in == null && out == null)) {
757767
if (system != null
@@ -989,6 +999,10 @@ public String computeType() {
989999
}
9901000

9911001
public Charset computeEncoding() {
1002+
return computeEncoding(-1);
1003+
}
1004+
1005+
Charset computeEncoding(int consoleCodepage) {
9921006
Charset encoding = this.encoding;
9931007
if (encoding == null) {
9941008
String charsetName = System.getProperty(PROP_ENCODING);
@@ -1007,7 +1021,7 @@ public Charset computeEncoding() {
10071021
// Auto-detect Windows console codepage if not explicitly set
10081022
// Only auto-detect when codepage == 0 (unset), not -1 (explicitly set to force UTF-8)
10091023
if (codepage == 0 && OSUtils.IS_WINDOWS && !OSUtils.IS_CYGWIN && !OSUtils.IS_MSYSTEM) {
1010-
codepage = getConsoleCodepage();
1024+
codepage = consoleCodepage;
10111025
}
10121026
if (codepage > 0) {
10131027
encoding = getCodepageCharset(codepage);
@@ -1171,39 +1185,6 @@ private static <S> S load(Class<S> clazz) {
11711185

11721186
private static final int UTF8_CODE_PAGE = 65001;
11731187

1174-
/**
1175-
* Auto-detect the Windows console output codepage using GetConsoleOutputCP().
1176-
* Uses reflection to avoid hard dependency on native provider modules.
1177-
*
1178-
* @return the detected codepage (positive integer), 0 if detection fails or API returns 0,
1179-
* or -1 to indicate no native provider is available
1180-
*/
1181-
private static int getConsoleCodepage() {
1182-
try {
1183-
// Try JNI provider's Kernel32 first
1184-
Class<?> kernel32Class = Class.forName("org.jline.nativ.Kernel32");
1185-
java.lang.reflect.Method method = kernel32Class.getMethod("GetConsoleOutputCP");
1186-
int codepage = (Integer) method.invoke(null);
1187-
// GetConsoleOutputCP returns 0 on failure - treat as detection failure
1188-
return codepage > 0 ? codepage : 0;
1189-
} catch (ClassNotFoundException | NoSuchMethodException e) {
1190-
// JNI provider not available, try FFM provider
1191-
try {
1192-
Class<?> kernel32Class = Class.forName("org.jline.terminal.impl.ffm.Kernel32");
1193-
java.lang.reflect.Method method = kernel32Class.getMethod("GetConsoleOutputCP");
1194-
int codepage = (Integer) method.invoke(null);
1195-
// GetConsoleOutputCP returns 0 on failure - treat as detection failure
1196-
return codepage > 0 ? codepage : 0;
1197-
} catch (Throwable ex) {
1198-
// FFM provider not available or reflection failed, return -1
1199-
return -1;
1200-
}
1201-
} catch (Throwable e) {
1202-
// Reflection failed (including ExceptionInInitializerError, LinkageError), return -1
1203-
return -1;
1204-
}
1205-
}
1206-
12071188
private static Charset getCodepageCharset(int codepage) {
12081189
// http://docs.oracle.com/javase/6/docs/technotes/guides/intl/encoding.doc.html
12091190
if (codepage == UTF8_CODE_PAGE) {

terminal/src/main/java/org/jline/terminal/spi/TerminalProvider.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,21 @@ Terminal newTerminal(
182182
*/
183183
int systemStreamWidth(SystemStream stream);
184184

185+
/**
186+
* Returns the Windows console output codepage.
187+
*
188+
* <p>
189+
* On Windows, this method returns the console output codepage (equivalent to
190+
* {@code GetConsoleOutputCP()}). On non-Windows platforms, or if the codepage
191+
* cannot be determined, this method returns {@code -1}.
192+
* </p>
193+
*
194+
* @return the console output codepage, or {@code -1} if not available
195+
*/
196+
default int getConsoleCodepage() {
197+
return -1;
198+
}
199+
185200
/**
186201
* Loads a terminal provider with the specified name.
187202
*

0 commit comments

Comments
 (0)