290 lines
11 KiB
Java
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();
|
|
}
|
|
}
|