fix CQL -> RPN generator, make implementation name configurable
This commit is contained in:
parent
b17c13def4
commit
f27a11af50
4 changed files with 111 additions and 137 deletions
|
@ -1,5 +1,5 @@
|
|||
group = org.xbib
|
||||
name = z3950
|
||||
version = 5.0.4
|
||||
version = 5.0.5
|
||||
|
||||
org.gradle.warning.mode = ALL
|
||||
|
|
|
@ -39,33 +39,7 @@ public class JDKZClient implements Client, Closeable {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(JDKZClient.class.getName());
|
||||
|
||||
private final String host;
|
||||
|
||||
private final int port;
|
||||
|
||||
private final String user;
|
||||
|
||||
private final String pass;
|
||||
|
||||
private final long timeout;
|
||||
|
||||
private final String preferredRecordSyntax;
|
||||
|
||||
private final String resultSetName;
|
||||
|
||||
private final String elementSetName;
|
||||
|
||||
private final String encoding;
|
||||
|
||||
private final String format;
|
||||
|
||||
private final String type;
|
||||
|
||||
private final List<String> databases;
|
||||
|
||||
private final Integer preferredMessageSize;
|
||||
|
||||
private final InitListener initListener;
|
||||
private final Builder builder;
|
||||
|
||||
private final Lock lock;
|
||||
|
||||
|
@ -75,34 +49,8 @@ public class JDKZClient implements Client, Closeable {
|
|||
|
||||
private OutputStreamBERWriter berWriter;
|
||||
|
||||
private JDKZClient(String host,
|
||||
int port,
|
||||
String user,
|
||||
String pass,
|
||||
long timeout,
|
||||
String preferredRecordSyntax,
|
||||
String resultSetName,
|
||||
String elementSetName,
|
||||
String encoding,
|
||||
String format,
|
||||
String type,
|
||||
List<String> databases,
|
||||
Integer preferredMessageSize,
|
||||
InitListener initListener) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.user = user;
|
||||
this.pass = pass;
|
||||
this.timeout = timeout;
|
||||
this.preferredRecordSyntax = preferredRecordSyntax;
|
||||
this.resultSetName = resultSetName;
|
||||
this.elementSetName = elementSetName;
|
||||
this.encoding = encoding;
|
||||
this.format = format;
|
||||
this.type = type;
|
||||
this.databases = databases;
|
||||
this.preferredMessageSize = preferredMessageSize;
|
||||
this.initListener = initListener;
|
||||
private JDKZClient(Builder builder) {
|
||||
this.builder = builder;
|
||||
this.lock = new ReentrantLock();
|
||||
}
|
||||
|
||||
|
@ -117,7 +65,8 @@ public class JDKZClient implements Client, Closeable {
|
|||
ensureConnected();
|
||||
try {
|
||||
lock.lock();
|
||||
SearchOperation searchOperation = new SearchOperation(berReader, berWriter, resultSetName, databases, host);
|
||||
SearchOperation searchOperation = new SearchOperation(berReader, berWriter,
|
||||
builder.resultSetName, builder.databases, builder.host);
|
||||
boolean success = searchOperation.executeCQL(query);
|
||||
if (!success) {
|
||||
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
||||
|
@ -130,7 +79,7 @@ public class JDKZClient implements Client, Closeable {
|
|||
}
|
||||
if (searchOperation.getCount() > 0) {
|
||||
PresentOperation present = new PresentOperation(berReader, berWriter,
|
||||
resultSetName, elementSetName, preferredRecordSyntax);
|
||||
builder.resultSetName, builder.elementSetName, builder.preferredRecordSyntax);
|
||||
if (offset < 1) {
|
||||
// Z39.50 present bails out when offset = 0
|
||||
offset = 1;
|
||||
|
@ -164,7 +113,8 @@ public class JDKZClient implements Client, Closeable {
|
|||
ensureConnected();
|
||||
try {
|
||||
lock.lock();
|
||||
SearchOperation search = new SearchOperation(berReader, berWriter, resultSetName, databases, host);
|
||||
SearchOperation search = new SearchOperation(berReader, berWriter,
|
||||
builder.resultSetName, builder.databases, builder.host);
|
||||
search.executePQF(query, StandardCharsets.UTF_8);
|
||||
if (!search.isSuccess()) {
|
||||
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
||||
|
@ -178,7 +128,7 @@ public class JDKZClient implements Client, Closeable {
|
|||
if (search.getCount() > 0) {
|
||||
logger.log(Level.FINE, "search returned " + search.getCount());
|
||||
PresentOperation present = new PresentOperation(berReader, berWriter,
|
||||
resultSetName, elementSetName, preferredRecordSyntax);
|
||||
builder.resultSetName, builder.elementSetName, builder.preferredRecordSyntax);
|
||||
if (offset < 1) {
|
||||
// Z39.50 bails out when offset = 0
|
||||
offset = 1;
|
||||
|
@ -211,7 +161,7 @@ public class JDKZClient implements Client, Closeable {
|
|||
ensureConnected();
|
||||
try {
|
||||
lock.lock();
|
||||
ScanOperation scanOperation = new ScanOperation(berReader, berWriter, databases);
|
||||
ScanOperation scanOperation = new ScanOperation(berReader, berWriter, builder.databases);
|
||||
scanOperation.executePQF(nTerms, step, position, query, scanListener);
|
||||
} catch (SocketTimeoutException e) {
|
||||
if (timeoutListener != null) {
|
||||
|
@ -224,77 +174,78 @@ public class JDKZClient implements Client, Closeable {
|
|||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return host;
|
||||
return builder.host;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return port;
|
||||
return builder.port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUser() {
|
||||
return user;
|
||||
return builder.user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPass() {
|
||||
return pass;
|
||||
return builder.pass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimeout() {
|
||||
return timeout;
|
||||
return builder.timeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferredRecordSyntax() {
|
||||
return preferredRecordSyntax;
|
||||
return builder.preferredRecordSyntax;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResultSetName() {
|
||||
return resultSetName;
|
||||
return builder.resultSetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElementSetName() {
|
||||
return elementSetName;
|
||||
return builder.elementSetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
return builder.encoding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return format;
|
||||
return builder.format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
return builder.type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getDatabases() {
|
||||
return databases;
|
||||
return builder.databases;
|
||||
}
|
||||
|
||||
public void connect() throws IOException {
|
||||
try {
|
||||
lock.lock();
|
||||
Socket socket = new Socket();
|
||||
socket.connect(new InetSocketAddress(host, port), (int) timeout); // in milliseconds
|
||||
socket.setSoTimeout((int) timeout); // timeout in milliseconds
|
||||
socket.connect(new InetSocketAddress(builder.host, builder.port), (int) builder.timeout); // in milliseconds
|
||||
socket.setSoTimeout((int) builder.timeout); // timeout in milliseconds
|
||||
this.socket = socket;
|
||||
InputStream src = new BufferedInputStream(socket.getInputStream());
|
||||
OutputStream dest = new BufferedOutputStream(socket.getOutputStream());
|
||||
this.berReader = new InputStreamBERReader(src);
|
||||
this.berWriter = new OutputStreamBERWriter(dest);
|
||||
InitOperation initOperation = new InitOperation(berReader, berWriter, user, pass);
|
||||
if (initOperation.execute(preferredMessageSize, initListener)) {
|
||||
InitOperation initOperation = new InitOperation(berReader, berWriter, builder.user, builder.pass);
|
||||
if (initOperation.execute(builder.preferredMessageSize,
|
||||
builder.implementationName, builder.implementationVersion, builder.initListener)) {
|
||||
throw new IOException("could not initiate connection");
|
||||
}
|
||||
logger.log(Level.INFO, initOperation.getTargetInfo());
|
||||
|
@ -410,6 +361,10 @@ public class JDKZClient implements Client, Closeable {
|
|||
|
||||
private Integer preferredMessageSize;
|
||||
|
||||
private String implementationName;
|
||||
|
||||
private String implementationVersion;
|
||||
|
||||
private InitListener initListener;
|
||||
|
||||
private Builder() {
|
||||
|
@ -421,6 +376,8 @@ public class JDKZClient implements Client, Closeable {
|
|||
this.type = "Bibliographic";
|
||||
this.databases = Collections.singletonList("");
|
||||
this.preferredMessageSize = 10 * 1024 * 1024;
|
||||
this.implementationName = "Java Z Client";
|
||||
this.implementationVersion = "1.00";
|
||||
}
|
||||
|
||||
public Builder setHost(String host) {
|
||||
|
@ -491,22 +448,23 @@ public class JDKZClient implements Client, Closeable {
|
|||
return this;
|
||||
}
|
||||
|
||||
public Builder setImplementationName(String implementationName) {
|
||||
this.implementationName = implementationName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setImplementationVersion(String implementationVersion) {
|
||||
this.implementationVersion = implementationVersion;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setInitListener(InitListener initListener) {
|
||||
this.initListener = initListener;
|
||||
return this;
|
||||
}
|
||||
|
||||
public JDKZClient build() {
|
||||
return new JDKZClient(host, port, user, pass, timeout,
|
||||
preferredRecordSyntax,
|
||||
resultSetName,
|
||||
elementSetName,
|
||||
encoding,
|
||||
format,
|
||||
type,
|
||||
databases,
|
||||
preferredMessageSize,
|
||||
initListener);
|
||||
return new JDKZClient(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,9 +49,8 @@ public final class CQLRPNGenerator implements Visitor {
|
|||
/**
|
||||
* Context map.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private final Map<String, ResourceBundle> contexts = new HashMap<>() {
|
||||
private static final long serialVersionUID = 8199395368653216950L;
|
||||
|
||||
{
|
||||
put("default", ResourceBundle.getBundle("org.xbib.z3950.common.cql.default"));
|
||||
put("cql", ResourceBundle.getBundle("org.xbib.z3950.common.cql.cql"));
|
||||
|
@ -156,17 +155,12 @@ public final class CQLRPNGenerator implements Visitor {
|
|||
rpn.c_rpnRpnOp.s_op = new Operator();
|
||||
BooleanOperator op = node.getBooleanGroup().getOperator();
|
||||
switch (op) {
|
||||
case AND:
|
||||
rpn.c_rpnRpnOp.s_op.andOp = new ASN1Null();
|
||||
break;
|
||||
case OR:
|
||||
rpn.c_rpnRpnOp.s_op.orOp = new ASN1Null();
|
||||
break;
|
||||
case NOT:
|
||||
rpn.c_rpnRpnOp.s_op.andNotOp = new ASN1Null();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case AND -> rpn.c_rpnRpnOp.s_op.andOp = new ASN1Null();
|
||||
case OR -> rpn.c_rpnRpnOp.s_op.orOp = new ASN1Null();
|
||||
case NOT -> rpn.c_rpnRpnOp.s_op.andNotOp = new ASN1Null();
|
||||
default -> {
|
||||
throw new UnsupportedOperationException("op: " + op);
|
||||
}
|
||||
}
|
||||
rpn.c_rpnRpnOp.s_rpn1 = (RPNStructure) result.pop();
|
||||
rpn.c_rpnRpnOp.s_rpn2 = (RPNStructure) result.pop();
|
||||
|
@ -218,36 +212,28 @@ public final class CQLRPNGenerator implements Visitor {
|
|||
node.getModifierList().accept(this);
|
||||
}
|
||||
int attributeType = 2;
|
||||
int attributeValue = 3;
|
||||
int attributeValue = 3; // EQUALS, 2=3
|
||||
switch (node.getComparitor()) {
|
||||
case LESS: // 2=1
|
||||
case LESS -> // 2=1
|
||||
attributeValue = 1;
|
||||
break;
|
||||
case LESS_EQUALS: // 2=2
|
||||
case LESS_EQUALS -> // 2=2
|
||||
attributeValue = 2;
|
||||
break;
|
||||
case EQUALS: // 2=3
|
||||
attributeValue = 3;
|
||||
break;
|
||||
case GREATER_EQUALS: // 2=4
|
||||
case GREATER_EQUALS -> // 2=4
|
||||
attributeValue = 4;
|
||||
break;
|
||||
case GREATER: // 2=5
|
||||
case GREATER -> // 2=5
|
||||
attributeValue = 5;
|
||||
break;
|
||||
case NOT_EQUALS: // 2=6
|
||||
case NOT_EQUALS -> // 2=6
|
||||
attributeValue = 6;
|
||||
break;
|
||||
case ALL: // 4=6
|
||||
case ALL -> { // 4=6
|
||||
attributeType = 4;
|
||||
attributeValue = 6;
|
||||
break;
|
||||
case ANY: // 4=105
|
||||
}
|
||||
case ANY -> { // 4=104
|
||||
attributeType = 4;
|
||||
attributeValue = 104;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
if (attributeValue != 3) {
|
||||
AttributeElement ae = new AttributeElement();
|
||||
|
@ -298,13 +284,11 @@ public final class CQLRPNGenerator implements Visitor {
|
|||
if (context == null) {
|
||||
context = "default"; // default context
|
||||
}
|
||||
|
||||
int attributeType = 1; // use attribute set: bib-1 = 1
|
||||
int attributeValue = getUseAttr(context, index.getName());
|
||||
AttributeElement ae = new AttributeElement();
|
||||
ae.attributeType = new ASN1Integer(attributeType);
|
||||
ae.attributeValue = new AttributeElementAttributeValue();
|
||||
ae.attributeValue.numeric = new ASN1Integer(attributeValue);
|
||||
attributeElements.push(ae);
|
||||
|
||||
push(attributeElements, createAttributeElement(attributeType, attributeValue));
|
||||
}
|
||||
|
||||
private int getUseAttr(String context, String attrName) {
|
||||
|
@ -316,18 +300,31 @@ public final class CQLRPNGenerator implements Visitor {
|
|||
}
|
||||
|
||||
private ASN1OctetString transformTerm(Term term) {
|
||||
|
||||
int firstAttributeValue = 0;
|
||||
if (!attributeElements.isEmpty()) {
|
||||
AttributeElement attributeElement = attributeElements.peek();
|
||||
if (attributeElement.attributeType.get() == 1) {
|
||||
firstAttributeValue = attributeElement.attributeValue.numeric.get();
|
||||
}
|
||||
}
|
||||
|
||||
String v = term.getValue();
|
||||
// let's derive attributes from the search term syntax
|
||||
|
||||
// relation attribute = 2
|
||||
int attributeType = 2;
|
||||
int attributeValue = 3; // equal = 3
|
||||
int attributeValue = 3; // equal 2=3
|
||||
push(attributeElements, createAttributeElement(attributeType, attributeValue));
|
||||
|
||||
// position attribute = 3
|
||||
//attributeType = 3;
|
||||
// attributeValue = 3; // any position = 3
|
||||
//push(attributeElements, createAttributeElement(attributeType, attributeValue));
|
||||
attributeType = 3;
|
||||
attributeValue = 3; // any position = 3
|
||||
if (v.startsWith("^")) {
|
||||
attributeValue = 1; // first posiiton
|
||||
v = v.substring(1);
|
||||
}
|
||||
push(attributeElements, createAttributeElement(attributeType, attributeValue));
|
||||
|
||||
// structure attribute = 4
|
||||
attributeType = 4;
|
||||
|
@ -336,6 +333,12 @@ public final class CQLRPNGenerator implements Visitor {
|
|||
attributeValue = 1; // phrase
|
||||
v = v.substring(1, v.length()-1);
|
||||
}
|
||||
if (firstAttributeValue == 31) {
|
||||
attributeValue = 5; // date (normalized) = 5
|
||||
}
|
||||
if (firstAttributeValue == 1016) {
|
||||
attributeValue = 6; // word list
|
||||
}
|
||||
push(attributeElements, createAttributeElement(attributeType, attributeValue));
|
||||
|
||||
// truncation attribute = 5
|
||||
|
@ -354,6 +357,16 @@ public final class CQLRPNGenerator implements Visitor {
|
|||
v = v.substring(1);
|
||||
}
|
||||
push(attributeElements, createAttributeElement(attributeType, attributeValue));
|
||||
|
||||
// completeness attribute = 6
|
||||
attributeType = 6;
|
||||
attributeValue = 1; // not complete = 1
|
||||
if (v.startsWith("^") && v.endsWith("$")) {
|
||||
attributeValue = 3; // complete = 3
|
||||
v = v.substring(1, v.length() - 1);
|
||||
}
|
||||
push(attributeElements, createAttributeElement(attributeType, attributeValue));
|
||||
|
||||
return new ASN1OctetString(v);
|
||||
}
|
||||
|
||||
|
|
|
@ -37,7 +37,10 @@ public class InitOperation extends AbstractOperation<InitializeResponse, Initial
|
|||
this.pass = pass;
|
||||
}
|
||||
|
||||
public boolean execute(Integer preferredMessageSize, InitListener initListener) throws IOException {
|
||||
public boolean execute(Integer preferredMessageSize,
|
||||
String implementationName,
|
||||
String implementationVersion,
|
||||
InitListener initListener) throws IOException {
|
||||
InitializeRequest init = new InitializeRequest();
|
||||
boolean[] version = new boolean[3];
|
||||
version[0] = true; // any version, should alwasy be true
|
||||
|
@ -68,9 +71,9 @@ public class InitOperation extends AbstractOperation<InitializeResponse, Initial
|
|||
init.implementationId = new InternationalString();
|
||||
init.implementationId.value = new ASN1GeneralString("1");
|
||||
init.implementationName = new InternationalString();
|
||||
init.implementationName.value = new ASN1GeneralString("Java ZClient");
|
||||
init.implementationName.value = new ASN1GeneralString(implementationName);
|
||||
init.implementationVersion = new InternationalString();
|
||||
init.implementationVersion.value = new ASN1GeneralString("1.00");
|
||||
init.implementationVersion.value = new ASN1GeneralString(implementationVersion);
|
||||
if (user != null) {
|
||||
init.idAuthentication = new IdAuthentication();
|
||||
init.idAuthentication.idPass = new IdAuthenticationIdPass();
|
||||
|
|
Loading…
Reference in a new issue