diff --git a/j3270/src/main/java/haus/nightmare/j3270/J3270App.java b/j3270/src/main/java/haus/nightmare/j3270/J3270App.java index 791ca24..4bd3f14 100644 --- a/j3270/src/main/java/haus/nightmare/j3270/J3270App.java +++ b/j3270/src/main/java/haus/nightmare/j3270/J3270App.java @@ -242,6 +242,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate dialog.setInitialPort(initialPort); dialog.setInitialTls(haus.nightmare.j3270.config.Settings.getAutoConnectTls()); dialog.setInitialVerifyCert(haus.nightmare.j3270.config.Settings.getAutoConnectVerifyCert()); + dialog.setInitialCodePage(haus.nightmare.j3270.config.Settings.getCodePage()); dialog.setVisible(true); if (dialog.isConfirmed()) { @@ -270,6 +271,7 @@ public class J3270App extends JFrame implements ConnectionListener, ScreenUpdate haus.nightmare.j3270.config.Settings.setAutoConnectPort(config.getPort()); haus.nightmare.j3270.config.Settings.setAutoConnectTls(config.isUseTls()); haus.nightmare.j3270.config.Settings.setAutoConnectVerifyCert(config.isTlsVerifyCert()); + haus.nightmare.j3270.config.Settings.setCodePage(config.getCodePage()); // Disconnect existing connection disconnect(); diff --git a/j3270/src/main/java/haus/nightmare/j3270/config/Settings.java b/j3270/src/main/java/haus/nightmare/j3270/config/Settings.java index 4df5945..036ba70 100644 --- a/j3270/src/main/java/haus/nightmare/j3270/config/Settings.java +++ b/j3270/src/main/java/haus/nightmare/j3270/config/Settings.java @@ -95,6 +95,14 @@ public class Settings { prefs.put("graphicsMode", (mode != null ? mode : haus.nightmare.lib3270j.graphics.GraphicsMode.BOTH).name()); } + public static String getCodePage() { + return prefs.get("codePage", "037"); + } + + public static void setCodePage(String codePage) { + prefs.put("codePage", (codePage != null && !codePage.trim().isEmpty()) ? codePage.trim() : "037"); + } + public static Color getColorOverride(int index, Color defaultColor) { String hex = prefs.get("color_" + index, null); try { @@ -256,6 +264,11 @@ public class Settings { case "enableTn3270e": setAutoConnectTn3270e(Boolean.parseBoolean(value)); break; + case "codePage": + case "codepage": + case "charset": + setCodePage(value); + break; case "blockSelectMode": setBlockSelectMode(Boolean.parseBoolean(value)); break; default: log.warning("Unknown behavior/connection key: " + key); @@ -324,6 +337,7 @@ public class Settings { w.println("autoConnectVerifyCert = " + getAutoConnectVerifyCert()); w.println("autoConnectTn3270e = " + getAutoConnectTn3270e()); } + w.println("codePage = " + getCodePage()); w.println("blockSelectMode = " + getBlockSelectMode()); w.println(); diff --git a/j3270/src/main/java/haus/nightmare/j3270/ui/ConnectDialog.java b/j3270/src/main/java/haus/nightmare/j3270/ui/ConnectDialog.java index 92c25ff..e9dd8ad 100644 --- a/j3270/src/main/java/haus/nightmare/j3270/ui/ConnectDialog.java +++ b/j3270/src/main/java/haus/nightmare/j3270/ui/ConnectDialog.java @@ -15,6 +15,7 @@ public class ConnectDialog extends JDialog { private JTextField portField; private JComboBox modelCombo; private JComboBox graphicsCombo; + private JComboBox codePageCombo; private JTextField luField; private JCheckBox tlsCheckBox; private JCheckBox verifyCertCheckBox; @@ -114,9 +115,57 @@ public class ConnectDialog extends JDialog { graphicsCombo.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13)); mainPanel.add(graphicsCombo, gbc); + // Code Page + gbc.gridx = 0; + gbc.gridy = 5; + gbc.weightx = 0; + JLabel cpLabel = new JLabel("Code Page:"); + cpLabel.setForeground(fg); + cpLabel.setFont(labelFont); + mainPanel.add(cpLabel, gbc); + gbc.gridx = 1; + gbc.weightx = 1.0; + String[] commonCodePages = { + "037 - US / Canada / Brazil", + "1047 - IBM Open Systems / z/OS Unix", + "500 - International Latin-1", + "273 - Germany / Austria", + "277 - Denmark / Norway", + "278 - Sweden / Finland", + "280 - Italy", + "284 - Spain / Latin America", + "285 - United Kingdom", + "297 - France", + "870 - Eastern Europe / Latin-2", + "871 - Iceland", + "875 - Greece (Greek)", + "1026 - Turkey (Turkish)", + "1140 - US / Canada (Euro \u20AC)", + "1141 - Germany / Austria (Euro \u20AC)", + "1148 - International (Euro \u20AC)", + "930 - Japanese Katakana Mixed DBCS", + "939 - Japanese Latin Mixed DBCS", + "935 - Simplified Chinese Mixed DBCS", + "937 - Traditional Chinese Mixed DBCS", + "933 - Korean Mixed DBCS" + }; + codePageCombo = new JComboBox<>(commonCodePages); + codePageCombo.setEditable(true); + String currentCp = haus.nightmare.j3270.config.Settings.getCodePage(); + for (String item : commonCodePages) { + if (item.startsWith(currentCp + " ") || item.equals(currentCp)) { + codePageCombo.setSelectedItem(item); + break; + } + } + codePageCombo.setBackground(new Color(45, 45, 45)); + codePageCombo.setForeground(fg); + codePageCombo.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 13)); + mainPanel.add(codePageCombo, gbc); + // TLS / SSL Checkbox gbc.gridx = 1; - gbc.gridy = 5; + gbc.gridy = 6; gbc.weightx = 1.0; tlsCheckBox = new JCheckBox("Enable TLS/SSL"); tlsCheckBox.setBackground(new Color(30, 30, 30)); @@ -137,7 +186,7 @@ public class ConnectDialog extends JDialog { // Verify Certificate Checkbox gbc.gridx = 1; - gbc.gridy = 6; + gbc.gridy = 7; verifyCertCheckBox = new JCheckBox("Verify Server Certificate"); verifyCertCheckBox.setBackground(new Color(30, 30, 30)); verifyCertCheckBox.setForeground(new Color(160, 160, 160)); @@ -149,7 +198,7 @@ public class ConnectDialog extends JDialog { // TN3270E Checkbox gbc.gridx = 1; - gbc.gridy = 7; + gbc.gridy = 8; tn3270eCheckBox = new JCheckBox("Enable TN3270E (Extended 3270)"); tn3270eCheckBox.setBackground(new Color(30, 30, 30)); tn3270eCheckBox.setForeground(fg); @@ -224,6 +273,18 @@ public class ConnectDialog extends JDialog { result.setLuName(lu); } result.setGraphicsMode((haus.nightmare.lib3270j.graphics.GraphicsMode) graphicsCombo.getSelectedItem()); + + Object cpSelection = codePageCombo.getSelectedItem(); + if (cpSelection != null) { + String cpStr = cpSelection.toString().trim(); + int dash = cpStr.indexOf(" -"); + if (dash > 0) { + cpStr = cpStr.substring(0, dash).trim(); + } + result.setCodePage(cpStr); + haus.nightmare.j3270.config.Settings.setCodePage(cpStr); + } + result.setUseTls(tlsCheckBox.isSelected()); result.setTlsVerifyCert(verifyCertCheckBox.isSelected()); result.setTn3270eEnabled(tn3270eCheckBox.isSelected()); @@ -260,4 +321,17 @@ public class ConnectDialog extends JDialog { public void setInitialTn3270e(boolean tn3270e) { tn3270eCheckBox.setSelected(tn3270e); } + + public void setInitialCodePage(String cp) { + if (cp != null && codePageCombo != null) { + for (int i = 0; i < codePageCombo.getItemCount(); i++) { + String item = codePageCombo.getItemAt(i); + if (item.startsWith(cp + " ") || item.equals(cp)) { + codePageCombo.setSelectedIndex(i); + return; + } + } + codePageCombo.setSelectedItem(cp); + } + } } diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/ConnectionConfig.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/ConnectionConfig.java index b93dd7f..8b83865 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/ConnectionConfig.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/ConnectionConfig.java @@ -26,6 +26,7 @@ public class ConnectionConfig { private int dynamicRows = 24; private int dynamicCols = 80; private haus.nightmare.lib3270j.graphics.GraphicsMode graphicsMode = haus.nightmare.lib3270j.graphics.GraphicsMode.BOTH; + private String codePage = "037"; public ConnectionConfig() {} @@ -94,6 +95,11 @@ public class ConnectionConfig { this.graphicsMode = (mode != null) ? mode : haus.nightmare.lib3270j.graphics.GraphicsMode.NONE; } + public String getCodePage() { return codePage; } + public void setCodePage(String codePage) { + this.codePage = (codePage != null && !codePage.trim().isEmpty()) ? codePage.trim() : "037"; + } + public String getTerminalName() { return terminalName; } public void setTerminalName(String name) { this.terminalName = name; } diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java index 5b0b174..22fb045 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/Telnet3270Client.java @@ -46,7 +46,7 @@ public class Telnet3270Client { public Telnet3270Client(ConnectionConfig config) { this.config = config; - this.translator = new EbcdicTranslator(); + this.translator = new EbcdicTranslator(config.getCodePage()); this.screenBuffer = new ScreenBuffer(config.getModel(), translator); this.dsProcessor = new DataStreamProcessor(screenBuffer, translator); this.dsProcessor.getQueryReplyBuilder().setGraphicsMode(config.getGraphicsMode()); @@ -126,6 +126,12 @@ public class Telnet3270Client { /** Get the EBCDIC translator. */ public EbcdicTranslator getTranslator() { return translator; } + /** Get the active Code Page identifier. */ + public String getCodePage() { return translator.getCodePageId(); } + + /** Set the active Code Page identifier. */ + public void setCodePage(String codePageId) { translator.setCodePage(codePageId); } + /** Get the current connection state. */ public ConnectionState getConnectionState() { return fsm.getConnectionState(); } diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/AbstractCodePage.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/AbstractCodePage.java new file mode 100644 index 0000000..ebc4ba4 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/AbstractCodePage.java @@ -0,0 +1,158 @@ +package haus.nightmare.lib3270j.charset; + +import java.nio.charset.Charset; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Base implementation for Single-Byte Character Set (SBCS) EBCDIC Code Pages. + */ +public abstract class AbstractCodePage implements CodePage { + + protected final String id; + protected final String description; + protected final int cpgid; + protected final int cgcsgid; + + protected final int[] toUnicode = new int[256]; + protected final int[] toEbcdic = new int[256]; + protected final Map extendedToEbcdic = new HashMap<>(); + + public AbstractCodePage(String id, String description, int cpgid, int cgcsgid, int[] unicodeMapping) { + this(id, description, cpgid, cgcsgid, null, unicodeMapping); + } + + public AbstractCodePage(String id, String description, int cpgid, int cgcsgid, String javaCharsetName, int[] unicodeMapping) { + this.id = id; + this.description = description; + this.cpgid = cpgid; + this.cgcsgid = cgcsgid; + + int[] map = loadMapping(javaCharsetName, unicodeMapping); + System.arraycopy(map, 0, this.toUnicode, 0, 256); + initReverseMapping(); + } + + public static int[] loadMapping(String charsetName, int[] fallback) { + if (charsetName != null) { + String[] candidates = { + charsetName, + "IBM" + charsetName, + "Cp" + charsetName, + "IBM-" + charsetName, + "x-IBM" + charsetName + }; + for (String name : candidates) { + try { + if (Charset.isSupported(name)) { + Charset cs = Charset.forName(name); + int[] map = new int[256]; + for (int i = 0; i < 256; i++) { + byte[] b = new byte[]{(byte) i}; + String s = new String(b, cs); + map[i] = (!s.isEmpty() && s.charAt(0) != '\uFFFD') ? s.charAt(0) : (fallback != null ? fallback[i] : 0); + } + return map; + } + } catch (Exception ignored) {} + } + } + return fallback != null ? fallback : new int[256]; + } + + protected void initReverseMapping() { + Arrays.fill(toEbcdic, -1); + for (int i = 0; i < 256; i++) { + int uc = toUnicode[i]; + if (uc >= 0 && uc < 256) { + if (toEbcdic[uc] == -1) { + toEbcdic[uc] = i; + } + } else if (uc >= 256) { + extendedToEbcdic.putIfAbsent((char) uc, i); + } + } + } + + @Override + public String getCodePageId() { + return id; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public int getCpgid() { + return cpgid; + } + + @Override + public int getCgcsgid() { + return cgcsgid; + } + + @Override + public boolean isDBCS() { + return false; + } + + @Override + public char ebcdicToUnicode(int ebc) { + return (char) toUnicode[ebc & 0xFF]; + } + + @Override + public int unicodeToEbcdic(char unicode) { + int u = unicode; + if (u >= 0 && u < 256) { + return toEbcdic[u]; + } + Integer val = extendedToEbcdic.get(unicode); + return val != null ? val : -1; + } + + @Override + public byte unicodeToEbcdicSafe(char unicode) { + int ebc = unicodeToEbcdic(unicode); + return (byte) (ebc >= 0 ? ebc : 0x40); + } + + @Override + public char dbcsToUnicode(int b1, int b2) { + return '\uFFFD'; + } + + @Override + public int unicodeToDbcs(char unicode) { + return -1; + } + + @Override + public String ebcdicToString(byte[] ebcdic, int offset, int length) { + if (ebcdic == null || length <= 0) return ""; + char[] chars = new char[length]; + for (int i = 0; i < length; i++) { + chars[i] = ebcdicToUnicode(ebcdic[offset + i]); + } + return new String(chars); + } + + @Override + public byte[] stringToEbcdic(String s) { + if (s == null || s.isEmpty()) return new byte[0]; + byte[] bytes = new byte[s.length()]; + for (int i = 0; i < s.length(); i++) { + bytes[i] = unicodeToEbcdicSafe(s.charAt(i)); + } + return bytes; + } + + @Override + public String toString() { + return id + " (" + description + ")"; + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/AbstractDBCSCodePage.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/AbstractDBCSCodePage.java new file mode 100644 index 0000000..a1e61bf --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/AbstractDBCSCodePage.java @@ -0,0 +1,184 @@ +package haus.nightmare.lib3270j.charset; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CodingErrorAction; +import java.util.HashMap; +import java.util.Map; + +/** + * Base implementation for Mixed Double-Byte Character Set (DBCS) EBCDIC Code Pages + * (e.g. Japanese Katakana 930, Japanese Latin 939, Chinese 935/937/1388, Korean 933). + */ +public abstract class AbstractDBCSCodePage extends AbstractCodePage { + + public static final int SO = 0x0E; // Shift Out (enter DBCS mode) + public static final int SI = 0x0F; // Shift In (enter SBCS mode) + + protected final Map dbcsToUnicodeMap = new HashMap<>(); + protected final Map unicodeToDbcsMap = new HashMap<>(); + protected Charset nioCharset; + + public AbstractDBCSCodePage(String id, String description, int cpgid, int cgcsgid, + int[] sbcsMapping, String nioCharsetName) { + super(id, description, cpgid, cgcsgid, sbcsMapping); + if (nioCharsetName != null) { + try { + if (Charset.isSupported(nioCharsetName)) { + this.nioCharset = Charset.forName(nioCharsetName); + } + } catch (Exception ignored) {} + } + // Initialize ideographic space (0x4040 <-> \u3000) + registerDbcsPair(0x4040, '\u3000'); + } + + public void registerDbcsPair(int ebcdicDBCS, char unicodeChar) { + dbcsToUnicodeMap.put(ebcdicDBCS, unicodeChar); + unicodeToDbcsMap.put(unicodeChar, ebcdicDBCS); + } + + @Override + public boolean isDBCS() { + return true; + } + + @Override + public char dbcsToUnicode(int b1, int b2) { + int key = ((b1 & 0xFF) << 8) | (b2 & 0xFF); + Character c = dbcsToUnicodeMap.get(key); + if (c != null) { + return c; + } + + if (nioCharset != null) { + try { + byte[] raw = new byte[] { (byte) SO, (byte) b1, (byte) b2, (byte) SI }; + CharBuffer cb = nioCharset.decode(ByteBuffer.wrap(raw)); + if (cb.hasRemaining()) { + char decoded = cb.get(); + if (decoded != '\uFFFD' && decoded != 0) { + registerDbcsPair(key, decoded); + return decoded; + } + } + } catch (Exception ignored) {} + } + + return '?'; + } + + @Override + public int unicodeToDbcs(char unicode) { + Integer val = unicodeToDbcsMap.get(unicode); + if (val != null) { + return val; + } + + if (nioCharset != null) { + try { + CharsetEncoder encoder = nioCharset.newEncoder() + .onMalformedInput(CodingErrorAction.REPLACE) + .onUnmappableCharacter(CodingErrorAction.REPLACE); + ByteBuffer bb = encoder.encode(CharBuffer.wrap(new char[]{unicode})); + byte[] bytes = new byte[bb.remaining()]; + bb.get(bytes); + + // Check if encoded as SO + b1 + b2 + SI or b1 + b2 + if (bytes.length >= 4 && (bytes[0] & 0xFF) == SO) { + int key = ((bytes[1] & 0xFF) << 8) | (bytes[2] & 0xFF); + registerDbcsPair(key, unicode); + return key; + } else if (bytes.length == 2) { + int key = ((bytes[0] & 0xFF) << 8) | (bytes[1] & 0xFF); + registerDbcsPair(key, unicode); + return key; + } + } catch (Exception ignored) {} + } + + return -1; + } + + @Override + public String ebcdicToString(byte[] ebcdic, int offset, int length) { + if (ebcdic == null || length <= 0) return ""; + + StringBuilder sb = new StringBuilder(length); + boolean inDBCS = false; + int end = Math.min(ebcdic.length, offset + length); + int i = offset; + + while (i < end) { + int b = ebcdic[i] & 0xFF; + if (b == SO) { + inDBCS = true; + i++; + } else if (b == SI) { + inDBCS = false; + i++; + } else if (inDBCS) { + if (i + 1 < end) { + int b2 = ebcdic[i + 1] & 0xFF; + if (b2 == SI) { + // Orphaned single byte before SI + inDBCS = false; + i += 2; + } else { + sb.append(dbcsToUnicode(b, b2)); + i += 2; + } + } else { + // Trailing byte + i++; + } + } else { + sb.append(ebcdicToUnicode(b)); + i++; + } + } + + return sb.toString(); + } + + @Override + public byte[] stringToEbcdic(String s) { + if (s == null || s.isEmpty()) return new byte[0]; + + // Manual state machine encoding + ByteArrayOutputStream out = new ByteArrayOutputStream(s.length() * 2); + boolean inDBCS = false; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + int dbcsCode = unicodeToDbcs(c); + + if (dbcsCode >= 0) { + // Character is DBCS + if (!inDBCS) { + out.write(SO); + inDBCS = true; + } + out.write((dbcsCode >> 8) & 0xFF); + out.write(dbcsCode & 0xFF); + } else { + // Character is SBCS + if (inDBCS) { + out.write(SI); + inDBCS = false; + } + out.write(unicodeToEbcdicSafe(c)); + } + } + + if (inDBCS) { + out.write(SI); + } + + return out.toByteArray(); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CodePage.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CodePage.java new file mode 100644 index 0000000..73ad1ee --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CodePage.java @@ -0,0 +1,72 @@ +package haus.nightmare.lib3270j.charset; + +/** + * Interface defining character translation and metadata for EBCDIC code pages + * conforming to IBM Host On-Demand (HoD v14) converters specification. + */ +public interface CodePage { + + /** + * Get the primary identifier for this code page (e.g. "037", "1047", "500", "273"). + */ + String getCodePageId(); + + /** + * Get a human-readable description (e.g. "US / Canada - EBCDIC"). + */ + String getDescription(); + + /** + * Get the Code Page Global ID (CPGID / CCSID) for 3270 query replies. + */ + int getCpgid(); + + /** + * Get the Graphic Character Set Global ID (GCSGID) for 3270 query replies. + */ + int getCgcsgid(); + + /** + * Translate an EBCDIC byte (0x00-0xFF) to a Unicode character. + */ + char ebcdicToUnicode(int ebc); + + /** + * Translate a Unicode character to an EBCDIC byte value. + * Returns -1 if unmappable. + */ + int unicodeToEbcdic(char unicode); + + /** + * Translate a Unicode character to an EBCDIC byte value, returning a safe + * fallback (default EBCDIC space 0x40) if unmappable. + */ + byte unicodeToEbcdicSafe(char unicode); + + /** + * Translate an EBCDIC byte array slice to a Unicode String. + */ + String ebcdicToString(byte[] ebcdic, int offset, int length); + + /** + * Translate a Unicode String to an EBCDIC byte array. + */ + byte[] stringToEbcdic(String s); + + /** + * Check if this code page is a double-byte (DBCS) / mixed SBCS+DBCS code page. + */ + boolean isDBCS(); + + /** + * Translate a double-byte EBCDIC pair (b1, b2) to a Unicode character. + * Returns '\uFFFD' or '?' if unmappable. + */ + char dbcsToUnicode(int b1, int b2); + + /** + * Translate a Unicode character to a double-byte EBCDIC code ((b1 << 8) | b2). + * Returns -1 if unmappable. + */ + int unicodeToDbcs(char unicode); +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CodePageRegistry.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CodePageRegistry.java new file mode 100644 index 0000000..d7155b5 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CodePageRegistry.java @@ -0,0 +1,270 @@ +package haus.nightmare.lib3270j.charset; + +import java.nio.charset.Charset; +import java.util.*; +import java.util.logging.Logger; + +/** + * Registry and factory for 3270 EBCDIC Code Pages. + * Manages built-in SBCS/DBCS codepages, alias normalization, and dynamic JVM Charset resolution. + */ +public class CodePageRegistry { + + private static final Logger log = Logger.getLogger(CodePageRegistry.class.getName()); + + private static final Map CODE_PAGES = new LinkedHashMap<>(); + private static final Map ALIASES = new HashMap<>(); + + static { + // Register core SBCS codepages + register(new Cp037()); + register(new Cp1047()); + register(new Cp500()); + register(new Cp273()); + register(new Cp277()); + register(new Cp278()); + register(new Cp280()); + register(new Cp284()); + register(new Cp285()); + register(new Cp297()); + register(new Cp870()); + register(new Cp871()); + register(new Cp875()); + register(new Cp1026()); + + // Register Euro variants (1140-1149) + register(new CpEuroVariants.Cp1140()); + register(new CpEuroVariants.Cp1141()); + register(new CpEuroVariants.Cp1142()); + register(new CpEuroVariants.Cp1143()); + register(new CpEuroVariants.Cp1144()); + register(new CpEuroVariants.Cp1145()); + register(new CpEuroVariants.Cp1146()); + register(new CpEuroVariants.Cp1147()); + register(new CpEuroVariants.Cp1148()); + register(new CpEuroVariants.Cp1149()); + + // Register DBCS mixed codepages + register(new Cp930()); + register(new Cp939()); + register(new Cp935()); + register(new Cp935.Cp1388()); + register(new Cp937()); + register(new Cp937.Cp1371()); + register(new Cp933()); + + // Setup common aliases + addAlias("us", "037"); + addAlias("usa", "037"); + addAlias("ebcdic-cp-us", "037"); + addAlias("ebcdic-cp-ca", "037"); + addAlias("ebcdic-cp-nl", "037"); + + addAlias("posix", "1047"); + addAlias("unix", "1047"); + addAlias("open-systems", "1047"); + addAlias("zos-unix", "1047"); + + addAlias("intl", "500"); + addAlias("international", "500"); + addAlias("ebcdic-cp-ch", "500"); + + addAlias("de", "273"); + addAlias("germany", "273"); + addAlias("austria", "273"); + addAlias("ebcdic-cp-de", "273"); + + addAlias("dk", "277"); + addAlias("no", "277"); + addAlias("denmark", "277"); + addAlias("norway", "277"); + addAlias("ebcdic-cp-dk", "277"); + addAlias("ebcdic-cp-no", "277"); + + addAlias("se", "278"); + addAlias("fi", "278"); + addAlias("sweden", "278"); + addAlias("finland", "278"); + addAlias("ebcdic-cp-se", "278"); + addAlias("ebcdic-cp-fi", "278"); + + addAlias("it", "280"); + addAlias("italy", "280"); + addAlias("ebcdic-cp-it", "280"); + + addAlias("es", "284"); + addAlias("spain", "284"); + addAlias("latin-america", "284"); + addAlias("ebcdic-cp-es", "284"); + + addAlias("uk", "285"); + addAlias("gb", "285"); + addAlias("great-britain", "285"); + addAlias("ebcdic-cp-gb", "285"); + + addAlias("fr", "297"); + addAlias("france", "297"); + addAlias("ebcdic-cp-fr", "297"); + + addAlias("latin2", "870"); + addAlias("pl", "870"); + addAlias("cz", "870"); + addAlias("hu", "870"); + addAlias("ebcdic-cp-roece", "870"); + + addAlias("is", "871"); + addAlias("iceland", "871"); + addAlias("ebcdic-cp-is", "871"); + + addAlias("gr", "875"); + addAlias("greece", "875"); + addAlias("greek", "875"); + addAlias("ebcdic-cp-gr", "875"); + + addAlias("tr", "1026"); + addAlias("turkey", "1026"); + addAlias("turkish", "1026"); + addAlias("ebcdic-cp-tr", "1026"); + + addAlias("ja", "930"); + addAlias("japanese", "930"); + addAlias("katakana", "930"); + addAlias("ja-latin", "939"); + + addAlias("zh", "935"); + addAlias("chinese-simplified", "935"); + addAlias("zh-tw", "937"); + addAlias("chinese-traditional", "937"); + + addAlias("ko", "933"); + addAlias("korean", "933"); + } + + public static void register(CodePage cp) { + if (cp != null) { + CODE_PAGES.put(cp.getCodePageId(), cp); + } + } + + public static void addAlias(String alias, String targetId) { + if (alias != null && targetId != null) { + ALIASES.put(normalizeKey(alias), targetId); + } + } + + public static String normalizeKey(String name) { + if (name == null) return ""; + String s = name.trim().toLowerCase(); + s = s.replace("_", "").replace("-", ""); + if (s.startsWith("ebcdiccp")) { + s = s.substring(8); + } else if (s.startsWith("ebcdic")) { + s = s.substring(6); + } else if (s.startsWith("ibm")) { + s = s.substring(3); + } else if (s.startsWith("cp")) { + s = s.substring(2); + } else if (s.startsWith("ccsid")) { + s = s.substring(5); + } + return s; + } + + /** + * Look up a code page by ID or alias. + * If not found in built-ins, attempts to load via java.nio.charset.Charset. + * Falls back to CP037 if completely unresolvable. + */ + public static CodePage getCodePage(String name) { + if (name == null || name.trim().isEmpty()) { + return CODE_PAGES.get("037"); + } + + String raw = name.trim(); + // Direct match + CodePage cp = CODE_PAGES.get(raw); + if (cp != null) return cp; + + // Normalized key lookup + String norm = normalizeKey(raw); + cp = CODE_PAGES.get(norm); + if (cp != null) return cp; + + // Alias lookup + String targetId = ALIASES.get(norm); + if (targetId != null) { + cp = CODE_PAGES.get(targetId); + if (cp != null) return cp; + } + + // Try standard NIO Charset dynamic adapter + try { + if (Charset.isSupported(raw)) { + return new NioCodePageAdapter(raw); + } + String ibmName = "IBM" + norm; + if (Charset.isSupported(ibmName)) { + return new NioCodePageAdapter(ibmName); + } + String cpName = "Cp" + norm; + if (Charset.isSupported(cpName)) { + return new NioCodePageAdapter(cpName); + } + } catch (Exception e) { + log.fine("Dynamic charset loading failed for " + name + ": " + e.getMessage()); + } + + log.warning("CodePage not recognized: '" + name + "'; falling back to CP037"); + return CODE_PAGES.get("037"); + } + + /** + * Get an unmodifiable list of all registered built-in CodePages. + */ + public static List getAvailableCodePages() { + return Collections.unmodifiableList(new ArrayList<>(CODE_PAGES.values())); + } + + /** + * Get an array of all registered built-in CodePage IDs. + */ + public static String[] getAvailableCodePageIds() { + return CODE_PAGES.keySet().toArray(new String[0]); + } + + /** + * Dynamic fallback adapter wrapping any JVM java.nio.charset.Charset. + */ + public static class NioCodePageAdapter extends AbstractCodePage { + private final Charset charset; + + public NioCodePageAdapter(String charsetName) { + this(Charset.forName(charsetName)); + } + + public NioCodePageAdapter(Charset charset) { + super(charset.name(), "JVM Charset: " + charset.displayName(), parseCpgid(charset.name()), 697, buildMappingFromCharset(charset)); + this.charset = charset; + } + + private static int parseCpgid(String name) { + try { + String num = name.replaceAll("\\D+", ""); + if (!num.isEmpty()) { + return Integer.parseInt(num); + } + } catch (Exception ignored) {} + return 37; + } + + private static int[] buildMappingFromCharset(Charset cs) { + int[] map = new int[256]; + for (int i = 0; i < 256; i++) { + byte[] b = new byte[]{(byte) i}; + String s = new String(b, cs); + map[i] = !s.isEmpty() ? s.charAt(0) : (char) i; + } + return map; + } + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp037.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp037.java new file mode 100644 index 0000000..632365a --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp037.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 037 (US / Canada / Netherlands / Portugal / Brazil). + * CCSID / CPGID: 37, GCSGID: 697. + */ +public class Cp037 extends AbstractCodePage { + + public static final String ID = "037"; + public static final String DESCRIPTION = "US / Canada / Brazil - EBCDIC"; + public static final int CPGID = 37; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F (space, accent chars, punctuation) + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + // 80-8F (lowercase a-i) + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F (lowercase j-r) + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF (lowercase s-z) + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF (uppercase A-I) + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF (uppercase J-R) + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF (uppercase S-Z) + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF (digits 0-9) + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp037() { + super(ID, DESCRIPTION, CPGID, GCSGID, "037", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp1026.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp1026.java new file mode 100644 index 0000000..3d1ff0c --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp1026.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 1026 (Turkey - Turkish Latin-5 EBCDIC). + * CCSID / CPGID: 1026, GCSGID: 1152. + */ +public class Cp1026 extends AbstractCodePage { + + public static final String ID = "1026"; + public static final String DESCRIPTION = "Turkey - Turkish Latin-5 EBCDIC"; + public static final int CPGID = 1026; + public static final int GCSGID = 1152; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x015E, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00D6, 0x00C7, 0x00DC, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x0130, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x00F6, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x011E, 0x00F4, 0x0131, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x00E7, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x011F, 0x00FB, 0x015F, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x00FC, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00A6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp1026() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1026", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp1047.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp1047.java new file mode 100644 index 0000000..5edd2ba --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp1047.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 1047 (IBM Open Systems / z/OS Unix System Services Latin-1). + * CCSID / CPGID: 1047, GCSGID: 103. + */ +public class Cp1047 extends AbstractCodePage { + + public static final String ID = "1047"; + public static final String DESCRIPTION = "IBM Open Systems / z/OS Unix Latin-1"; + public static final int CPGID = 1047; + public static final int GCSGID = 103; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x000A, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x005B, 0x00DE, 0x00AE, + // B0-BF + 0x00AC, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00DD, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp1047() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1047", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp273.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp273.java new file mode 100644 index 0000000..d0341bc --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp273.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 273 (Germany / Austria). + * CCSID / CPGID: 273, GCSGID: 697. + */ +public class Cp273 extends AbstractCodePage { + + public static final String ID = "273"; + public static final String DESCRIPTION = "Germany / Austria - EBCDIC"; + public static final int CPGID = 273; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00E4, 0x00F6, 0x00FC, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x00E4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x00FC, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00DC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp273() { + super(ID, DESCRIPTION, CPGID, GCSGID, "273", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp277.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp277.java new file mode 100644 index 0000000..ea8fc25 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp277.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 277 (Denmark / Norway). + * CCSID / CPGID: 277, GCSGID: 697. + */ +public class Cp277 extends AbstractCodePage { + + public static final String ID = "277"; + public static final String DESCRIPTION = "Denmark / Norway - EBCDIC"; + public static final int CPGID = 277; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x00A4, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00E6, 0x00F8, 0x00E5, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x00C6, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x00D8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x00C5, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp277() { + super(ID, DESCRIPTION, CPGID, GCSGID, "277", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp278.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp278.java new file mode 100644 index 0000000..8b761cc --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp278.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 278 (Sweden / Finland). + * CCSID / CPGID: 278, GCSGID: 697. + */ +public class Cp278 extends AbstractCodePage { + + public static final String ID = "278"; + public static final String DESCRIPTION = "Sweden / Finland - EBCDIC"; + public static final int CPGID = 278; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x00A4, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00E4, 0x00F6, 0x00E5, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x00C4, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x00D6, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x00C5, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp278() { + super(ID, DESCRIPTION, CPGID, GCSGID, "278", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp280.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp280.java new file mode 100644 index 0000000..5d14437 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp280.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 280 (Italy). + * CCSID / CPGID: 280, GCSGID: 697. + */ +public class Cp280 extends AbstractCodePage { + + public static final String ID = "280"; + public static final String DESCRIPTION = "Italy - EBCDIC"; + public static final int CPGID = 280; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00A3, 0x00A7, 0x00E9, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x00F2, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x00F9, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x00E0, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp280() { + super(ID, DESCRIPTION, CPGID, GCSGID, "280", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp284.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp284.java new file mode 100644 index 0000000..083fa0f --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp284.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 284 (Spain / Latin America). + * CCSID / CPGID: 284, GCSGID: 697. + */ +public class Cp284 extends AbstractCodePage { + + public static final String ID = "284"; + public static final String DESCRIPTION = "Spain / Latin America - EBCDIC"; + public static final int CPGID = 284; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00F1, 0x00D1, 0x00E7, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp284() { + super(ID, DESCRIPTION, CPGID, GCSGID, "284", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp285.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp285.java new file mode 100644 index 0000000..f5d9ff1 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp285.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 285 (United Kingdom). + * CCSID / CPGID: 285, GCSGID: 697. + */ +public class Cp285 extends AbstractCodePage { + + public static final String ID = "285"; + public static final String DESCRIPTION = "United Kingdom - EBCDIC"; + public static final int CPGID = 285; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00A3, 0x0040, 0x0027, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp285() { + super(ID, DESCRIPTION, CPGID, GCSGID, "285", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp297.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp297.java new file mode 100644 index 0000000..bb9efd9 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp297.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 297 (France). + * CCSID / CPGID: 297, GCSGID: 697. + */ +public class Cp297 extends AbstractCodePage { + + public static final String ID = "297"; + public static final String DESCRIPTION = "France - EBCDIC"; + public static final int CPGID = 297; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x007B, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00B0, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x005B, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00E0, 0x00E9, 0x00E8, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x00E9, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x00E8, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp297() { + super(ID, DESCRIPTION, CPGID, GCSGID, "297", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp500.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp500.java new file mode 100644 index 0000000..1c7076d --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp500.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 500 (International / Western Europe Latin-1). + * CCSID / CPGID: 500, GCSGID: 697. + */ +public class Cp500 extends AbstractCodePage { + + public static final String ID = "500"; + public static final String DESCRIPTION = "International / Western Europe Latin-1"; + public static final int CPGID = 500; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x00A2, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp500() { + super(ID, DESCRIPTION, CPGID, GCSGID, "500", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp870.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp870.java new file mode 100644 index 0000000..2114f14 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp870.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 870 (Latin-2 / Eastern Europe: Polish, Czech, Slovak, Hungarian, etc.). + * CCSID / CPGID: 870, GCSGID: 959. + */ +public class Cp870 extends AbstractCodePage { + + public static final String ID = "870"; + public static final String DESCRIPTION = "Eastern Europe / Latin-2 - EBCDIC"; + public static final int CPGID = 870; + public static final int GCSGID = 959; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x0103, 0x00E4, 0x016F, 0x0165, 0x0105, 0x0155, + 0x0107, 0x00E9, 0x015B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x0119, 0x00EB, 0x011B, 0x00ED, 0x00EE, 0x010F, + 0x010D, 0x00DF, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x016E, 0x0164, 0x0104, 0x0154, + 0x0106, 0x00C9, 0x015A, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x0118, 0x00CB, 0x011A, 0x00CD, 0x00CE, 0x010E, + 0x010C, 0x0060, 0x003A, 0x0161, 0x017C, 0x00FD, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x0160, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x017B, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp870() { + super(ID, DESCRIPTION, CPGID, GCSGID, "870", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp871.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp871.java new file mode 100644 index 0000000..d856ba4 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp871.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 871 (Iceland). + * CCSID / CPGID: 871, GCSGID: 697. + */ +public class Cp871 extends AbstractCodePage { + + public static final String ID = "871"; + public static final String DESCRIPTION = "Iceland - EBCDIC"; + public static final int CPGID = 871; + public static final int GCSGID = 697; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, + 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, + 0x00EC, 0x00DF, 0x00A4, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, + 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, + 0x00CC, 0x0060, 0x003A, 0x00FE, 0x00F0, 0x00FD, 0x003D, 0x0022, + // 80-8F + 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, + // 90-9F + 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, + // A0-AF + 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x00DE, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, + // D0-DF + 0x00D0, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, + // E0-EF + 0x00DD, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, + }; + + public Cp871() { + super(ID, DESCRIPTION, CPGID, GCSGID, "871", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp875.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp875.java new file mode 100644 index 0000000..f015e7d --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp875.java @@ -0,0 +1,68 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 875 (Greece - Greek EBCDIC). + * CCSID / CPGID: 875, GCSGID: 925. + */ +public class Cp875 extends AbstractCodePage { + + public static final String ID = "875"; + public static final String DESCRIPTION = "Greece - Greek EBCDIC"; + public static final int CPGID = 875; + public static final int GCSGID = 925; + + public static final int[] MAPPING = { + // 00-0F + 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, + 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, + // 10-1F + 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, + 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, + // 20-2F + 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, + 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, + // 30-3F + 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, + 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, + // 40-4F + 0x0020, 0x00A0, 0x03AC, 0x03AD, 0x03AE, 0x03AF, 0x03CC, 0x03CD, + 0x03CE, 0x00DF, 0x005B, 0x002E, 0x003C, 0x0028, 0x002B, 0x0021, + // 50-5F + 0x0026, 0x0390, 0x03B0, 0x03CA, 0x03CB, 0x0390, 0x0385, 0x0386, + 0x0388, 0x0389, 0x005D, 0x0024, 0x002A, 0x0029, 0x003B, 0x005E, + // 60-6F + 0x002D, 0x002F, 0x038A, 0x038C, 0x038E, 0x038F, 0x03AA, 0x03AB, + 0x00A9, 0x00AE, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, + // 70-7F + 0x00F8, 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, + 0x03B8, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, + // 80-8F + 0x03B9, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, + 0x0068, 0x0069, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, + // 90-9F + 0x03C0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, + 0x0071, 0x0072, 0x03C1, 0x03C3, 0x03C2, 0x03C4, 0x03C5, 0x03C6, + // A0-AF + 0x03C7, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, + 0x0079, 0x007A, 0x03C8, 0x03C9, 0x00D0, 0x00DD, 0x00DE, 0x00AE, + // B0-BF + 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, + 0x00BD, 0x00BE, 0x00AC, 0x007C, 0x00AF, 0x00A8, 0x00B4, 0x00D7, + // C0-CF + 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, + 0x0048, 0x0049, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, + // D0-DF + 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, + 0x0051, 0x0052, 0x0397, 0x0398, 0x0399, 0x039A, 0x039B, 0x039C, + // E0-EF + 0x005C, 0x039D, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, + 0x0059, 0x005A, 0x039E, 0x039F, 0x03A0, 0x03A1, 0x03A3, 0x03A4, + // F0-FF + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x03A5, 0x03A6, 0x03A7, 0x03A8, 0x03A9, 0x009F, + }; + + public Cp875() { + super(ID, DESCRIPTION, CPGID, GCSGID, "875", MAPPING); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp930.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp930.java new file mode 100644 index 0000000..185687f --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp930.java @@ -0,0 +1,17 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 930 (Japanese Katakana Mixed DBCS: host SBCS CP290 / DBCS CP300). + * CCSID / CPGID: 930, GCSGID: 1172. + */ +public class Cp930 extends AbstractDBCSCodePage { + + public static final String ID = "930"; + public static final String DESCRIPTION = "Japanese Katakana Mixed DBCS"; + public static final int CPGID = 930; + public static final int GCSGID = 1172; + + public Cp930() { + super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM930"); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp933.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp933.java new file mode 100644 index 0000000..761f324 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp933.java @@ -0,0 +1,17 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 933 (Korean Mixed DBCS). + * CCSID / CPGID: 933, GCSGID: 1173. + */ +public class Cp933 extends AbstractDBCSCodePage { + + public static final String ID = "933"; + public static final String DESCRIPTION = "Korean Mixed DBCS"; + public static final int CPGID = 933; + public static final int GCSGID = 1173; + + public Cp933() { + super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM933"); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp935.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp935.java new file mode 100644 index 0000000..b7b4de3 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp935.java @@ -0,0 +1,28 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 935 / 1388 (Simplified Chinese Mixed DBCS). + * CCSID / CPGID: 935, GCSGID: 1175. + */ +public class Cp935 extends AbstractDBCSCodePage { + + public static final String ID = "935"; + public static final String DESCRIPTION = "Simplified Chinese Mixed DBCS"; + public static final int CPGID = 935; + public static final int GCSGID = 1175; + + public Cp935() { + super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM935"); + } + + public static class Cp1388 extends AbstractDBCSCodePage { + public static final String ID = "1388"; + public static final String DESCRIPTION = "Simplified Chinese Extended Mixed DBCS"; + public static final int CPGID = 1388; + public static final int GCSGID = 1175; + + public Cp1388() { + super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM1388"); + } + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp937.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp937.java new file mode 100644 index 0000000..c6df868 --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp937.java @@ -0,0 +1,28 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 937 / 1371 (Traditional Chinese Mixed DBCS). + * CCSID / CPGID: 937, GCSGID: 1174. + */ +public class Cp937 extends AbstractDBCSCodePage { + + public static final String ID = "937"; + public static final String DESCRIPTION = "Traditional Chinese Mixed DBCS"; + public static final int CPGID = 937; + public static final int GCSGID = 1174; + + public Cp937() { + super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM937"); + } + + public static class Cp1371 extends AbstractDBCSCodePage { + public static final String ID = "1371"; + public static final String DESCRIPTION = "Traditional Chinese Extended Mixed DBCS"; + public static final int CPGID = 1371; + public static final int GCSGID = 1174; + + public Cp1371() { + super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM1371"); + } + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp939.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp939.java new file mode 100644 index 0000000..c5b11db --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/Cp939.java @@ -0,0 +1,17 @@ +package haus.nightmare.lib3270j.charset; + +/** + * IBM Code Page 939 (Japanese Latin Mixed DBCS: host SBCS CP1027 / DBCS CP300). + * CCSID / CPGID: 939, GCSGID: 1172. + */ +public class Cp939 extends AbstractDBCSCodePage { + + public static final String ID = "939"; + public static final String DESCRIPTION = "Japanese Latin Mixed DBCS"; + public static final int CPGID = 939; + public static final int GCSGID = 1172; + + public Cp939() { + super(ID, DESCRIPTION, CPGID, GCSGID, Cp037.MAPPING, "x-IBM939"); + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CpEuroVariants.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CpEuroVariants.java new file mode 100644 index 0000000..ac9000e --- /dev/null +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/CpEuroVariants.java @@ -0,0 +1,125 @@ +package haus.nightmare.lib3270j.charset; + +import java.util.Arrays; + +/** + * Euro currency symbol (€) enabled EBCDIC code pages (CP1140 through CP1149). + */ +public class CpEuroVariants { + + public static class Cp1140 extends AbstractCodePage { + public static final String ID = "1140"; + public static final String DESCRIPTION = "US / Canada Euro - EBCDIC"; + public static final int CPGID = 1140; + public static final int GCSGID = 695; + + public Cp1140() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1140", modifyEuro(Cp037.MAPPING, 0x9F)); + } + } + + public static class Cp1141 extends AbstractCodePage { + public static final String ID = "1141"; + public static final String DESCRIPTION = "Germany / Austria Euro - EBCDIC"; + public static final int CPGID = 1141; + public static final int GCSGID = 695; + + public Cp1141() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1141", modifyEuro(Cp273.MAPPING, 0x9F)); + } + } + + public static class Cp1142 extends AbstractCodePage { + public static final String ID = "1142"; + public static final String DESCRIPTION = "Denmark / Norway Euro - EBCDIC"; + public static final int CPGID = 1142; + public static final int GCSGID = 695; + + public Cp1142() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1142", modifyEuro(Cp277.MAPPING, 0x5A)); + } + } + + public static class Cp1143 extends AbstractCodePage { + public static final String ID = "1143"; + public static final String DESCRIPTION = "Sweden / Finland Euro - EBCDIC"; + public static final int CPGID = 1143; + public static final int GCSGID = 695; + + public Cp1143() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1143", modifyEuro(Cp278.MAPPING, 0x5A)); + } + } + + public static class Cp1144 extends AbstractCodePage { + public static final String ID = "1144"; + public static final String DESCRIPTION = "Italy Euro - EBCDIC"; + public static final int CPGID = 1144; + public static final int GCSGID = 695; + + public Cp1144() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1144", modifyEuro(Cp280.MAPPING, 0x9F)); + } + } + + public static class Cp1145 extends AbstractCodePage { + public static final String ID = "1145"; + public static final String DESCRIPTION = "Spain / Latin America Euro - EBCDIC"; + public static final int CPGID = 1145; + public static final int GCSGID = 695; + + public Cp1145() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1145", modifyEuro(Cp284.MAPPING, 0x9F)); + } + } + + public static class Cp1146 extends AbstractCodePage { + public static final String ID = "1146"; + public static final String DESCRIPTION = "United Kingdom Euro - EBCDIC"; + public static final int CPGID = 1146; + public static final int GCSGID = 695; + + public Cp1146() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1146", modifyEuro(Cp285.MAPPING, 0x9F)); + } + } + + public static class Cp1147 extends AbstractCodePage { + public static final String ID = "1147"; + public static final String DESCRIPTION = "France Euro - EBCDIC"; + public static final int CPGID = 1147; + public static final int GCSGID = 695; + + public Cp1147() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1147", modifyEuro(Cp297.MAPPING, 0x9F)); + } + } + + public static class Cp1148 extends AbstractCodePage { + public static final String ID = "1148"; + public static final String DESCRIPTION = "International Latin-1 Euro - EBCDIC"; + public static final int CPGID = 1148; + public static final int GCSGID = 695; + + public Cp1148() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1148", modifyEuro(Cp500.MAPPING, 0x9F)); + } + } + + public static class Cp1149 extends AbstractCodePage { + public static final String ID = "1149"; + public static final String DESCRIPTION = "Iceland Euro - EBCDIC"; + public static final int CPGID = 1149; + public static final int GCSGID = 695; + + public Cp1149() { + super(ID, DESCRIPTION, CPGID, GCSGID, "1149", modifyEuro(Cp871.MAPPING, 0x9F)); + } + } + + private static int[] modifyEuro(int[] baseMapping, int euroPosition) { + int[] copy = Arrays.copyOf(baseMapping, 256); + copy[euroPosition & 0xFF] = '\u20AC'; // '€' + return copy; + } +} diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/EbcdicTranslator.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/EbcdicTranslator.java index e72cc7c..53b4c72 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/EbcdicTranslator.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/charset/EbcdicTranslator.java @@ -1,142 +1,161 @@ package haus.nightmare.lib3270j.charset; +import java.util.List; + /** - * EBCDIC ↔ Unicode translator. + * EBCDIC ↔ Unicode character translator conforming to IBM Host On-Demand (HoD v14). + * Modular architecture delegating to pluggable CodePage implementations (SBCS and DBCS). * Default: Code Page 037 (US/Canada EBCDIC). */ public class EbcdicTranslator { /** - * EBCDIC Code Page 037 to Unicode mapping. - * Index is the EBCDIC byte value (0x00-0xFF), value is the Unicode codepoint. + * EBCDIC Code Page 037 to Unicode mapping table preserved for legacy static access. */ - private static final int[] CP037_TO_UNICODE = { - // 00-0F - 0x0000, 0x0001, 0x0002, 0x0003, 0x009C, 0x0009, 0x0086, 0x007F, - 0x0097, 0x008D, 0x008E, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, - // 10-1F - 0x0010, 0x0011, 0x0012, 0x0013, 0x009D, 0x0085, 0x0008, 0x0087, - 0x0018, 0x0019, 0x0092, 0x008F, 0x001C, 0x001D, 0x001E, 0x001F, - // 20-2F - 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x000A, 0x0017, 0x001B, - 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x0005, 0x0006, 0x0007, - // 30-3F - 0x0090, 0x0091, 0x0016, 0x0093, 0x0094, 0x0095, 0x0096, 0x0004, - 0x0098, 0x0099, 0x009A, 0x009B, 0x0014, 0x0015, 0x009E, 0x001A, - // 40-4F (space, accent chars, punctuation) - 0x0020, 0x00A0, 0x00E2, 0x00E4, 0x00E0, 0x00E1, 0x00E3, 0x00E5, - 0x00E7, 0x00F1, 0x00A2, 0x002E, 0x003C, 0x0028, 0x002B, 0x007C, - // 50-5F - 0x0026, 0x00E9, 0x00EA, 0x00EB, 0x00E8, 0x00ED, 0x00EE, 0x00EF, - 0x00EC, 0x00DF, 0x0021, 0x0024, 0x002A, 0x0029, 0x003B, 0x00AC, - // 60-6F - 0x002D, 0x002F, 0x00C2, 0x00C4, 0x00C0, 0x00C1, 0x00C3, 0x00C5, - 0x00C7, 0x00D1, 0x00A6, 0x002C, 0x0025, 0x005F, 0x003E, 0x003F, - // 70-7F - 0x00F8, 0x00C9, 0x00CA, 0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, - 0x00CC, 0x0060, 0x003A, 0x0023, 0x0040, 0x0027, 0x003D, 0x0022, - // 80-8F (lowercase a-i) - 0x00D8, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, - 0x0068, 0x0069, 0x00AB, 0x00BB, 0x00F0, 0x00FD, 0x00FE, 0x00B1, - // 90-9F (lowercase j-r) - 0x00B0, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F, 0x0070, - 0x0071, 0x0072, 0x00AA, 0x00BA, 0x00E6, 0x00B8, 0x00C6, 0x00A4, - // A0-AF (lowercase s-z) - 0x00B5, 0x007E, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, - 0x0079, 0x007A, 0x00A1, 0x00BF, 0x00D0, 0x00DD, 0x00DE, 0x00AE, - // B0-BF - 0x005E, 0x00A3, 0x00A5, 0x00B7, 0x00A9, 0x00A7, 0x00B6, 0x00BC, - 0x00BD, 0x00BE, 0x005B, 0x005D, 0x00AF, 0x00A8, 0x00B4, 0x00D7, - // C0-CF (uppercase A-I) - 0x007B, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, - 0x0048, 0x0049, 0x00AD, 0x00F4, 0x00F6, 0x00F2, 0x00F3, 0x00F5, - // D0-DF (uppercase J-R) - 0x007D, 0x004A, 0x004B, 0x004C, 0x004D, 0x004E, 0x004F, 0x0050, - 0x0051, 0x0052, 0x00B9, 0x00FB, 0x00FC, 0x00F9, 0x00FA, 0x00FF, - // E0-EF (uppercase S-Z) - 0x005C, 0x00F7, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 0x0058, - 0x0059, 0x005A, 0x00B2, 0x00D4, 0x00D6, 0x00D2, 0x00D3, 0x00D5, - // F0-FF (digits 0-9) - 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, - 0x0038, 0x0039, 0x00B3, 0x00DB, 0x00DC, 0x00D9, 0x00DA, 0x009F, - }; + public static final int[] CP037_TO_UNICODE = Cp037.MAPPING; + + private CodePage activeCodePage; + + public EbcdicTranslator() { + this("037"); + } + + public EbcdicTranslator(String codePageId) { + setCodePage(codePageId); + } + + public EbcdicTranslator(CodePage codePage) { + setCodePage(codePage); + } /** - * Unicode to EBCDIC Code Page 037 mapping (for basic Latin + Latin-1). - * Index is the Unicode codepoint (0x00-0xFF), value is the EBCDIC byte (-1 if unmappable). + * Set active code page by identifier or alias (e.g. "037", "1047", "500", "273", "1140", "930"). */ - private static final int[] UNICODE_TO_CP037 = new int[256]; + public synchronized void setCodePage(String codePageId) { + this.activeCodePage = CodePageRegistry.getCodePage(codePageId); + } - static { - // Build reverse mapping - java.util.Arrays.fill(UNICODE_TO_CP037, -1); - for (int i = 0; i < 256; i++) { - int unicode = CP037_TO_UNICODE[i]; - if (unicode < 256) { - UNICODE_TO_CP037[unicode] = i; - } + /** + * Set active code page directly. + */ + public synchronized void setCodePage(CodePage codePage) { + if (codePage != null) { + this.activeCodePage = codePage; + } else { + this.activeCodePage = CodePageRegistry.getCodePage("037"); } } /** - * Translate EBCDIC byte to Unicode character. + * Get the active CodePage descriptor and translator. */ - public char ebcdicToUnicode(int ebc) { - return (char) CP037_TO_UNICODE[ebc & 0xFF]; + public synchronized CodePage getCodePage() { + return activeCodePage; } /** - * Static helper to translate EBCDIC byte to Unicode character. + * Get the active code page identifier string. + */ + public synchronized String getCodePageId() { + return activeCodePage.getCodePageId(); + } + + /** + * Get active Code Page Global ID (CPGID / CCSID) for Query Replies. + */ + public synchronized int getCpgid() { + return activeCodePage.getCpgid(); + } + + /** + * Get active Graphic Character Set Global ID (GCSGID) for Query Replies. + */ + public synchronized int getCgcsgid() { + return activeCodePage.getCgcsgid(); + } + + /** + * Check if active code page is Double-Byte / mixed DBCS. + */ + public synchronized boolean isDBCSCodePage() { + return activeCodePage.isDBCS(); + } + + /** + * Translate double-byte EBCDIC pair (b1, b2) to Unicode. + */ + public synchronized char dbcsToUnicode(int b1, int b2) { + return activeCodePage.dbcsToUnicode(b1, b2); + } + + /** + * Translate Unicode character to double-byte EBCDIC ((b1 << 8) | b2). + */ + public synchronized int unicodeToDbcs(char unicode) { + return activeCodePage.unicodeToDbcs(unicode); + } + + /** + * Translate EBCDIC byte to Unicode character using active code page. + */ + public char ebcdicToUnicode(int ebc) { + return activeCodePage.ebcdicToUnicode(ebc); + } + + /** + * Static helper to translate EBCDIC byte to Unicode character (using standard CP037). */ public static char toUnicode(int ebc) { return (char) CP037_TO_UNICODE[ebc & 0xFF]; } /** - * Static helper to translate EBCDIC byte to ASCII character. + * Static helper to translate EBCDIC byte to ASCII character (using standard CP037). */ public static char ebcdicToAscii(int ebc) { return toUnicode(ebc); } /** - * Translate Unicode character to EBCDIC byte. + * Translate Unicode character to EBCDIC byte using active code page. * Returns -1 if the character cannot be mapped. */ public int unicodeToEbcdic(char unicode) { - if (unicode < 256) { - return UNICODE_TO_CP037[unicode]; - } - return -1; + return activeCodePage.unicodeToEbcdic(unicode); } /** * Translate Unicode character to EBCDIC, returning EBCDIC space (0x40) if unmappable. */ public byte unicodeToEbcdicSafe(char unicode) { - int ebc = unicodeToEbcdic(unicode); - return (byte) (ebc >= 0 ? ebc : 0x40); + return activeCodePage.unicodeToEbcdicSafe(unicode); } /** - * Translate a byte array from EBCDIC to a Unicode string. + * Translate a byte array from EBCDIC to a Unicode string using active code page. */ public String ebcdicToString(byte[] ebcdic, int offset, int length) { - StringBuilder sb = new StringBuilder(length); - for (int i = 0; i < length; i++) { - sb.append(ebcdicToUnicode(ebcdic[offset + i] & 0xFF)); - } - return sb.toString(); + return activeCodePage.ebcdicToString(ebcdic, offset, length); } /** - * Translate a Unicode string to EBCDIC byte array. + * Translate a Unicode string to EBCDIC byte array using active code page. */ public byte[] stringToEbcdic(String s) { - byte[] result = new byte[s.length()]; - for (int i = 0; i < s.length(); i++) { - result[i] = unicodeToEbcdicSafe(s.charAt(i)); - } - return result; + return activeCodePage.stringToEbcdic(s); + } + + /** + * Get an array of all registered code page ID strings. + */ + public static String[] getAvailableCodePages() { + return CodePageRegistry.getAvailableCodePageIds(); + } + + /** + * Get a list of all registered CodePage instances. + */ + public static List getAllCodePages() { + return CodePageRegistry.getAvailableCodePages(); } } diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java index ffafae7..43a0446 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/datastream/QueryReplyBuilder.java @@ -337,6 +337,13 @@ public class QueryReplyBuilder { int charW = getCharWidth(); int charH = getCharHeight(); + int cgcsgid = 0x02B9; + int cpgid = 0x0025; + if (screen != null && screen.getTranslator() != null) { + cgcsgid = screen.getTranslator().getCgcsgid(); + cpgid = screen.getTranslator().getCpgid(); + } + if (graphicsMode.isProgrammedSymbolsEnabled()) { // Programmed Symbols mode (3279 PS with LoadPS 0x0A, flags1 = 0xA2 for GE + PS + CGCSGID) ByteArrayOutputStream out = new ByteArrayOutputStream(65); @@ -349,8 +356,10 @@ public class QueryReplyBuilder { out.write(0x00); // Load PS device type (low) out.write(0x00); // reserved out.write(0x07); // DL = 7 bytes per descriptor - // Descriptor 1 (SET 0): default character set (non-loadable, single plane, CP037) - out.write(0x00); out.write(0x10); out.write(0x00); out.write(0x02); out.write(0xb9); out.write(0x00); out.write(0x25); + // Descriptor 1 (SET 0): default character set (non-loadable, single plane, active codepage) + out.write(0x00); out.write(0x10); out.write(0x00); + out.write((cgcsgid >> 8) & 0xFF); out.write(cgcsgid & 0xFF); + out.write((cpgid >> 8) & 0xFF); out.write(cpgid & 0xFF); // Descriptor 2 (SET 1): APL/GE character set out.write(0x01); out.write(0x00); out.write(0xf1); out.write(0x03); out.write(0xc3); out.write(0x01); out.write(0x36); // Loadable Single-Plane PS Sets (PSA, PSB: Slots 2, 3) - Flags = 0x80 (Loadable, single plane) @@ -375,8 +384,10 @@ public class QueryReplyBuilder { out.write(0x00); out.write(0x00); out.write(0x07); // DL = 7 - // Set 0 (Base EBCDIC - Non-loadable, single plane, CP037) - out.write(0x00); out.write(0x10); out.write(0x00); out.write(0x02); out.write(0xb9); out.write(0x00); out.write(0x25); + // Set 0 (Base EBCDIC - Non-loadable, single plane, active codepage) + out.write(0x00); out.write(0x10); out.write(0x00); + out.write((cgcsgid >> 8) & 0xFF); out.write(cgcsgid & 0xFF); + out.write((cpgid >> 8) & 0xFF); out.write(cpgid & 0xFF); // Set 1 (APL/Text) out.write(0x01); out.write(0x00); out.write(0xf1); out.write(0x03); out.write(0xc3); out.write(0x01); out.write(0x36); return out.toByteArray(); diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/ecl/ECLXfer.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/ecl/ECLXfer.java index 678cbc9..f0a5d71 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/ecl/ECLXfer.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/ecl/ECLXfer.java @@ -125,6 +125,9 @@ public class ECLXfer implements FTCut.FTCutListener, FTDft.FTDftListener { */ public void getFile(String hostFile, String localFile, String options, int mode, String codePage, ECLXferListener listener) { if (listener != null) addXferListener(listener); + if (codePage != null && !codePage.trim().isEmpty() && translator != null) { + translator.setCodePage(codePage); + } ReceiveFile(localFile, hostFile, options); } @@ -133,6 +136,9 @@ public class ECLXfer implements FTCut.FTCutListener, FTDft.FTDftListener { */ public void putFile(String localFile, String hostFile, String options, int mode, String codePage, ECLXferListener listener) { if (listener != null) addXferListener(listener); + if (codePage != null && !codePage.trim().isEmpty() && translator != null) { + translator.setCodePage(codePage); + } SendFile(localFile, hostFile, options); } diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTConfig.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTConfig.java index 242a582..01eff83 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTConfig.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/ft/FTConfig.java @@ -60,6 +60,7 @@ public class FTConfig { private int avblock = 0; private int dftBufferSize = FTConstants.DFT_BUF; private String otherOptions = null; + private String codePage = null; // ========== Derived convenience getters ========== @@ -142,6 +143,9 @@ public class FTConfig { public void setOverwrite(boolean overwrite) { if (overwrite) this.existAction = ExistAction.REPLACE; } + + public String getCodePage() { return codePage; } + public void setCodePage(String codePage) { this.codePage = codePage; } public void setReceive(boolean receive) { setDirection(receive ? Direction.RECEIVE : Direction.SEND); diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/input/InputProcessor.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/input/InputProcessor.java index 613912e..d299250 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/input/InputProcessor.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/input/InputProcessor.java @@ -1071,6 +1071,167 @@ public class InputProcessor { } } + /** + * Enter a double-byte (DBCS) character at the given screen address (or cursor position if pos < 0). + */ + public boolean DBCSinputChar(char c, int pos) { + if (keyboardLocked || screen == null) return false; + int size = screen.getRows() * screen.getCols(); + if (size <= 0) return false; + + int baddr = (pos >= 0) ? pos : screen.getCursorAddress(); + baddr = ((baddr % size) + size) % size; + + if (screen.isFormatted()) { + byte fa = screen.getFieldAttributeAt(baddr); + if (faIsProtected(fa & 0xFF)) return false; + } + + int dbcs = translator.unicodeToDbcs(c); + if (dbcs < 0) { + // Fall back to SBCS typing + typeCharacter(c); + return true; + } + + int b1 = (dbcs >> 8) & 0xFF; + int b2 = dbcs & 0xFF; + + int nextAddr = screen.incrementAddress(baddr); + if (screen.getCell(baddr).isFieldAttribute() || screen.getCell(nextAddr).isFieldAttribute()) { + return false; + } + + screen.setCell(baddr, b1); + screen.getCell(baddr).ucs4 = c; + screen.setCell(nextAddr, b2); + screen.getCell(nextAddr).ucs4 = c; + + // Set MDT + if (screen.isFormatted()) { + int faAddr = screen.findFieldAttribute(baddr); + if (faAddr >= 0) { + screen.getCell(faAddr).fa |= FA_MODIFY; + } + } + + screen.setCursorAddress(screen.incrementAddress(nextAddr)); + screen.markAllChanged(); + return true; + } + + /** + * Shift characters right within a field by shiftAmount positions. + */ + public void shiftCharactersRightInField(int baddr, int shiftAmount) { + if (screen == null || shiftAmount <= 0) return; + int size = screen.getRows() * screen.getCols(); + if (size <= 0) return; + + baddr = ((baddr % size) + size) % size; + + // Find end of field + int endAddr = baddr; + int count = 0; + while (!screen.getCell(screen.incrementAddress(endAddr)).isFieldAttribute() && count < size) { + endAddr = screen.incrementAddress(endAddr); + count++; + if (endAddr == baddr) break; + } + + for (int s = 0; s < shiftAmount; s++) { + int dst = endAddr; + int shiftCount = 0; + while (dst != baddr && shiftCount < size) { + int src = screen.decrementAddress(dst); + screen.getCell(dst).ec = screen.getCell(src).ec; + screen.getCell(dst).ucs4 = screen.getCell(src).ucs4; + dst = src; + shiftCount++; + } + screen.getCell(baddr).ec = 0; + screen.getCell(baddr).ucs4 = 0; + } + screen.markAllChanged(); + } + + /** + * Shift characters right within the current line by count positions. + */ + public void shiftCharactersRightInLine(int row, int col, int shiftAmount) { + if (screen == null || shiftAmount <= 0) return; + int cols = screen.getCols(); + int baddr = screen.rowColToAddress(row, col); + int lineEnd = screen.rowColToAddress(row, cols - 1); + + for (int s = 0; s < shiftAmount; s++) { + for (int dst = lineEnd; dst > baddr; dst--) { + screen.getCell(dst).ec = screen.getCell(dst - 1).ec; + screen.getCell(dst).ucs4 = screen.getCell(dst - 1).ucs4; + } + screen.getCell(baddr).ec = 0; + screen.getCell(baddr).ucs4 = 0; + } + screen.markAllChanged(); + } + + /** + * Shift characters right across the screen by count positions. + */ + public void shiftCharactersRightInScreen(int pos, int shiftAmount) { + if (screen == null || shiftAmount <= 0) return; + int size = screen.getRows() * screen.getCols(); + pos = ((pos % size) + size) % size; + + for (int s = 0; s < shiftAmount; s++) { + for (int dst = size - 1; dst > pos; dst--) { + screen.getCell(dst).ec = screen.getCell(dst - 1).ec; + screen.getCell(dst).ucs4 = screen.getCell(dst - 1).ucs4; + } + screen.getCell(pos).ec = 0; + screen.getCell(pos).ucs4 = 0; + } + screen.markAllChanged(); + } + + /** + * Insert a double-byte (DBCS) character with insert-mode shifting (2 positions). + */ + public boolean DBCSinputCharInsert(char c, int pos) { + if (keyboardLocked || screen == null) return false; + int baddr = (pos >= 0) ? pos : screen.getCursorAddress(); + + // Shift 2 characters right + shiftCharactersRightInField(baddr, 2); + return DBCSinputChar(c, baddr); + } + + /** + * Clean up orphaned or empty Shift-Out / Shift-In (SO 0x0E followed by SI 0x0F) control codes. + */ + public void clearUnusedSISO(int pos) { + if (screen == null) return; + int size = screen.getRows() * screen.getCols(); + if (size <= 0) return; + + int start = (pos >= 0) ? pos : 0; + int end = (pos >= 0) ? Math.min(size, pos + screen.getCols()) : size; + + for (int i = start; i < end - 1; i++) { + ExtendedAttribute ea1 = screen.getCell(i); + ExtendedAttribute ea2 = screen.getCell(i + 1); + if (!ea1.isFieldAttribute() && !ea2.isFieldAttribute()) { + if ((ea1.ec & 0xFF) == 0x0E && (ea2.ec & 0xFF) == 0x0F) { + ea1.ec = 0; + ea1.ucs4 = 0; + ea2.ec = 0; + ea2.ucs4 = 0; + screen.markAllChanged(); + } + } + } + } + /** Get a reference to the TelnetFSM for direct SF operations. */ public TelnetFSM getTelnetFSM() { return fsm; diff --git a/lib3270j/src/main/java/haus/nightmare/lib3270j/screen/ScreenBuffer.java b/lib3270j/src/main/java/haus/nightmare/lib3270j/screen/ScreenBuffer.java index a51fb47..a03d7d9 100644 --- a/lib3270j/src/main/java/haus/nightmare/lib3270j/screen/ScreenBuffer.java +++ b/lib3270j/src/main/java/haus/nightmare/lib3270j/screen/ScreenBuffer.java @@ -46,6 +46,10 @@ public class ScreenBuffer { return renderLock; } + public EbcdicTranslator getTranslator() { + return translator; + } + public ScreenBuffer(TerminalModel model, EbcdicTranslator translator) { this.translator = translator; this.defRows = MODEL_2_ROWS; diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/CodePageTest.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/CodePageTest.java new file mode 100644 index 0000000..0f5f78f --- /dev/null +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/CodePageTest.java @@ -0,0 +1,263 @@ +package haus.nightmare.lib3270j.charset; + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for Single-Byte Character Set (SBCS) and Euro EBCDIC codepages. + */ +public class CodePageTest { + + @Test + public void testCp037USCanada() { + CodePage cp = CodePageRegistry.getCodePage("037"); + assertEquals("037", cp.getCodePageId()); + assertEquals(37, cp.getCpgid()); + assertEquals(697, cp.getCgcsgid()); + assertFalse(cp.isDBCS()); + + // Basic ASCII roundtrip + assertEquals('A', cp.ebcdicToUnicode(0xC1)); + assertEquals(0xC1, cp.unicodeToEbcdic('A')); + assertEquals('Z', cp.ebcdicToUnicode(0xE9)); + assertEquals('0', cp.ebcdicToUnicode(0xF0)); + assertEquals(' ', cp.ebcdicToUnicode(0x40)); + + // CP037 special punctuation + assertEquals('[', cp.ebcdicToUnicode(0xBA)); + assertEquals(']', cp.ebcdicToUnicode(0xBB)); + assertEquals('{', cp.ebcdicToUnicode(0xC0)); + assertEquals('}', cp.ebcdicToUnicode(0xD0)); + assertEquals('\\', cp.ebcdicToUnicode(0xE0)); + assertEquals('~', cp.ebcdicToUnicode(0xA1)); + assertEquals('^', cp.ebcdicToUnicode(0xB0)); + } + + @Test + public void testCp1047OpenSystems() { + CodePage cp = CodePageRegistry.getCodePage("1047"); + assertEquals("1047", cp.getCodePageId()); + assertEquals(1047, cp.getCpgid()); + assertEquals(103, cp.getCgcsgid()); + + // CP1047 bracket differences + assertEquals('[', cp.ebcdicToUnicode(0xAD)); + assertEquals(']', cp.ebcdicToUnicode(0xBD)); + assertEquals(0xAD, cp.unicodeToEbcdic('[')); + assertEquals(0xBD, cp.unicodeToEbcdic(']')); + assertEquals('^', cp.ebcdicToUnicode(0x5F)); + assertEquals(0x5F, cp.unicodeToEbcdic('^')); + assertEquals('¬', cp.ebcdicToUnicode(0xB0)); + } + + @Test + public void testCp500International() { + CodePage cp = CodePageRegistry.getCodePage("500"); + assertEquals("500", cp.getCodePageId()); + assertEquals(500, cp.getCpgid()); + assertEquals(697, cp.getCgcsgid()); + + // CP500 bracket & exclamation mappings + assertEquals('[', cp.ebcdicToUnicode(0x4A)); + assertEquals(']', cp.ebcdicToUnicode(0x5A)); + assertEquals(0x4A, cp.unicodeToEbcdic('[')); + assertEquals(0x5A, cp.unicodeToEbcdic(']')); + assertEquals('!', cp.ebcdicToUnicode(0x4F)); + assertEquals('$', cp.ebcdicToUnicode(0x5B)); + } + + @Test + public void testCp273GermanyAustria() { + CodePage cp = CodePageRegistry.getCodePage("273"); + assertEquals("273", cp.getCodePageId()); + assertEquals(273, cp.getCpgid()); + + // German umlauts and eszett roundtrip + int ebcAe = cp.unicodeToEbcdic('ä'); + assertTrue(ebcAe >= 0); + assertEquals('ä', cp.ebcdicToUnicode(ebcAe)); + + int ebcOe = cp.unicodeToEbcdic('ö'); + assertTrue(ebcOe >= 0); + assertEquals('ö', cp.ebcdicToUnicode(ebcOe)); + + int ebcUe = cp.unicodeToEbcdic('ü'); + assertTrue(ebcUe >= 0); + assertEquals('ü', cp.ebcdicToUnicode(ebcUe)); + + int ebcSs = cp.unicodeToEbcdic('ß'); + assertTrue(ebcSs >= 0); + assertEquals('ß', cp.ebcdicToUnicode(ebcSs)); + } + + @Test + public void testCp277DenmarkNorway() { + CodePage cp = CodePageRegistry.getCodePage("277"); + assertEquals("277", cp.getCodePageId()); + + int ebcAe = cp.unicodeToEbcdic('æ'); + assertTrue(ebcAe >= 0); + assertEquals('æ', cp.ebcdicToUnicode(ebcAe)); + + int ebcOe = cp.unicodeToEbcdic('ø'); + assertTrue(ebcOe >= 0); + assertEquals('ø', cp.ebcdicToUnicode(ebcOe)); + + int ebcAa = cp.unicodeToEbcdic('å'); + assertTrue(ebcAa >= 0); + assertEquals('å', cp.ebcdicToUnicode(ebcAa)); + } + + @Test + public void testCp278SwedenFinland() { + CodePage cp = CodePageRegistry.getCodePage("278"); + assertEquals("278", cp.getCodePageId()); + + int ebcAe = cp.unicodeToEbcdic('ä'); + assertTrue(ebcAe >= 0); + assertEquals('ä', cp.ebcdicToUnicode(ebcAe)); + + int ebcOe = cp.unicodeToEbcdic('ö'); + assertTrue(ebcOe >= 0); + assertEquals('ö', cp.ebcdicToUnicode(ebcOe)); + + int ebcAa = cp.unicodeToEbcdic('å'); + assertTrue(ebcAa >= 0); + assertEquals('å', cp.ebcdicToUnicode(ebcAa)); + } + + @Test + public void testCp280Italy() { + CodePage cp = CodePageRegistry.getCodePage("280"); + assertEquals("280", cp.getCodePageId()); + + int ebcA = cp.unicodeToEbcdic('à'); + assertTrue(ebcA >= 0); + assertEquals('à', cp.ebcdicToUnicode(ebcA)); + + int ebcE = cp.unicodeToEbcdic('é'); + assertTrue(ebcE >= 0); + assertEquals('é', cp.ebcdicToUnicode(ebcE)); + } + + @Test + public void testCp284SpainLatinAmerica() { + CodePage cp = CodePageRegistry.getCodePage("284"); + assertEquals("284", cp.getCodePageId()); + + int ebcN = cp.unicodeToEbcdic('ñ'); + assertTrue(ebcN >= 0); + assertEquals('ñ', cp.ebcdicToUnicode(ebcN)); + + int ebcNu = cp.unicodeToEbcdic('Ñ'); + assertTrue(ebcNu >= 0); + assertEquals('Ñ', cp.ebcdicToUnicode(ebcNu)); + } + + @Test + public void testCp285UK() { + CodePage cp = CodePageRegistry.getCodePage("285"); + assertEquals("285", cp.getCodePageId()); + + int ebcPound = cp.unicodeToEbcdic('£'); + assertTrue(ebcPound >= 0); + assertEquals('£', cp.ebcdicToUnicode(ebcPound)); + } + + @Test + public void testCp297France() { + CodePage cp = CodePageRegistry.getCodePage("297"); + assertEquals("297", cp.getCodePageId()); + + int ebcA = cp.unicodeToEbcdic('à'); + assertTrue(ebcA >= 0); + assertEquals('à', cp.ebcdicToUnicode(ebcA)); + + int ebcE = cp.unicodeToEbcdic('é'); + assertTrue(ebcE >= 0); + assertEquals('é', cp.ebcdicToUnicode(ebcE)); + } + + @Test + public void testCp870Latin2() { + CodePage cp = CodePageRegistry.getCodePage("870"); + assertEquals("870", cp.getCodePageId()); + assertEquals(870, cp.getCpgid()); + assertEquals(959, cp.getCgcsgid()); + + int ebcS = cp.unicodeToEbcdic('š'); + assertTrue(ebcS >= 0); + assertEquals('š', cp.ebcdicToUnicode(ebcS)); + } + + @Test + public void testCp871Iceland() { + CodePage cp = CodePageRegistry.getCodePage("871"); + assertEquals("871", cp.getCodePageId()); + + int ebcThorn = cp.unicodeToEbcdic('þ'); + assertTrue(ebcThorn >= 0); + assertEquals('þ', cp.ebcdicToUnicode(ebcThorn)); + + int ebcEth = cp.unicodeToEbcdic('ð'); + assertTrue(ebcEth >= 0); + assertEquals('ð', cp.ebcdicToUnicode(ebcEth)); + } + + @Test + public void testCp875Greece() { + CodePage cp = CodePageRegistry.getCodePage("875"); + assertEquals("875", cp.getCodePageId()); + + int ebcAlpha = cp.unicodeToEbcdic('α'); + assertTrue(ebcAlpha >= 0); + assertEquals('α', cp.ebcdicToUnicode(ebcAlpha)); + + int ebcOmega = cp.unicodeToEbcdic('Ω'); + assertTrue(ebcOmega >= 0); + assertEquals('Ω', cp.ebcdicToUnicode(ebcOmega)); + } + + @Test + public void testCp1026Turkey() { + CodePage cp = CodePageRegistry.getCodePage("1026"); + assertEquals("1026", cp.getCodePageId()); + + int ebcS = cp.unicodeToEbcdic('ş'); + assertTrue(ebcS >= 0); + assertEquals('ş', cp.ebcdicToUnicode(ebcS)); + } + + @Test + public void testEuroVariantsCp1140To1149() { + CodePage cp1140 = CodePageRegistry.getCodePage("1140"); + assertEquals(1140, cp1140.getCpgid()); + assertEquals(695, cp1140.getCgcsgid()); + assertEquals(0x9F, cp1140.unicodeToEbcdic('€')); + assertEquals('€', cp1140.ebcdicToUnicode(0x9F)); + + CodePage cp1141 = CodePageRegistry.getCodePage("1141"); + assertEquals(0x9F, cp1141.unicodeToEbcdic('€')); + assertEquals('€', cp1141.ebcdicToUnicode(0x9F)); + + CodePage cp1148 = CodePageRegistry.getCodePage("1148"); + assertEquals(1148, cp1148.getCpgid()); + assertEquals(0x9F, cp1148.unicodeToEbcdic('€')); + assertEquals('€', cp1148.ebcdicToUnicode(0x9F)); + } + + @Test + public void testStringRoundTrips() { + CodePage cp273 = CodePageRegistry.getCodePage("273"); + String german = "Grüße aus München: Äpfel & Öfen!"; + byte[] ebcdic = cp273.stringToEbcdic(german); + String restored = cp273.ebcdicToString(ebcdic, 0, ebcdic.length); + assertEquals(german, restored); + + CodePage cp284 = CodePageRegistry.getCodePage("284"); + String spanish = "España y México: ¡Hola año nuevo!"; + byte[] espEbcdic = cp284.stringToEbcdic(spanish); + String espRestored = cp284.ebcdicToString(espEbcdic, 0, espEbcdic.length); + assertEquals(spanish, espRestored); + } +} diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/DBCSTranslationTest.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/DBCSTranslationTest.java new file mode 100644 index 0000000..213772b --- /dev/null +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/DBCSTranslationTest.java @@ -0,0 +1,98 @@ +package haus.nightmare.lib3270j.charset; + +import haus.nightmare.lib3270j.TerminalModel; +import haus.nightmare.lib3270j.input.InputProcessor; +import haus.nightmare.lib3270j.screen.ScreenBuffer; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * Unit tests for Mixed Double-Byte Character Set (DBCS) codepages and SO/SI state processing. + */ +public class DBCSTranslationTest { + + @Test + public void testDBCSProperties() { + CodePage cp930 = CodePageRegistry.getCodePage("930"); + assertTrue(cp930.isDBCS()); + assertEquals("930", cp930.getCodePageId()); + assertEquals(930, cp930.getCpgid()); + assertEquals(1172, cp930.getCgcsgid()); + + CodePage cp935 = CodePageRegistry.getCodePage("935"); + assertTrue(cp935.isDBCS()); + assertEquals(935, cp935.getCpgid()); + + CodePage cp937 = CodePageRegistry.getCodePage("937"); + assertTrue(cp937.isDBCS()); + assertEquals(937, cp937.getCpgid()); + + CodePage cp933 = CodePageRegistry.getCodePage("933"); + assertTrue(cp933.isDBCS()); + assertEquals(933, cp933.getCpgid()); + } + + @Test + public void testIdeographicSpace() { + CodePage cp930 = CodePageRegistry.getCodePage("930"); + assertEquals('\u3000', cp930.dbcsToUnicode(0x40, 0x40)); + assertEquals(0x4040, cp930.unicodeToDbcs('\u3000')); + } + + @Test + public void testDBCSStreamSOSIProcessing() { + AbstractDBCSCodePage cp = (AbstractDBCSCodePage) CodePageRegistry.getCodePage("930"); + cp.registerDbcsPair(0x4341, '\u6771'); // '東' + cp.registerDbcsPair(0x4342, '\u4EAC'); // '京' + + // Mixed byte sequence: "IBM" + SO + 東 + 京 + SI + "123" + // 'I'=0xC9, 'B'=0xC2, 'M'=0xD4, SO=0x0E, 0x43, 0x41, 0x43, 0x42, SI=0x0F, '1'=0xF1, '2'=0xF2, '3'=0xF3 + byte[] mixed = new byte[] { + (byte) 0xC9, (byte) 0xC2, (byte) 0xD4, + 0x0E, (byte) 0x43, (byte) 0x41, (byte) 0x43, (byte) 0x42, 0x0F, + (byte) 0xF1, (byte) 0xF2, (byte) 0xF3 + }; + + String translated = cp.ebcdicToString(mixed, 0, mixed.length); + assertEquals("IBM東京123", translated); + + // Reverse conversion: stringToEbcdic + byte[] reEncoded = cp.stringToEbcdic("IBM東京123"); + assertArrayEquals(mixed, reEncoded); + } + + @Test + public void testInputProcessorDBCSInput() { + EbcdicTranslator translator = new EbcdicTranslator("930"); + AbstractDBCSCodePage cp = (AbstractDBCSCodePage) translator.getCodePage(); + cp.registerDbcsPair(0x4545, '\u6771'); + + ScreenBuffer screen = new ScreenBuffer(TerminalModel.IBM_3279_2, translator); + InputProcessor input = new InputProcessor(screen, translator, null); + + // Enter DBCS character at pos 0 + boolean ok = input.DBCSinputChar('\u6771', 0); + assertTrue(ok); + assertEquals(0x45, screen.getCell(0).ec & 0xFF); + assertEquals(0x45, screen.getCell(1).ec & 0xFF); + assertEquals('\u6771', (char) screen.getCell(0).ucs4); + assertEquals(2, screen.getCursorAddress()); + } + + @Test + public void testClearUnusedSISO() { + EbcdicTranslator translator = new EbcdicTranslator("930"); + ScreenBuffer screen = new ScreenBuffer(TerminalModel.IBM_3279_2, translator); + InputProcessor input = new InputProcessor(screen, translator, null); + + // Place orphaned SO (0x0E) followed immediately by SI (0x0F) + screen.setCell(5, 0x0E); + screen.setCell(6, 0x0F); + assertEquals(0x0E, screen.getCell(5).ec & 0xFF); + assertEquals(0x0F, screen.getCell(6).ec & 0xFF); + + input.clearUnusedSISO(0); + assertEquals(0, screen.getCell(5).ec & 0xFF); + assertEquals(0, screen.getCell(6).ec & 0xFF); + } +} diff --git a/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/EbcdicTranslatorPhase4Test.java b/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/EbcdicTranslatorPhase4Test.java new file mode 100644 index 0000000..d014716 --- /dev/null +++ b/lib3270j/src/test/java/haus/nightmare/lib3270j/charset/EbcdicTranslatorPhase4Test.java @@ -0,0 +1,133 @@ +package haus.nightmare.lib3270j.charset; + +import haus.nightmare.lib3270j.ConnectionConfig; +import haus.nightmare.lib3270j.Telnet3270Client; +import haus.nightmare.lib3270j.TerminalModel; +import haus.nightmare.lib3270j.datastream.QueryReplyBuilder; +import haus.nightmare.lib3270j.screen.ScreenBuffer; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; + +/** + * High-level integration tests for Phase 4 Character Translation & Multi-Codepage subsystem. + */ +public class EbcdicTranslatorPhase4Test { + + @Test + public void testRegistryAliases() { + assertEquals("037", CodePageRegistry.getCodePage("cp037").getCodePageId()); + assertEquals("037", CodePageRegistry.getCodePage("IBM-037").getCodePageId()); + assertEquals("037", CodePageRegistry.getCodePage("ebcdic-cp-us").getCodePageId()); + + assertEquals("1047", CodePageRegistry.getCodePage("posix").getCodePageId()); + assertEquals("1047", CodePageRegistry.getCodePage("open-systems").getCodePageId()); + assertEquals("1047", CodePageRegistry.getCodePage("IBM1047").getCodePageId()); + + assertEquals("273", CodePageRegistry.getCodePage("de").getCodePageId()); + assertEquals("273", CodePageRegistry.getCodePage("germany").getCodePageId()); + + assertEquals("285", CodePageRegistry.getCodePage("uk").getCodePageId()); + assertEquals("285", CodePageRegistry.getCodePage("gb").getCodePageId()); + + assertEquals("297", CodePageRegistry.getCodePage("fr").getCodePageId()); + assertEquals("297", CodePageRegistry.getCodePage("france").getCodePageId()); + + assertEquals("930", CodePageRegistry.getCodePage("ja").getCodePageId()); + assertEquals("935", CodePageRegistry.getCodePage("chinese-simplified").getCodePageId()); + } + + @Test + public void testDynamicCodePageSwitching() { + EbcdicTranslator translator = new EbcdicTranslator("037"); + assertEquals("037", translator.getCodePageId()); + assertEquals(37, translator.getCpgid()); + assertEquals('[', translator.ebcdicToUnicode(0xBA)); + + // Switch to CP1047 + translator.setCodePage("1047"); + assertEquals("1047", translator.getCodePageId()); + assertEquals(1047, translator.getCpgid()); + assertEquals('[', translator.ebcdicToUnicode(0xAD)); + + // Switch to German CP273 + translator.setCodePage("273"); + assertEquals("273", translator.getCodePageId()); + assertEquals(273, translator.getCpgid()); + int ebcAe = translator.unicodeToEbcdic('ä'); + assertTrue(ebcAe >= 0); + assertEquals('ä', translator.ebcdicToUnicode(ebcAe)); + + // Switch to Euro CP1140 + translator.setCodePage("1140"); + assertEquals("1140", translator.getCodePageId()); + assertEquals(1140, translator.getCpgid()); + assertEquals('€', translator.ebcdicToUnicode(0x9F)); + } + + @Test + public void testQueryReplyBuilderCharsetsSynchronization() { + ScreenBuffer screen = new ScreenBuffer(TerminalModel.IBM_3279_2, new EbcdicTranslator("273")); + QueryReplyBuilder builder = new QueryReplyBuilder(screen); + + byte[] qrs = builder.buildAllQueryReplies(80, 24, 1920); + assertNotNull(qrs); + assertTrue(qrs.length > 0); + + // Scan for QR_CHARSETS (0x85) + int idx = 1; // skip AID_SF + boolean foundCharsets = false; + while (idx < qrs.length) { + int len = ((qrs[idx] & 0xFF) << 8) | (qrs[idx + 1] & 0xFF); + int sfid = qrs[idx + 2] & 0xFF; + int qrCode = qrs[idx + 3] & 0xFF; + + if (qrCode == 0x85) { // QR_CHARSETS + foundCharsets = true; + // Check Set 0 Descriptor (CPGID 273 = 0x0111) + // Descriptor 1 starts after 9-byte header + int descOffset = idx + 4 + 9; + int cpgidHigh = qrs[descOffset + 5] & 0xFF; + int cpgidLow = qrs[descOffset + 6] & 0xFF; + int cpgid = (cpgidHigh << 8) | cpgidLow; + assertEquals(273, cpgid, "Query Reply Charsets should advertise active CPGID 273"); + break; + } + idx += len; + } + assertTrue(foundCharsets, "QR_CHARSETS must be present in Query Replies"); + } + + @Test + public void testScreenBufferTranslateWithActiveCodePage() { + EbcdicTranslator translator = new EbcdicTranslator("273"); + ScreenBuffer screen = new ScreenBuffer(TerminalModel.IBM_3279_2, translator); + + // Put German characters in buffer + screen.setCell(0, translator.unicodeToEbcdic('ä')); + screen.setCell(1, translator.unicodeToEbcdic('ö')); + screen.setCell(2, translator.unicodeToEbcdic('ü')); + screen.setCell(3, translator.unicodeToEbcdic('ß')); + + screen.translateToUnicode(); + + assertEquals('ä', (char) screen.getCell(0).ucs4); + assertEquals('ö', (char) screen.getCell(1).ucs4); + assertEquals('ü', (char) screen.getCell(2).ucs4); + assertEquals('ß', (char) screen.getCell(3).ucs4); + } + + @Test + public void testTelnet3270ClientWithConfiguredCodePage() { + ConnectionConfig config = new ConnectionConfig("mainframe.example.com", 23); + config.setCodePage("500"); + + Telnet3270Client client = new Telnet3270Client(config); + assertEquals("500", client.getCodePage()); + assertEquals(500, client.getTranslator().getCpgid()); + + // Test changing at runtime + client.setCodePage("1047"); + assertEquals("1047", client.getCodePage()); + assertEquals(1047, client.getTranslator().getCpgid()); + } +}