From e03dac53ef0d686e4a8ea44a3583d3c6e5444b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Tue, 28 Mar 2023 15:37:57 +0200 Subject: [PATCH] add sort operation --- gradle.properties | 2 +- .../org/xbib/z3950/client/api/Client.java | 15 +- .../org/xbib/z3950/client/jdk/JDKZClient.java | 73 +++++-- .../xbib/z3950/client/jdk/test/COPACTest.java | 2 +- .../z3950/client/jdk/test/GBVZClientTest.java | 4 +- .../client/jdk/test/LIBRISClientTest.java | 4 +- .../z3950/client/jdk/test/LVIZClientTest.java | 31 +-- .../z3950/client/jdk/test/SWBClientTest.java | 5 +- .../xbib/z3950/client/netty/NettyZClient.java | 12 +- .../org/xbib/z3950/common/Diagnostics.java | 6 +- .../common/operations/AbstractOperation.java | 16 +- .../common/operations/PresentOperation.java | 3 +- .../common/operations/ScanOperation.java | 6 +- .../common/operations/SortOperation.java | 197 ++++++++++++++++++ .../z3950/common/v3/CompSpecDbSpecific.java | 25 +-- .../org/xbib/z3950/common/v3/SortElement.java | 38 ++-- .../v3/SortElementDatabaseSpecific.java | 29 +-- .../org/xbib/z3950/common/v3/SortKey.java | 8 +- .../common/v3/SortKeySortAttributes.java | 8 +- .../org/xbib/z3950/common/v3/SortKeySpec.java | 31 ++- .../v3/SortKeySpecMissingValueAction.java | 5 +- .../org/xbib/z3950/common/v3/SortRequest.java | 16 +- .../xbib/z3950/common/v3/SortResponse.java | 31 +-- .../xbib/z3950/common/v3/Specification.java | 10 +- .../common/v3/SpecificationElementSpec.java | 3 + 25 files changed, 432 insertions(+), 148 deletions(-) create mode 100644 z3950-common/src/main/java/org/xbib/z3950/common/operations/SortOperation.java diff --git a/gradle.properties b/gradle.properties index 0a9a09a..8248dd7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = z3950 -version = 5.0.5 +version = 5.1.0 org.gradle.warning.mode = ALL diff --git a/z3950-client-api/src/main/java/org/xbib/z3950/client/api/Client.java b/z3950-client-api/src/main/java/org/xbib/z3950/client/api/Client.java index b028e13..91b8cc7 100644 --- a/z3950-client-api/src/main/java/org/xbib/z3950/client/api/Client.java +++ b/z3950-client-api/src/main/java/org/xbib/z3950/client/api/Client.java @@ -4,6 +4,7 @@ import org.xbib.z3950.api.RecordListener; import org.xbib.z3950.api.ScanListener; import org.xbib.z3950.api.SearchListener; import org.xbib.z3950.api.TimeoutListener; +import org.xbib.z3950.common.operations.SortOperation; import java.io.Closeable; import java.io.IOException; @@ -11,12 +12,18 @@ import java.util.List; public interface Client extends Closeable { - int searchCQL(String query, int offset, int length, + int searchCQL(String query, + int offset, + int length, + List parameters, SearchListener searchListener, RecordListener recordListener, TimeoutListener timeoutListener) throws IOException; - int searchPQF(String query, int offset, int length, + int searchPQF(String query, + int offset, + int length, + List parameters, SearchListener searchListener, RecordListener recordListener, TimeoutListener timeoutListener) throws IOException; @@ -25,6 +32,10 @@ public interface Client extends Closeable { ScanListener scanListener, TimeoutListener timeoutListener) throws IOException; + void sort(String referenceId, + List parameters, + TimeoutListener timeoutListener) throws IOException; + String getHost(); int getPort(); diff --git a/z3950-client-jdk/src/main/java/org/xbib/z3950/client/jdk/JDKZClient.java b/z3950-client-jdk/src/main/java/org/xbib/z3950/client/jdk/JDKZClient.java index 4d51e55..962ac1a 100644 --- a/z3950-client-jdk/src/main/java/org/xbib/z3950/client/jdk/JDKZClient.java +++ b/z3950-client-jdk/src/main/java/org/xbib/z3950/client/jdk/JDKZClient.java @@ -14,6 +14,8 @@ import org.xbib.z3950.api.InitListener; import org.xbib.z3950.api.RecordListener; import org.xbib.z3950.api.ScanListener; import org.xbib.z3950.api.SearchListener; +import org.xbib.z3950.common.operations.SortOperation; + import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; @@ -55,7 +57,10 @@ public class JDKZClient implements Client, Closeable { } @Override - public int searchCQL(String query, int offset, int length, + public int searchCQL(String query, + int offset, + int length, + List sortParameters, SearchListener searchListener, RecordListener recordListener, TimeoutListener timeoutListener) throws IOException { @@ -78,8 +83,19 @@ public class JDKZClient implements Client, Closeable { }; } if (searchOperation.getCount() > 0) { + logger.log(Level.FINE, "search returned " + searchOperation.getCount()); + String resultSetName = builder.resultSetName; + if (sortParameters != null && !sortParameters.isEmpty()) { + SortOperation sort = new SortOperation(berReader, berWriter); + boolean sortSuccess = sort.execute("sort-ref", resultSetName, resultSetName + "-sort", + sortParameters); + logger.log(Level.FINE, "sort returned " + sortSuccess); + if (sortSuccess) { + resultSetName = resultSetName + "-sort"; + } + } PresentOperation present = new PresentOperation(berReader, berWriter, - builder.resultSetName, builder.elementSetName, builder.preferredRecordSyntax); + resultSetName, builder.elementSetName, builder.preferredRecordSyntax); if (offset < 1) { // Z39.50 present bails out when offset = 0 offset = 1; @@ -103,7 +119,10 @@ public class JDKZClient implements Client, Closeable { } @Override - public int searchPQF(String query, int offset, int length, + public int searchPQF(String query, + int offset, + int length, + List sortParameters, SearchListener searchListener, RecordListener recordListener, TimeoutListener timeoutListener) throws IOException { @@ -113,10 +132,10 @@ public class JDKZClient implements Client, Closeable { ensureConnected(); try { lock.lock(); - SearchOperation search = new SearchOperation(berReader, berWriter, + SearchOperation searchOperation = new SearchOperation(berReader, berWriter, builder.resultSetName, builder.databases, builder.host); - search.executePQF(query, StandardCharsets.UTF_8); - if (!search.isSuccess()) { + searchOperation.executePQF(query, StandardCharsets.UTF_8); + if (!searchOperation.isSuccess()) { logger.log(Level.WARNING, MessageFormat.format("search was not a success [{0}]", query)); } else { if (searchListener == null) { @@ -125,22 +144,32 @@ public class JDKZClient implements Client, Closeable { elapsedMillis, total, returned, query)); }; } - if (search.getCount() > 0) { - logger.log(Level.FINE, "search returned " + search.getCount()); + if (searchOperation.getCount() > 0) { + logger.log(Level.FINE, "search returned " + searchOperation.getCount()); + String resultSetName = builder.resultSetName; + if (sortParameters != null && !sortParameters.isEmpty()) { + SortOperation sort = new SortOperation(berReader, berWriter); + boolean sortSuccess = sort.execute("sort-ref", resultSetName, resultSetName + "-sort", + sortParameters); + logger.log(Level.FINE, "sort returned " + sortSuccess); + if (sortSuccess) { + resultSetName = resultSetName + "-sort"; + } + } PresentOperation present = new PresentOperation(berReader, berWriter, - builder.resultSetName, builder.elementSetName, builder.preferredRecordSyntax); + resultSetName, builder.elementSetName, builder.preferredRecordSyntax); if (offset < 1) { // Z39.50 bails out when offset = 0 offset = 1; } - if (length > search.getCount()) { + if (length > searchOperation.getCount()) { // avoid condition 13 "Present request out-of-range" - length = search.getCount(); + length = searchOperation.getCount(); } - present.execute(offset, length, search.getCount(), searchListener, recordListener); + present.execute(offset, length, searchOperation.getCount(), searchListener, recordListener); } } - return search.getCount(); + return searchOperation.getCount(); } catch (SocketTimeoutException e) { if (timeoutListener != null) { timeoutListener.onTimeout(); @@ -172,6 +201,24 @@ public class JDKZClient implements Client, Closeable { } } + @Override + public void sort(String referenceId, + List parameters, + TimeoutListener timeoutListener) throws IOException { + ensureConnected(); + try { + lock.lock(); + SortOperation sortOperation = new SortOperation(berReader, berWriter); + sortOperation.execute(referenceId, getResultSetName(), getResultSetName() + "-sort", parameters); + } catch (SocketTimeoutException e) { + if (timeoutListener != null) { + timeoutListener.onTimeout(); + } + } finally { + lock.unlock(); + } + } + @Override public String getHost() { return builder.host; diff --git a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/COPACTest.java b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/COPACTest.java index c368044..a6f0f80 100644 --- a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/COPACTest.java +++ b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/COPACTest.java @@ -30,7 +30,7 @@ class COPACTest { .setDatabases(Collections.singletonList(database)) .setPreferredRecordSyntax(preferredRecordSyntax) .build(); - client.searchPQF(query, from, length, + client.searchPQF(query, from, length, null, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, "total records = " + total), record -> logger.log(Level.INFO, "found record " + record), // MODS () -> logger.log(Level.WARNING, "timeout")); diff --git a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/GBVZClientTest.java b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/GBVZClientTest.java index 6001912..30fb335 100644 --- a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/GBVZClientTest.java +++ b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/GBVZClientTest.java @@ -28,7 +28,7 @@ class GBVZClientTest { int size = 10; try (JDKZClient client = newZClient("GBV")) { logger.log(Level.INFO, "executing PQF " + query); - int count = client.searchPQF(query, from, size, + int count = client.searchPQF(query, from, size, null, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, "total results = " + total + " millis = " + elapsedMillis), record -> logger.log(Level.INFO, "record = " + record.toString(StandardCharsets.UTF_8)), @@ -48,7 +48,7 @@ class GBVZClientTest { int size = 2; try (JDKZClient client = newZClient(serviceName)) { logger.log(Level.INFO, "executing CQL " + query); - int count = client.searchCQL(query, from, size, + int count = client.searchCQL(query, from, size, null, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, serviceName + " total results = " + total), record -> logger.log(Level.INFO, "record = " + record), diff --git a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LIBRISClientTest.java b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LIBRISClientTest.java index 1482a0b..4236ce0 100644 --- a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LIBRISClientTest.java +++ b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LIBRISClientTest.java @@ -23,7 +23,7 @@ class LIBRISClientTest { int size = 10; try (JDKZClient client = newZClient(serviceName)) { logger.log(Level.INFO, "executing CQL " + serviceName); - int count = client.searchCQL(query, from, size, + int count = client.searchCQL(query, from, size, null, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, serviceName + " total results = " + total), record -> logger.log(Level.INFO, "record = " + record), @@ -42,7 +42,7 @@ class LIBRISClientTest { int size = 10; try (JDKZClient client = newZClient(serviceName)) { logger.log(Level.INFO, "executing PQF " + serviceName); - int count = client.searchPQF(query, from, size, + int count = client.searchPQF(query, from, size, null, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, serviceName + " status = " + status + " total results = " + total), record -> logger.log(Level.INFO, "record = " + record.toString(Charset.forName(client.getEncoding()))), diff --git a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LVIZClientTest.java b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LVIZClientTest.java index 8385dda..7d3e8f6 100644 --- a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LVIZClientTest.java +++ b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/LVIZClientTest.java @@ -2,9 +2,11 @@ package org.xbib.z3950.client.jdk.test; import org.junit.jupiter.api.Test; import org.xbib.z3950.client.jdk.JDKZClient; +import org.xbib.z3950.common.operations.SortOperation; import java.nio.charset.StandardCharsets; import java.util.Collections; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -14,12 +16,14 @@ class LVIZClientTest { @Test void testLVI() { - String query = "@attr 1=4 Köln"; - int from = 1; + String query = "@attr 1=4 @attr 4=6 \"Köln strafrecht\""; + int offset = 1; int size = 10; try (JDKZClient client = newZClient()) { + List sort = List.of(SortOperation.SortParameter.of("title") + .setAscending(true)); logger.log(Level.INFO, "executing PQF " + query); - int count = client.searchPQF(query, from, size, + int count = client.searchPQF(query, offset, size, sort, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, "total results = " + total + " millis = " + elapsedMillis), record -> logger.log(Level.INFO, "record = " + record.toString(StandardCharsets.UTF_8)), @@ -31,16 +35,17 @@ class LVIZClientTest { } private JDKZClient newZClient() { - JDKZClient.Builder builder = JDKZClient.builder(); - builder.setHost("localhost"); - builder.setPort(1210); - builder.setDatabases(Collections.singletonList("LVI")); - builder.setElementSetName(null); - builder.setPreferredRecordSyntax("xml"); - builder.setResultSetName("default"); - builder.setEncoding("utf-8"); - builder.setFormat("Marc21"); - builder.setType("Bibliographic"); + JDKZClient.Builder builder = JDKZClient.builder() + .setHost("sru.hbz-nrw.de") + .setPort(210) + .setTimeout(30000L) + .setDatabases(Collections.singletonList("LVI")) + .setElementSetName(null) + .setPreferredRecordSyntax("xml") + .setResultSetName("default") + .setEncoding("UTF-8") + .setFormat("Marc21") + .setType("Bibliographic"); return builder.build(); } } diff --git a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/SWBClientTest.java b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/SWBClientTest.java index 1dc3fa9..164fe0e 100644 --- a/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/SWBClientTest.java +++ b/z3950-client-jdk/src/test/java/org/xbib/z3950/client/jdk/test/SWBClientTest.java @@ -3,7 +3,6 @@ package org.xbib.z3950.client.jdk.test; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; -import java.util.Arrays; import java.util.Collections; import java.util.Properties; import java.util.logging.Level; @@ -23,7 +22,7 @@ class SWBClientTest { int size = 10; try (JDKZClient client = newZClient(serviceName)) { logger.log(Level.INFO, "executing CQL " + serviceName); - int count = client.searchCQL(query, from, size, + int count = client.searchCQL(query, from, size, null, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, serviceName + " total results = " + total), record -> logger.log(Level.INFO, "record = " + record), @@ -42,7 +41,7 @@ class SWBClientTest { int size = 10; try (JDKZClient client = newZClient(serviceName)) { logger.log(Level.INFO, "executing PQF " + serviceName); - int count = client.searchPQF(query, from, size, + int count = client.searchPQF(query, from, size, null, (status, total, returned, elapsedMillis) -> logger.log(Level.INFO, serviceName + " status = " + status + " total results = " + total), record -> logger.log(Level.INFO, "record = " + record.toString(Charset.forName(client.getEncoding()))), diff --git a/z3950-client-netty/src/main/java/org/xbib/z3950/client/netty/NettyZClient.java b/z3950-client-netty/src/main/java/org/xbib/z3950/client/netty/NettyZClient.java index be8e65c..7e257a5 100644 --- a/z3950-client-netty/src/main/java/org/xbib/z3950/client/netty/NettyZClient.java +++ b/z3950-client-netty/src/main/java/org/xbib/z3950/client/netty/NettyZClient.java @@ -18,6 +18,8 @@ import org.xbib.z3950.api.ScanListener; import org.xbib.z3950.api.SearchListener; import org.xbib.z3950.api.TimeoutListener; import org.xbib.z3950.client.api.Client; +import org.xbib.z3950.common.operations.SortOperation; + import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; @@ -63,6 +65,7 @@ public class NettyZClient implements Client, Closeable { public int searchCQL(String query, int offset, int length, + List sortParameters, SearchListener searchListener, RecordListener recordListener, TimeoutListener timoutListener) throws IOException { @@ -73,6 +76,7 @@ public class NettyZClient implements Client, Closeable { public int searchPQF(String query, int offset, int length, + List sortParameters, SearchListener searchListener, RecordListener recordListener, TimeoutListener timeoutListener) throws IOException { @@ -89,6 +93,12 @@ public class NettyZClient implements Client, Closeable { } + @Override + public void sort(String referenceId, + List parameters, + TimeoutListener timeoutListener) throws IOException { + } + @Override public String getHost() { return null; @@ -149,7 +159,7 @@ public class NettyZClient implements Client, Closeable { return null; } - class Handler extends SimpleChannelInboundHandler { + static class Handler extends SimpleChannelInboundHandler { @Override public void channelActive(ChannelHandlerContext channelHandlerContext){ diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/Diagnostics.java b/z3950-common/src/main/java/org/xbib/z3950/common/Diagnostics.java index c21897c..1db13fc 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/Diagnostics.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/Diagnostics.java @@ -11,11 +11,9 @@ public class Diagnostics extends IOException { private static final ResourceBundle bundle = ResourceBundle.getBundle("org.xbib.z3950.common.diagnostics"); - private static final long serialVersionUID = -899201811019819079L; + private final int diagCode; - private int diagCode; - - private String message; + private final String message; public Diagnostics(int diagCode, String message) { super("" + diagCode + " " + bundle.getString(Integer.toString(diagCode)) + " " + message); diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/operations/AbstractOperation.java b/z3950-common/src/main/java/org/xbib/z3950/common/operations/AbstractOperation.java index c3e621f..3a898f0 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/operations/AbstractOperation.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/operations/AbstractOperation.java @@ -16,6 +16,8 @@ import org.xbib.z3950.common.v3.ScanRequest; import org.xbib.z3950.common.v3.ScanResponse; import org.xbib.z3950.common.v3.SearchRequest; import org.xbib.z3950.common.v3.SearchResponse; +import org.xbib.z3950.common.v3.SortRequest; +import org.xbib.z3950.common.v3.SortResponse; import java.io.IOException; @@ -113,6 +115,12 @@ public class AbstractOperation { if (data instanceof ScanResponse) { return data.berEncode(BEREncoding.CONTEXT_SPECIFIC_TAG, 36); } + if (data instanceof SortRequest) { + return data.berEncode(BEREncoding.CONTEXT_SPECIFIC_TAG, 43); + } + if (data instanceof SortResponse) { + return data.berEncode(BEREncoding.CONTEXT_SPECIFIC_TAG, 44); + } if (data instanceof Close) { return data.berEncode(BEREncoding.CONTEXT_SPECIFIC_TAG, 48); } @@ -159,8 +167,12 @@ public class AbstractOperation { case 36 -> { return (IN) new ScanResponse(ber, false); } - // 43 new SortRequest(ber, false); - // 44 new SortResponse(ber, false); + case 43 -> { + return (IN) new SortRequest(ber, false); + } + case 44 -> { + return (IN) new SortResponse(ber, false); + } // 45 new Segment(ber, false); // 46 new ExtendedServicesRequest(ber, false); // 47 new ExtendedServicesResponse(ber, false); diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/operations/PresentOperation.java b/z3950-common/src/main/java/org/xbib/z3950/common/operations/PresentOperation.java index a3dd1d0..73235fe 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/operations/PresentOperation.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/operations/PresentOperation.java @@ -44,7 +44,8 @@ public class PresentOperation extends AbstractOperation private final List databases; - public ScanOperation(BERReader reader, BERWriter writer, + public ScanOperation(BERReader reader, + BERWriter writer, List databases) { super(reader, writer); this.databases = databases; @@ -42,8 +43,7 @@ public class ScanOperation extends AbstractOperation ScanRequest scanRequest = new ScanRequest(); scanRequest.attributeSet = new AttributeSetId(); // Z39.50 BIB-1: urn:oid:1.2.840.10003.3.1 - scanRequest.attributeSet.value = - new ASN1ObjectIdentifier(new int[]{1, 2, 840, 10003, 3, 1}); + scanRequest.attributeSet.value = new ASN1ObjectIdentifier(new int[]{1, 2, 840, 10003, 3, 1}); DatabaseName[] databaseNames = new DatabaseName[databases.size()]; for (int n = 0; n < databases.size(); n++) { databaseNames[n] = new DatabaseName(); diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/operations/SortOperation.java b/z3950-common/src/main/java/org/xbib/z3950/common/operations/SortOperation.java new file mode 100644 index 0000000..bc3488c --- /dev/null +++ b/z3950-common/src/main/java/org/xbib/z3950/common/operations/SortOperation.java @@ -0,0 +1,197 @@ +package org.xbib.z3950.common.operations; + +import org.xbib.asn1.ASN1GeneralString; +import org.xbib.asn1.ASN1Integer; +import org.xbib.asn1.ASN1Null; +import org.xbib.asn1.ASN1ObjectIdentifier; +import org.xbib.asn1.ASN1OctetString; +import org.xbib.asn1.io.BERReader; +import org.xbib.asn1.io.BERWriter; +import org.xbib.z3950.common.Diagnostics; +import org.xbib.z3950.common.v3.AttributeElement; +import org.xbib.z3950.common.v3.AttributeElementAttributeValue; +import org.xbib.z3950.common.v3.AttributeList; +import org.xbib.z3950.common.v3.AttributeSetId; +import org.xbib.z3950.common.v3.DiagRec; +import org.xbib.z3950.common.v3.InternationalString; +import org.xbib.z3950.common.v3.ReferenceId; +import org.xbib.z3950.common.v3.SortElement; +import org.xbib.z3950.common.v3.SortKey; +import org.xbib.z3950.common.v3.SortKeySortAttributes; +import org.xbib.z3950.common.v3.SortKeySpec; +import org.xbib.z3950.common.v3.SortKeySpecMissingValueAction; +import org.xbib.z3950.common.v3.SortRequest; +import org.xbib.z3950.common.v3.SortResponse; +import org.xbib.z3950.common.v3.Specification; +import org.xbib.z3950.common.v3.SpecificationElementSpec; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class SortOperation extends AbstractOperation { + + private static final Logger logger = Logger.getLogger(SortOperation.class.getName()); + + public SortOperation(BERReader reader, + BERWriter writer) { + super(reader, writer); + } + + public boolean execute(String referenceId, + String resultSetName, + String sortResultSetName, + List list) throws IOException { + SortRequest sortRequest = new SortRequest(); + sortRequest.s_inputResultSetNames = new InternationalString[1]; + sortRequest.s_inputResultSetNames[0] = new InternationalString(); + sortRequest.s_inputResultSetNames[0].value = new ASN1GeneralString(resultSetName); + int i = 0; + SortKeySpec[] sortKeySpec = new SortKeySpec[list.size()]; + for (SortParameter parameter : list) { + SortElement sortElement = new SortElement(); + sortElement.c_generic = new SortKey(); + if (parameter.sortField != null) { + sortElement.c_generic.c_sortfield = new InternationalString(); + sortElement.c_generic.c_sortfield.value = new ASN1GeneralString(parameter.sortField); + } else if (parameter.attributes != null) { + sortElement.c_generic.c_sortAttributes = new SortKeySortAttributes(); + sortElement.c_generic.c_sortAttributes.s_id = new AttributeSetId(); + // Z39.50 BIB-1: urn:oid:1.2.840.10003.3.1 + sortElement.c_generic.c_sortAttributes.s_id.value = new ASN1ObjectIdentifier(new int[]{1, 2, 840, 10003, 3, 1}); + sortElement.c_generic.c_sortAttributes.s_list = new AttributeList(); + sortElement.c_generic.c_sortAttributes.s_list.value = new AttributeElement[parameter.attributes.size()]; + int j = 0; + for (SortAttribute attribute : parameter.attributes) { + sortElement.c_generic.c_sortAttributes.s_list.value[j].attributeType = new ASN1Integer(attribute.type); + sortElement.c_generic.c_sortAttributes.s_list.value[j].attributeValue = new AttributeElementAttributeValue(); + sortElement.c_generic.c_sortAttributes.s_list.value[j].attributeValue.numeric = new ASN1Integer(attribute.value); + j++; + } + } else if (parameter.elementSetName != null) { + sortElement.c_generic.c_elementSpec = new Specification(); + sortElement.c_generic.c_elementSpec.s_elementSpec = new SpecificationElementSpec(); + sortElement.c_generic.c_elementSpec.s_elementSpec.c_elementSetName = new InternationalString(); + sortElement.c_generic.c_elementSpec.s_elementSpec.c_elementSetName.value = + new ASN1GeneralString(parameter.elementSetName); + } else { + throw new IllegalArgumentException("no sortfield, no element set name, and no attributes given in sort request"); + } + sortKeySpec[i] = new SortKeySpec(); + sortKeySpec[i].s_sortElement = sortElement; + sortKeySpec[i].s_sortRelation = new ASN1Integer(parameter.ascending ? 0 : 1); // 0 = ascending, 1 = descending + sortKeySpec[i].s_caseSensitivity = new ASN1Integer(parameter.caseSensitive ? 0 : 1); // 0 = case sensitive, 1 case insensitive + sortKeySpec[i].s_missingValueAction = new SortKeySpecMissingValueAction(); + if (parameter.missingValue != null) { + sortKeySpec[i].s_missingValueAction.c_missingValueData = new ASN1OctetString(parameter.missingValue); + } else { + if (parameter.abort) { + sortKeySpec[i].s_missingValueAction.c_abort = new ASN1Null(); + } else { + sortKeySpec[i].s_missingValueAction.c_null = new ASN1Null(); + } + } + i++; + } + sortRequest.s_sortSequence = sortKeySpec; + sortRequest.s_sortedResultSetName = new InternationalString(); + sortRequest.s_sortedResultSetName.value = new ASN1GeneralString(sortResultSetName); + sortRequest.s_referenceId = new ReferenceId(); + sortRequest.s_referenceId.value = new ASN1OctetString(referenceId); + logger.log(Level.FINER, sortRequest.toString()); + write(sortRequest); + SortResponse sortResponse = read(); + if (sortResponse != null) { + if (sortResponse.s_sortStatus.get() == SortResponse.E_success) { + return true; + } else { + DiagRec[] diagRecs = sortResponse.s_diagnostics; + logger.log(Level.WARNING, "sort failed with diags = " + Arrays.asList(diagRecs)); + for (DiagRec diagRec : diagRecs) { + throw new Diagnostics(diagRec.defaultFormat.condition.get(), + diagRec.defaultFormat.addinfo.v3Addinfo.value.get()); + } + } + } + return false; + } + + public static class SortParameter { + + String sortField; + + List attributes; + + String elementSetName; + + boolean ascending; + + boolean caseSensitive; + + String missingValue; + + boolean abort; + + public SortParameter() { + this.sortField = null; + this.attributes = null; + this.elementSetName = null; + this.ascending = true; + this.caseSensitive = true; + this.abort = false; + this.missingValue = null; + } + + public static SortParameter of(String sortField) { + return new SortParameter().sortField(sortField); + } + + public SortParameter sortField(String sortField) { + this.sortField = sortField; + return this; + } + + public SortParameter setAttributes(List attributes) { + this.attributes = attributes; + return this; + } + + public SortParameter setAscending(boolean ascending) { + this.ascending = ascending; + return this; + } + + public SortParameter setCaseSensitive(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + return this; + } + + public SortParameter setMissingValue(String missingValue) { + this.missingValue = missingValue; + return this; + } + + public SortParameter abortOnMissingValue(boolean abort) { + this.abort = abort; + return this; + } + + public SortParameter setElementSetName(String elementSetName) { + this.elementSetName = elementSetName; + return this; + } + } + + public static class SortAttribute { + int type; + + int value; + + public SortAttribute(int type, int value) { + this.type = type; + this.value = value; + } + } +} diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/CompSpecDbSpecific.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/CompSpecDbSpecific.java index fd41ba7..9eab84e 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/CompSpecDbSpecific.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/CompSpecDbSpecific.java @@ -23,6 +23,9 @@ public final class CompSpecDbSpecific extends ASN1Any { public Specification sSpec; + public CompSpecDbSpecific() { + } + /** * Constructor for a CompSpec_dbSpecific from a BER encoding. * @@ -116,9 +119,9 @@ public final class CompSpecDbSpecific extends ASN1Any { @Override public BEREncoding berEncode(int tagType, int tag) throws ASN1Exception { int numFields = 2; - BEREncoding fields[] = new BEREncoding[numFields]; + BEREncoding[] fields = new BEREncoding[numFields]; int x = 0; - BEREncoding enc[]; + BEREncoding[] enc; enc = new BEREncoding[1]; enc[0] = sDb.berEncode(); fields[x++] = new BERConstructed(BEREncoding.CONTEXT_SPECIFIC_TAG, 1, enc); @@ -132,17 +135,11 @@ public final class CompSpecDbSpecific extends ASN1Any { */ @Override public String toString() { - StringBuilder str = new StringBuilder("{"); - int outputted = 0; - str.append("db "); - str.append(sDb); - outputted++; - if (0 < outputted) { - str.append(", "); - } - str.append("spec "); - str.append(sSpec); - str.append("}"); - return str.toString(); + return "{" + "db " + + sDb + + ", " + + "spec " + + sSpec + + "}"; } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElement.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElement.java index 92a1b8f..2b78b61 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElement.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElement.java @@ -13,15 +13,19 @@ import org.xbib.asn1.BEREncoding; * SortElement ::= * CHOICE { * generic [1] EXPLICIT SortKey - * datbaseSpecific [2] IMPLICIT SEQUENCE OF SortElement_datbaseSpecific + * databaseSpecific [2] IMPLICIT SEQUENCE OF SortElement_databaseSpecific * } * */ public final class SortElement extends ASN1Any { public SortKey c_generic; - public SortElementDatabaseSpecific c_datbaseSpecific[]; + public SortElementDatabaseSpecific[] c_databaseSpecific; + + + public SortElement() { + } /** * Constructor for a SortElement from a BER encoding. @@ -52,7 +56,7 @@ public final class SortElement extends ASN1Any { // Null out all choices c_generic = null; - c_datbaseSpecific = null; + c_databaseSpecific = null; // Try choice generic if (ber.getTag() == 1 && @@ -84,10 +88,10 @@ public final class SortElement extends ASN1Any { int numParts = berConstructed.numberComponents(); int p; - c_datbaseSpecific = new SortElementDatabaseSpecific[numParts]; + c_databaseSpecific = new SortElementDatabaseSpecific[numParts]; for (p = 0; p < numParts; p++) { - c_datbaseSpecific[p] = new SortElementDatabaseSpecific(berConstructed.elementAt(p), true); + c_databaseSpecific[p] = new SortElementDatabaseSpecific(berConstructed.elementAt(p), true); } return; } @@ -105,9 +109,9 @@ public final class SortElement extends ASN1Any { public BEREncoding berEncode() throws ASN1Exception { BEREncoding chosen = null; - BEREncoding f2[]; + BEREncoding[] f2; int p; - BEREncoding enc[]; + BEREncoding[] enc; // Encoding choice: c_generic if (c_generic != null) { @@ -116,15 +120,15 @@ public final class SortElement extends ASN1Any { chosen = new BERConstructed(BEREncoding.CONTEXT_SPECIFIC_TAG, 1, enc); } - // Encoding choice: c_datbaseSpecific - if (c_datbaseSpecific != null) { + // Encoding choice: c_databaseSpecific + if (c_databaseSpecific != null) { if (chosen != null) { throw new ASN1Exception("CHOICE multiply set"); } - f2 = new BEREncoding[c_datbaseSpecific.length]; + f2 = new BEREncoding[c_databaseSpecific.length]; - for (p = 0; p < c_datbaseSpecific.length; p++) { - f2[p] = c_datbaseSpecific[p].berEncode(); + for (p = 0; p < c_databaseSpecific.length; p++) { + f2[p] = c_databaseSpecific[p].berEncode(); } chosen = new BERConstructed(BEREncoding.CONTEXT_SPECIFIC_TAG, 2, f2); @@ -179,22 +183,18 @@ public final class SortElement extends ASN1Any { str.append("generic "); str.append(c_generic); } - - if (c_datbaseSpecific != null) { + if (c_databaseSpecific != null) { if (found) { str.append(" "); } - found = true; str.append("datbaseSpecific "); str.append("{"); - for (p = 0; p < c_datbaseSpecific.length; p++) { - str.append(c_datbaseSpecific[p]); + for (p = 0; p < c_databaseSpecific.length; p++) { + str.append(c_databaseSpecific[p]); } str.append("}"); } - str.append("}"); - return str.toString(); } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElementDatabaseSpecific.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElementDatabaseSpecific.java index 796250d..75f4f80 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElementDatabaseSpecific.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortElementDatabaseSpecific.java @@ -20,8 +20,12 @@ import org.xbib.asn1.BEREncoding; public final class SortElementDatabaseSpecific extends ASN1Any { public DatabaseName s_databaseName; + public SortKey s_dbSort; + public SortElementDatabaseSpecific() { + } + /** * Constructor for a SortElement_datbaseSpecific from a BER encoding. * @@ -117,7 +121,7 @@ public final class SortElementDatabaseSpecific extends ASN1Any { // Encode it - BEREncoding fields[] = new BEREncoding[numFields]; + BEREncoding[] fields = new BEREncoding[numFields]; int x = 0; // Encoding s_databaseName: DatabaseName @@ -136,22 +140,11 @@ public final class SortElementDatabaseSpecific extends ASN1Any { */ @Override public String toString() { - StringBuilder str = new StringBuilder("{"); - int outputted = 0; - - str.append("databaseName "); - str.append(s_databaseName); - outputted++; - - if (0 < outputted) { - str.append(", "); - } - str.append("dbSort "); - str.append(s_dbSort); - - str.append("}"); - - return str.toString(); + return "{" + "databaseName " + + s_databaseName + + ", " + + "dbSort " + + s_dbSort + + "}"; } - } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKey.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKey.java index 5541144..2349ab6 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKey.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKey.java @@ -23,6 +23,9 @@ public final class SortKey extends ASN1Any { public Specification c_elementSpec; public SortKeySortAttributes c_sortAttributes; + public SortKey() { + } + /** * Constructor for a SortKey from a BER encoding. * @@ -146,7 +149,6 @@ public final class SortKey extends ASN1Any { str.append("sortfield "); str.append(c_sortfield); } - if (c_elementSpec != null) { if (found) { str.append(" "); @@ -155,18 +157,14 @@ public final class SortKey extends ASN1Any { str.append("elementSpec "); str.append(c_elementSpec); } - if (c_sortAttributes != null) { if (found) { str.append(" "); } - found = true; str.append("sortAttributes "); str.append(c_sortAttributes); } - str.append("}"); - return str.toString(); } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySortAttributes.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySortAttributes.java index 20501e6..c1ade67 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySortAttributes.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySortAttributes.java @@ -23,6 +23,8 @@ public final class SortKeySortAttributes extends ASN1Any { public AttributeSetId s_id; public AttributeList s_list; + public SortKeySortAttributes() { + } /** * Constructor for a SortKey_sortAttributes from a BER encoding. @@ -96,7 +98,7 @@ public final class SortKeySortAttributes extends ASN1Any { @Override public BEREncoding berEncode(int tagType, int tag) throws ASN1Exception { int numFields = 2; // number of mandatories - BEREncoding fields[] = new BEREncoding[numFields]; + BEREncoding[] fields = new BEREncoding[numFields]; int x = 0; fields[x++] = s_id.berEncode(); fields[x] = s_list.berEncode(); @@ -114,13 +116,9 @@ public final class SortKeySortAttributes extends ASN1Any { str.append("id "); str.append(s_id); outputted++; - if (0 < outputted) { - str.append(", "); - } str.append("list "); str.append(s_list); str.append("}"); return str.toString(); } - } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpec.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpec.java index e4f1dab..c6f01a0 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpec.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpec.java @@ -23,16 +23,28 @@ import org.xbib.asn1.BEREncoding; public final class SortKeySpec extends ASN1Any { public static final int E_ascending = 0; + public static final int E_descending = 1; + public static final int E_ascendingByFrequency = 3; + public static final int E_descendingByfrequency = 4; + public static final int E_caseSensitive = 0; + public static final int E_caseInsensitive = 1; + public SortElement s_sortElement; + public ASN1Integer s_sortRelation; + public ASN1Integer s_caseSensitivity; + public SortKeySpecMissingValueAction s_missingValueAction; // optional + public SortKeySpec() { + } + /** * Constructor for a SortKeySpec from a BER encoding. * @@ -196,29 +208,16 @@ public final class SortKeySpec extends ASN1Any { @Override public String toString() { StringBuilder str = new StringBuilder("{"); - int outputted = 0; str.append("sortElement "); str.append(s_sortElement); - outputted++; - - if (0 < outputted) { - str.append(", "); - } + str.append(", "); str.append("sortRelation "); str.append(s_sortRelation); - outputted++; - - if (0 < outputted) { - str.append(", "); - } + str.append(", "); str.append("caseSensitivity "); str.append(s_caseSensitivity); - outputted++; - if (s_missingValueAction != null) { - if (0 < outputted) { - str.append(", "); - } + str.append(", "); str.append("missingValueAction "); str.append(s_missingValueAction); } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpecMissingValueAction.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpecMissingValueAction.java index 9ff329c..b6d9a85 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpecMissingValueAction.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortKeySpecMissingValueAction.java @@ -24,6 +24,9 @@ public final class SortKeySpecMissingValueAction extends ASN1Any { public ASN1Null c_null; public ASN1OctetString c_missingValueData; + public SortKeySpecMissingValueAction() { + } + /** * Constructor for a SortKeySpec_missingValueAction from a BER encoding. * @@ -157,7 +160,6 @@ public final class SortKeySpecMissingValueAction extends ASN1Any { str.append("abort "); str.append(c_abort); } - if (c_null != null) { if (found) { str.append(" "); @@ -166,7 +168,6 @@ public final class SortKeySpecMissingValueAction extends ASN1Any { str.append("null "); str.append(c_null); } - if (c_missingValueData != null) { if (found) { str.append(" "); diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortRequest.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortRequest.java index a9d8df4..79bf8eb 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortRequest.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortRequest.java @@ -22,12 +22,20 @@ import org.xbib.asn1.BEREncoding; * */ public final class SortRequest extends ASN1Any { + public ReferenceId s_referenceId; // optional - public InternationalString s_inputResultSetNames[]; + + public InternationalString[] s_inputResultSetNames; + public InternationalString s_sortedResultSetName; - public SortKeySpec s_sortSequence[]; + + public SortKeySpec[] s_sortSequence; + public OtherInformation s_otherInfo; // optional + public SortRequest() { + } + /** * Constructor for a SortRequest from a BER encoding. * @@ -205,9 +213,9 @@ public final class SortRequest extends ASN1Any { if (s_otherInfo != null) { numFields++; } - BEREncoding fields[] = new BEREncoding[numFields]; + BEREncoding[] fields = new BEREncoding[numFields]; int x = 0; - BEREncoding f2[]; + BEREncoding[] f2; int p; if (s_referenceId != null) { fields[x++] = s_referenceId.berEncode(); diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortResponse.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortResponse.java index c6e4779..7541356 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortResponse.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SortResponse.java @@ -24,18 +24,32 @@ import org.xbib.asn1.BEREncoding; public final class SortResponse extends ASN1Any { public static final int E_success = 0; + public static final int E_partial_1 = 1; + public static final int E_failure = 2; + public static final int E_empty = 1; + public static final int E_interim = 2; + public static final int E_unchanged = 3; + public static final int E_none = 4; + public ReferenceId s_referenceId; // optional + public ASN1Integer s_sortStatus; + public ASN1Integer s_resultSetStatus; // optional - public DiagRec s_diagnostics[]; // optional + + public DiagRec[] s_diagnostics; // optional + public OtherInformation s_otherInfo; // optional + public SortResponse() { + } + /** * Constructor for a SortResponse from a BER encoding. * @@ -194,9 +208,9 @@ public final class SortResponse extends ASN1Any { if (s_otherInfo != null) { numFields++; } - BEREncoding fields[] = new BEREncoding[numFields]; + BEREncoding[] fields = new BEREncoding[numFields]; int x = 0; - BEREncoding f2[]; + BEREncoding[] f2; int p; if (s_referenceId != null) { fields[x++] = s_referenceId.berEncode(); @@ -239,17 +253,13 @@ public final class SortResponse extends ASN1Any { str.append(s_sortStatus); outputted++; if (s_resultSetStatus != null) { - if (0 < outputted) { - str.append(", "); - } + str.append(", "); str.append("resultSetStatus "); str.append(s_resultSetStatus); outputted++; } if (s_diagnostics != null) { - if (0 < outputted) { - str.append(", "); - } + str.append(", "); str.append("diagnostics "); str.append("{"); for (p = 0; p < s_diagnostics.length; p++) { @@ -261,7 +271,6 @@ public final class SortResponse extends ASN1Any { str.append("}"); outputted++; } - if (s_otherInfo != null) { if (0 < outputted) { str.append(", "); @@ -269,9 +278,7 @@ public final class SortResponse extends ASN1Any { str.append("otherInfo "); str.append(s_otherInfo); } - str.append("}"); - return str.toString(); } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/Specification.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/Specification.java index 8996677..4e1618f 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/Specification.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/Specification.java @@ -24,6 +24,9 @@ public final class Specification extends ASN1Any { public ASN1ObjectIdentifier s_schema; // optional public SpecificationElementSpec s_elementSpec; // optional + public Specification() { + } + /** * Constructor for a Specification from a BER encoding. * @@ -145,9 +148,9 @@ public final class Specification extends ASN1Any { // Encode it - BEREncoding fields[] = new BEREncoding[numFields]; + BEREncoding[] fields = new BEREncoding[numFields]; int x = 0; - BEREncoding enc[]; + BEREncoding[] enc; // Encoding s_schema: OBJECT IDENTIFIER OPTIONAL @@ -178,7 +181,6 @@ public final class Specification extends ASN1Any { str.append(s_schema); outputted++; } - if (s_elementSpec != null) { if (0 < outputted) { str.append(", "); @@ -186,9 +188,7 @@ public final class Specification extends ASN1Any { str.append("elementSpec "); str.append(s_elementSpec); } - str.append("}"); - return str.toString(); } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SpecificationElementSpec.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SpecificationElementSpec.java index 16db163..9ab1438 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/SpecificationElementSpec.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/SpecificationElementSpec.java @@ -21,6 +21,9 @@ public final class SpecificationElementSpec extends ASN1Any { public InternationalString c_elementSetName; public ASN1External c_externalEspec; + public SpecificationElementSpec() { + } + /** * Constructor for a Specification_elementSpec from a BER encoding. *