enable UTF-8 for PQF queries by default
This commit is contained in:
parent
758ab83b3c
commit
6f53533c63
11 changed files with 99 additions and 58 deletions
|
@ -1,5 +1,5 @@
|
||||||
group = org.xbib
|
group = org.xbib
|
||||||
name = z3950
|
name = z3950
|
||||||
version = 5.0.3
|
version = 5.0.4
|
||||||
|
|
||||||
org.gradle.warning.mode = ALL
|
org.gradle.warning.mode = ALL
|
||||||
|
|
|
@ -45,8 +45,7 @@ public class ASN1Any {
|
||||||
* @throws ASN1Exception If the BER encoding is incorrect.
|
* @throws ASN1Exception If the BER encoding is incorrect.
|
||||||
* Never occurs for ASN1Any.
|
* Never occurs for ASN1Any.
|
||||||
*/
|
*/
|
||||||
public void berDecode(BEREncoding berEncoding, boolean checkTag)
|
public void berDecode(BEREncoding berEncoding, boolean checkTag) throws ASN1Exception {
|
||||||
throws ASN1Exception {
|
|
||||||
asn1anyBer = berEncoding;
|
asn1anyBer = berEncoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.xbib.asn1;
|
package org.xbib.asn1;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +28,8 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
|
|
||||||
private byte[] octets;
|
private byte[] octets;
|
||||||
|
|
||||||
|
private final Charset charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor for an OCTET STRING object. The tag is set to the
|
* Constructor for an OCTET STRING object. The tag is set to the
|
||||||
* default of UNIVERSAL 4, and its value to the given bytes.
|
* default of UNIVERSAL 4, and its value to the given bytes.
|
||||||
|
@ -34,6 +37,7 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
*/
|
*/
|
||||||
public ASN1OctetString(byte[] data) {
|
public ASN1OctetString(byte[] data) {
|
||||||
octets = new byte[data.length];
|
octets = new byte[data.length];
|
||||||
|
this.charset = StandardCharsets.ISO_8859_1;
|
||||||
System.arraycopy(data, 0, octets, 0, data.length);
|
System.arraycopy(data, 0, octets, 0, data.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +48,12 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
* @param str string
|
* @param str string
|
||||||
*/
|
*/
|
||||||
public ASN1OctetString(String str) {
|
public ASN1OctetString(String str) {
|
||||||
octets = str.getBytes(StandardCharsets.ISO_8859_1);
|
this(str, StandardCharsets.ISO_8859_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ASN1OctetString(String str, Charset charset) {
|
||||||
|
this.octets = str.getBytes(charset);
|
||||||
|
this.charset = charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -55,7 +64,8 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
* @throws ASN1Exception If the BER encoding is incorrect.
|
* @throws ASN1Exception If the BER encoding is incorrect.
|
||||||
*/
|
*/
|
||||||
public ASN1OctetString(BEREncoding ber, boolean checkTag) throws ASN1Exception {
|
public ASN1OctetString(BEREncoding ber, boolean checkTag) throws ASN1Exception {
|
||||||
super(ber, checkTag);
|
this.charset = StandardCharsets.ISO_8859_1;
|
||||||
|
berDecode(ber, checkTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,7 +90,7 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
for (int anEncoding : encoding) {
|
for (int anEncoding : encoding) {
|
||||||
buf.append((char) (anEncoding & 0x00ff));
|
buf.append((char) (anEncoding & 0x00ff));
|
||||||
}
|
}
|
||||||
octets = buf.toString().getBytes(StandardCharsets.ISO_8859_1);
|
octets = buf.toString().getBytes(charset);
|
||||||
} else {
|
} else {
|
||||||
throw new ASN1EncodingException("decode from constructed NOT IMPLEMENTED YET");
|
throw new ASN1EncodingException("decode from constructed NOT IMPLEMENTED YET");
|
||||||
}
|
}
|
||||||
|
@ -140,7 +150,7 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
* @return the object.
|
* @return the object.
|
||||||
*/
|
*/
|
||||||
public ASN1OctetString set(String str) {
|
public ASN1OctetString set(String str) {
|
||||||
octets = str.getBytes(StandardCharsets.ISO_8859_1);
|
octets = str.getBytes(charset);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +160,7 @@ public class ASN1OctetString extends ASN1Any {
|
||||||
* @return the OCTET STRING's current value.
|
* @return the OCTET STRING's current value.
|
||||||
*/
|
*/
|
||||||
public String get() {
|
public String get() {
|
||||||
return new String(octets, StandardCharsets.ISO_8859_1);
|
return new String(octets, charset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.xbib.z3950.client.jdk;
|
package org.xbib.z3950.client.jdk;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import org.xbib.asn1.io.InputStreamBERReader;
|
import org.xbib.asn1.io.InputStreamBERReader;
|
||||||
import org.xbib.asn1.io.OutputStreamBERWriter;
|
import org.xbib.asn1.io.OutputStreamBERWriter;
|
||||||
import org.xbib.z3950.api.TimeoutListener;
|
import org.xbib.z3950.api.TimeoutListener;
|
||||||
|
@ -164,7 +165,7 @@ public class JDKZClient implements Client, Closeable {
|
||||||
try {
|
try {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
SearchOperation search = new SearchOperation(berReader, berWriter, resultSetName, databases, host);
|
SearchOperation search = new SearchOperation(berReader, berWriter, resultSetName, databases, host);
|
||||||
search.executePQF(query);
|
search.executePQF(query, StandardCharsets.UTF_8);
|
||||||
if (!search.isSuccess()) {
|
if (!search.isSuccess()) {
|
||||||
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -14,7 +14,7 @@ class LVIZClientTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testLVI() {
|
void testLVI() {
|
||||||
String query = "@attr 1=4 linux";
|
String query = "@attr 1=4 Köln";
|
||||||
int from = 1;
|
int from = 1;
|
||||||
int size = 10;
|
int size = 10;
|
||||||
try (JDKZClient client = newZClient()) {
|
try (JDKZClient client = newZClient()) {
|
||||||
|
@ -32,8 +32,8 @@ class LVIZClientTest {
|
||||||
|
|
||||||
private JDKZClient newZClient() {
|
private JDKZClient newZClient() {
|
||||||
JDKZClient.Builder builder = JDKZClient.builder();
|
JDKZClient.Builder builder = JDKZClient.builder();
|
||||||
builder.setHost("sru.hbz-nrw.de");
|
builder.setHost("localhost");
|
||||||
builder.setPort(210);
|
builder.setPort(1210);
|
||||||
builder.setDatabases(Collections.singletonList("LVI"));
|
builder.setDatabases(Collections.singletonList("LVI"));
|
||||||
builder.setElementSetName(null);
|
builder.setElementSetName(null);
|
||||||
builder.setPreferredRecordSyntax("xml");
|
builder.setPreferredRecordSyntax("xml");
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.xbib.z3950.common.operations;
|
package org.xbib.z3950.common.operations;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
import org.xbib.asn1.ASN1Any;
|
import org.xbib.asn1.ASN1Any;
|
||||||
import org.xbib.asn1.ASN1Exception;
|
import org.xbib.asn1.ASN1Exception;
|
||||||
import org.xbib.asn1.BEREncoding;
|
import org.xbib.asn1.BEREncoding;
|
||||||
|
@ -53,6 +55,8 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public class AbstractOperation<IN extends ASN1Any, OUT extends ASN1Any> {
|
public class AbstractOperation<IN extends ASN1Any, OUT extends ASN1Any> {
|
||||||
|
|
||||||
|
private static final Logger logger = Logger.getLogger(AbstractOperation.class.getName());
|
||||||
|
|
||||||
protected final BERReader reader;
|
protected final BERReader reader;
|
||||||
|
|
||||||
protected final BERWriter writer;
|
protected final BERWriter writer;
|
||||||
|
@ -122,18 +126,24 @@ public class AbstractOperation<IN extends ASN1Any, OUT extends ASN1Any> {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
switch (ber.getTag()) {
|
switch (ber.getTag()) {
|
||||||
case 20:
|
case 20 -> {
|
||||||
return (IN) new InitializeRequest(ber, false);
|
return (IN) new InitializeRequest(ber, false);
|
||||||
case 21:
|
}
|
||||||
|
case 21 -> {
|
||||||
return (IN) new InitializeResponse(ber, false);
|
return (IN) new InitializeResponse(ber, false);
|
||||||
case 22:
|
}
|
||||||
|
case 22 -> {
|
||||||
return (IN) new SearchRequest(ber, false);
|
return (IN) new SearchRequest(ber, false);
|
||||||
case 23:
|
}
|
||||||
|
case 23 -> {
|
||||||
return (IN) new SearchResponse(ber, false);
|
return (IN) new SearchResponse(ber, false);
|
||||||
case 24:
|
}
|
||||||
|
case 24 -> {
|
||||||
return (IN) new PresentRequest(ber, false);
|
return (IN) new PresentRequest(ber, false);
|
||||||
case 25:
|
}
|
||||||
|
case 25 -> {
|
||||||
return (IN) new PresentResponse(ber, false);
|
return (IN) new PresentResponse(ber, false);
|
||||||
|
}
|
||||||
// 26 new DeleteResultSetRequest(ber, false);
|
// 26 new DeleteResultSetRequest(ber, false);
|
||||||
// 27 new DeleteResultSetResponse(ber, false);
|
// 27 new DeleteResultSetResponse(ber, false);
|
||||||
// 28 new AccessControlRequest(ber, false);
|
// 28 new AccessControlRequest(ber, false);
|
||||||
|
@ -143,23 +153,29 @@ public class AbstractOperation<IN extends ASN1Any, OUT extends ASN1Any> {
|
||||||
// 32 new TriggerResourceControlRequest(ber, false);
|
// 32 new TriggerResourceControlRequest(ber, false);
|
||||||
// 33 new ResourceReportRequest(ber, false);
|
// 33 new ResourceReportRequest(ber, false);
|
||||||
// 34 new ResourceReportResponse(ber, false);
|
// 34 new ResourceReportResponse(ber, false);
|
||||||
case 35:
|
case 35 -> {
|
||||||
return (IN) new ScanRequest(ber, false);
|
return (IN) new ScanRequest(ber, false);
|
||||||
case 36:
|
}
|
||||||
|
case 36 -> {
|
||||||
return (IN) new ScanResponse(ber, false);
|
return (IN) new ScanResponse(ber, false);
|
||||||
|
}
|
||||||
// 43 new SortRequest(ber, false);
|
// 43 new SortRequest(ber, false);
|
||||||
// 44 new SortResponse(ber, false);
|
// 44 new SortResponse(ber, false);
|
||||||
// 45 new Segment(ber, false);
|
// 45 new Segment(ber, false);
|
||||||
// 46 new ExtendedServicesRequest(ber, false);
|
// 46 new ExtendedServicesRequest(ber, false);
|
||||||
// 47 new ExtendedServicesResponse(ber, false);
|
// 47 new ExtendedServicesResponse(ber, false);
|
||||||
case 48:
|
case 48 -> {
|
||||||
return (IN) new Close(ber, false);
|
return (IN) new Close(ber, false);
|
||||||
}
|
}
|
||||||
|
default -> {
|
||||||
|
throw new ASN1Exception("bad BER encoding: " + ber.getTag() + " not matched");
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// class cast exception if Close is returned, ignore
|
// class cast exception if Close is returned, ignore
|
||||||
|
logger.log(Level.SEVERE, e.getMessage(), e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
throw new ASN1Exception("bad BER encoding: choice not matched");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,8 @@ public class InitOperation extends AbstractOperation<InitializeResponse, Initial
|
||||||
}
|
}
|
||||||
write(init);
|
write(init);
|
||||||
InitializeResponse initResp = read();
|
InitializeResponse initResp = read();
|
||||||
|
int targetVersion = 0;
|
||||||
|
if (initResp != null) {
|
||||||
if (initResp.implementationName != null) {
|
if (initResp.implementationName != null) {
|
||||||
targetInfo = initResp.implementationName.toString();
|
targetInfo = initResp.implementationName.toString();
|
||||||
if (initResp.implementationVersion != null) {
|
if (initResp.implementationVersion != null) {
|
||||||
|
@ -94,7 +96,6 @@ public class InitOperation extends AbstractOperation<InitializeResponse, Initial
|
||||||
} else {
|
} else {
|
||||||
targetInfo = "server";
|
targetInfo = "server";
|
||||||
}
|
}
|
||||||
int targetVersion = 0;
|
|
||||||
if (initResp.protocolVersion != null) {
|
if (initResp.protocolVersion != null) {
|
||||||
for (int n = 0; n < initResp.protocolVersion.value.get().length; n++) {
|
for (int n = 0; n < initResp.protocolVersion.value.get().length; n++) {
|
||||||
if (initResp.protocolVersion.value.get()[n]) {
|
if (initResp.protocolVersion.value.get()[n]) {
|
||||||
|
@ -114,10 +115,11 @@ public class InitOperation extends AbstractOperation<InitializeResponse, Initial
|
||||||
targetInfo += "\n" + initResp.otherInfo.toString();
|
targetInfo += "\n" + initResp.otherInfo.toString();
|
||||||
}
|
}
|
||||||
targetInfo = targetInfo.replaceAll("\"", "");
|
targetInfo = targetInfo.replaceAll("\"", "");
|
||||||
|
}
|
||||||
if (initListener != null) {
|
if (initListener != null) {
|
||||||
initListener.onInit(targetVersion, targetInfo);
|
initListener.onInit(targetVersion, targetInfo);
|
||||||
}
|
}
|
||||||
return !initResp.result.get();
|
return initResp != null && !initResp.result.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTargetInfo() {
|
public String getTargetInfo() {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.xbib.z3950.common.operations;
|
package org.xbib.z3950.common.operations;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import org.xbib.asn1.ASN1Boolean;
|
import org.xbib.asn1.ASN1Boolean;
|
||||||
import org.xbib.asn1.ASN1GeneralString;
|
import org.xbib.asn1.ASN1GeneralString;
|
||||||
import org.xbib.asn1.ASN1Integer;
|
import org.xbib.asn1.ASN1Integer;
|
||||||
|
@ -53,8 +54,8 @@ public class SearchOperation extends AbstractOperation<SearchResponse, SearchReq
|
||||||
this.status = false;
|
this.status = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean executePQF(String pqf) throws IOException {
|
public boolean executePQF(String pqf, Charset charset) throws IOException {
|
||||||
return execute(createRPNQueryFromPQF(pqf));
|
return execute(createRPNQueryFromPQF(pqf, charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean executeCQL(String cql) throws IOException {
|
public boolean executeCQL(String cql) throws IOException {
|
||||||
|
@ -125,8 +126,8 @@ public class SearchOperation extends AbstractOperation<SearchResponse, SearchReq
|
||||||
return generator.getQueryResult();
|
return generator.getQueryResult();
|
||||||
}
|
}
|
||||||
|
|
||||||
private RPNQuery createRPNQueryFromPQF(String query) {
|
private RPNQuery createRPNQueryFromPQF(String query, Charset charset) {
|
||||||
PQFRPNGenerator generator = new PQFRPNGenerator();
|
PQFRPNGenerator generator = new PQFRPNGenerator(charset);
|
||||||
PQFParser parser = new PQFParser(new StringReader(query));
|
PQFParser parser = new PQFParser(new StringReader(query));
|
||||||
parser.parse();
|
parser.parse();
|
||||||
parser.getResult().accept(generator);
|
parser.getResult().accept(generator);
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package org.xbib.z3950.common.pqf;
|
package org.xbib.z3950.common.pqf;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import org.xbib.asn1.ASN1Any;
|
import org.xbib.asn1.ASN1Any;
|
||||||
import org.xbib.asn1.ASN1Integer;
|
import org.xbib.asn1.ASN1Integer;
|
||||||
import org.xbib.asn1.ASN1Null;
|
import org.xbib.asn1.ASN1Null;
|
||||||
|
@ -27,12 +29,19 @@ public class PQFRPNGenerator implements Visitor {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(PQFRPNGenerator.class.getName());
|
private static final Logger logger = Logger.getLogger(PQFRPNGenerator.class.getName());
|
||||||
|
|
||||||
|
private final Charset charset;
|
||||||
|
|
||||||
private final Stack<ASN1Any> result;
|
private final Stack<ASN1Any> result;
|
||||||
|
|
||||||
private RPNQuery rpnQuery;
|
private RPNQuery rpnQuery;
|
||||||
|
|
||||||
public PQFRPNGenerator() {
|
public PQFRPNGenerator() {
|
||||||
|
this(StandardCharsets.ISO_8859_1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public PQFRPNGenerator(Charset charset) {
|
||||||
this.result = new Stack<>();
|
this.result = new Stack<>();
|
||||||
|
this.charset = charset;
|
||||||
}
|
}
|
||||||
|
|
||||||
public RPNQuery getResult() {
|
public RPNQuery getResult() {
|
||||||
|
@ -46,9 +55,6 @@ public class PQFRPNGenerator implements Visitor {
|
||||||
ASN1Any any = result.pop();
|
ASN1Any any = result.pop();
|
||||||
if (any instanceof RPNStructure) {
|
if (any instanceof RPNStructure) {
|
||||||
rpnQuery.rpn = (RPNStructure) any;
|
rpnQuery.rpn = (RPNStructure) any;
|
||||||
} else if (any instanceof ASN1OctetString) {
|
|
||||||
// TODO
|
|
||||||
logger.log(Level.INFO, "found ASN1OctetString = " + ((ASN1OctetString) any).get());
|
|
||||||
}
|
}
|
||||||
if (pqf.getAttrSet() == null) {
|
if (pqf.getAttrSet() == null) {
|
||||||
// Z39.50 BIB-1: urn:oid:1.2.840.10003.3.1
|
// Z39.50 BIB-1: urn:oid:1.2.840.10003.3.1
|
||||||
|
@ -131,7 +137,7 @@ public class PQFRPNGenerator implements Visitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(Term term) {
|
public void visit(Term term) {
|
||||||
result.push(new ASN1OctetString(term.getValue()));
|
result.push(new ASN1OctetString(term.getValue(), charset));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -2,9 +2,6 @@ package org.xbib.z3950.common.pqf;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class Term extends Node {
|
public class Term extends Node {
|
||||||
|
|
||||||
private final String value;
|
private final String value;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package org.xbib.z3950.common.pqf;
|
package org.xbib.z3950.common.pqf;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.xbib.z3950.common.v3.RPNQuery;
|
import org.xbib.z3950.common.v3.RPNQuery;
|
||||||
|
|
||||||
|
@ -38,8 +39,16 @@ class PQFTest {
|
||||||
createRPNQueryFromPQF(q).toString());
|
createRPNQueryFromPQF(q).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testRPN4() {
|
||||||
|
// test UTF-8
|
||||||
|
String q = "@attr 1=3 Jörg";
|
||||||
|
assertEquals("{attributeSetId 1.2.840.10003.3.1, rpn {op {attrTerm {attributes {{attributeType 1, attributeValue {numeric 3}}}, term {general \"J\\703\\666rg\"}}}}}",
|
||||||
|
createRPNQueryFromPQF(q).toString());
|
||||||
|
}
|
||||||
|
|
||||||
private RPNQuery createRPNQueryFromPQF(String query) {
|
private RPNQuery createRPNQueryFromPQF(String query) {
|
||||||
PQFRPNGenerator generator = new PQFRPNGenerator();
|
PQFRPNGenerator generator = new PQFRPNGenerator(StandardCharsets.UTF_8);
|
||||||
org.xbib.z3950.common.pqf.PQFParser parser = new org.xbib.z3950.common.pqf.PQFParser(new StringReader(query));
|
org.xbib.z3950.common.pqf.PQFParser parser = new org.xbib.z3950.common.pqf.PQFParser(new StringReader(query));
|
||||||
parser.parse();
|
parser.parse();
|
||||||
parser.getResult().accept(generator);
|
parser.getResult().accept(generator);
|
||||||
|
|
Loading…
Reference in a new issue