Files
j3270/lib3270j/src/main/java/org/lib3270j/datastream/QueryReplyBuilder.java
T
rudi a1b1273022
Build and Test j3270 / Build JAR & Run Tests (push) Failing after 2m24s
Fix gradle and add workflows
2026-08-20 18:24:35 -04:00

290 lines
11 KiB
Java

package org.lib3270j.datastream;
import java.io.ByteArrayOutputStream;
import java.util.logging.Logger;
import org.lib3270j.screen.ScreenBuffer;
import static org.lib3270j.protocol.DS3270Constants.*;
/**
* Builds Query Reply structured fields in response to host Read Partition queries.
* Equivalent to the do_qr_* functions in sf.c.
*/
public class QueryReplyBuilder {
private static final Logger log = Logger.getLogger(QueryReplyBuilder.class.getName());
// Canned values from 3279-2 (matching sf.c)
private static final int SW_3279_2 = 0x09;
private static final int SH_3279_2 = 0x0c;
private static final int Xr_3279_2 = 0x000a02e5;
private static final int Yr_3279_2 = 0x0002006f;
private final ScreenBuffer screen;
// Supported query reply codes (must match what we send in summary)
private static final int[] SUPPORTED_QR = {
QR_SUMMARY, // 0x80 — summary must list itself
QR_USABLE_AREA, // 0x81
QR_ALPHA_PART, // 0x84
QR_CHARSETS, // 0x85
QR_COLOR, // 0x86
QR_HIGHLIGHTING, // 0x87
QR_REPLY_MODES, // 0x88
QR_DDM, // 0x95 - Distributed Data Management (file transfer)
QR_IMP_PART, // 0xa6
};
public QueryReplyBuilder(ScreenBuffer screen) {
this.screen = screen;
}
/**
* Build all query replies as a single AID_SF + structured field response.
*/
public byte[] buildAllQueryReplies(int maxCols, int maxRows, int bufferSize) {
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
// AID byte for structured field
out.write(AID_SF);
// Summary
appendQueryReply(out, QR_SUMMARY, buildSummary());
// Usable Area
appendQueryReply(out, QR_USABLE_AREA, buildUsableArea(maxCols, maxRows, bufferSize));
// Alpha Partitions
appendQueryReply(out, QR_ALPHA_PART, buildAlphaPartitions(maxRows));
// Character Sets
appendQueryReply(out, QR_CHARSETS, buildCharsets());
// Color
appendQueryReply(out, QR_COLOR, buildColor());
// Highlighting
appendQueryReply(out, QR_HIGHLIGHTING, buildHighlighting());
// Reply Modes
appendQueryReply(out, QR_REPLY_MODES, buildReplyModes());
// Distributed Data Management (DFT File Transfer)
appendQueryReply(out, QR_DDM, buildDdm(4096));
// Implicit Partition
appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows));
log.info("Built " + out.size() + " bytes of all query replies");
return out.toByteArray();
}
/**
* Build specific query replies in response to a Read Partition Query List (SF_RPQ_LIST).
* For any unsupported requested query code, emits a QR_NULL (0xFF) structured field
* matching x3270 sf.c behavior.
*/
public byte[] buildQueryReplies(byte[] requestedCodes, int maxCols, int maxRows, int bufferSize) {
if (requestedCodes == null || requestedCodes.length == 0) {
return buildAllQueryReplies(maxCols, maxRows, bufferSize);
}
ByteArrayOutputStream out = new ByteArrayOutputStream(512);
out.write(AID_SF);
for (byte codeByte : requestedCodes) {
int code = codeByte & 0xFF;
switch (code) {
case QR_SUMMARY:
appendQueryReply(out, QR_SUMMARY, buildSummary());
break;
case QR_USABLE_AREA:
appendQueryReply(out, QR_USABLE_AREA, buildUsableArea(maxCols, maxRows, bufferSize));
break;
case QR_ALPHA_PART:
appendQueryReply(out, QR_ALPHA_PART, buildAlphaPartitions(maxRows));
break;
case QR_CHARSETS:
appendQueryReply(out, QR_CHARSETS, buildCharsets());
break;
case QR_COLOR:
appendQueryReply(out, QR_COLOR, buildColor());
break;
case QR_HIGHLIGHTING:
appendQueryReply(out, QR_HIGHLIGHTING, buildHighlighting());
break;
case QR_REPLY_MODES:
appendQueryReply(out, QR_REPLY_MODES, buildReplyModes());
break;
case QR_DDM:
appendQueryReply(out, QR_DDM, buildDdm(4096));
break;
case QR_IMP_PART:
appendQueryReply(out, QR_IMP_PART, buildImplicitPartition(maxCols, maxRows));
break;
case QR_RPQNAMES:
appendQueryReply(out, QR_RPQNAMES, new byte[0]);
break;
default:
// Unsupported query reply code — emit QR_NULL
appendQueryReply(out, QR_NULL, new byte[0]);
break;
}
}
log.info("Built " + out.size() + " bytes of requested query replies for " + requestedCodes.length + " codes");
return out.toByteArray();
}
private void appendQueryReply(ByteArrayOutputStream out, int code, byte[] data) {
// Length includes the 2-byte length field + SFID_QREPLY + code + data
int len = 4 + data.length;
out.write((len >> 8) & 0xFF);
out.write(len & 0xFF);
out.write(SFID_QREPLY);
out.write(code);
out.write(data, 0, data.length);
}
private byte[] buildSummary() {
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (int code : SUPPORTED_QR) {
out.write(code);
}
return out.toByteArray();
}
private byte[] buildUsableArea(int maxCols, int maxRows, int bufferSize) {
ByteArrayOutputStream out = new ByteArrayOutputStream(19);
out.write(0x01); // 12/14-bit addressing
out.write(0x00); // no special character features
out.write((maxCols >> 8) & 0xFF); // usable width high
out.write(maxCols & 0xFF); // usable width low
out.write((maxRows >> 8) & 0xFF); // usable height high
out.write(maxRows & 0xFF); // usable height low
out.write(0x01); // units (mm)
// Xr (4 bytes) - canned from 3279-2
out.write((Xr_3279_2 >> 24) & 0xFF);
out.write((Xr_3279_2 >> 16) & 0xFF);
out.write((Xr_3279_2 >> 8) & 0xFF);
out.write(Xr_3279_2 & 0xFF);
// Yr (4 bytes) - canned from 3279-2
out.write((Yr_3279_2 >> 24) & 0xFF);
out.write((Yr_3279_2 >> 16) & 0xFF);
out.write((Yr_3279_2 >> 8) & 0xFF);
out.write(Yr_3279_2 & 0xFF);
out.write(SW_3279_2); // AW
out.write(SH_3279_2); // AH
int buf = maxCols * maxRows;
out.write((buf >> 8) & 0xFF); // buffer size high
out.write(buf & 0xFF); // buffer size low
return out.toByteArray();
}
private byte[] buildAlphaPartitions(int maxRows) {
int bufSize = screen.getMaxCols() * screen.getMaxRows();
ByteArrayOutputStream out = new ByteArrayOutputStream(4);
out.write(0x00); // max partitions (1 partition)
out.write((bufSize >> 8) & 0xFF); // total partition storage high
out.write(bufSize & 0xFF); // total partition storage low
out.write(0x00); // no special features
return out.toByteArray();
}
private byte[] buildCharsets() {
ByteArrayOutputStream out = new ByteArrayOutputStream(32);
out.write(0x82); // flags: GE, CGCSGID present
out.write(0x00); // more flags
out.write(SW_3279_2); // SDW - default char width
out.write(SH_3279_2); // SDH - default char height
out.write(0x00); // Load PS format types supported: none
out.write(0x00); // Load PS device type (high)
out.write(0x00); // Load PS device type (low)
out.write(0x00); // reserved
out.write(0x07); // DL = 7 bytes per descriptor (non-DBCS)
// Descriptor 1 (SET 0): default character set
out.write(0x00); // SET 0
out.write(0x10); // FLAGS: non-loadable, single-plane, single-byte, no compare
out.write(0x00); // LCID 0
out.write(0x02); // CGCSGID (4 bytes) = 0x02b90025 (CGEN|CSET)
out.write(0xb9);
out.write(0x00);
out.write(0x25);
// Descriptor 2 (SET 1): APL/GE character set
out.write(0x01); // SET 1
out.write(0x00); // FLAGS: non-loadable, single-plane, single-byte, no compare
out.write(0xf1); // LCID 0xf1
out.write(0x03); // CGCSGID: 3179-style APL2 = 0x03c30136
out.write(0xc3);
out.write(0x01);
out.write(0x36);
return out.toByteArray();
}
private byte[] buildColor() {
ByteArrayOutputStream out = new ByteArrayOutputStream(36);
int colorMax = 16;
out.write(0x00); // no options
out.write(colorMax); // number of colors
out.write(0x00); // default color pair: attribute
out.write(0xf0 + HOST_COLOR_GREEN); // default color: green
for (int i = 0xf1; i < 0xf1 + colorMax - 1; i++) {
out.write(i); // color attribute value
out.write(i); // maps to itself (color mode)
}
return out.toByteArray();
}
private byte[] buildHighlighting() {
ByteArrayOutputStream out = new ByteArrayOutputStream(11);
out.write(5); // 5 pairs
out.write(XAH_DEFAULT); out.write(XAH_NORMAL);
out.write(XAH_BLINK); out.write(XAH_BLINK);
out.write(XAH_REVERSE); out.write(XAH_REVERSE);
out.write(XAH_UNDERSCORE); out.write(XAH_UNDERSCORE);
out.write(XAH_INTENSIFY); out.write(XAH_INTENSIFY);
return out.toByteArray();
}
private byte[] buildReplyModes() {
return new byte[] { SF_SRM_FIELD, SF_SRM_XFIELD, SF_SRM_CHAR };
}
private byte[] buildDdm(int bufferSize) {
ByteArrayOutputStream out = new ByteArrayOutputStream(8);
out.write(0x00); // reserved
out.write(0x00); // reserved
out.write((bufferSize >> 8) & 0xFF); // inbound length limit (INLIM)
out.write(bufferSize & 0xFF);
out.write((bufferSize >> 8) & 0xFF); // outbound length limit (OUTLIM)
out.write(bufferSize & 0xFF);
out.write(0x01); // NSS = 01
out.write(0x01); // DDMSS = 01
return out.toByteArray();
}
private byte[] buildImplicitPartition(int maxCols, int maxRows) {
ByteArrayOutputStream out = new ByteArrayOutputStream(22);
// Implicit partition sizes, 2 self-defining parameters
// SDP 1: Default screen size
out.write(0x00); // flags
out.write(0x00); // reserved
out.write(0x0b); // SDP length
out.write(0x01); // SDP type: implicit partition sizes
out.write(0x00); // reserved
// Default
out.write((MODEL_2_COLS >> 8) & 0xFF);
out.write(MODEL_2_COLS & 0xFF);
out.write((MODEL_2_ROWS >> 8) & 0xFF);
out.write(MODEL_2_ROWS & 0xFF);
// Alternate
out.write((maxCols >> 8) & 0xFF);
out.write(maxCols & 0xFF);
out.write((maxRows >> 8) & 0xFF);
out.write(maxRows & 0xFF);
return out.toByteArray();
}
}