diff --git a/gradle.properties b/gradle.properties index b0095b2..0a9a09a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = z3950 -version = 5.0.4 +version = 5.0.5 org.gradle.warning.mode = ALL 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 e8d013a..4d51e55 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 @@ -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 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 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 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); } } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/cql/CQLRPNGenerator.java b/z3950-common/src/main/java/org/xbib/z3950/common/cql/CQLRPNGenerator.java index 1bc1f99..613ec3c 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/cql/CQLRPNGenerator.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/cql/CQLRPNGenerator.java @@ -49,9 +49,8 @@ public final class CQLRPNGenerator implements Visitor { /** * Context map. */ + @SuppressWarnings("serial") private final Map 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 - attributeValue = 1; - break; - case LESS_EQUALS: // 2=2 - attributeValue = 2; - break; - case EQUALS: // 2=3 - attributeValue = 3; - break; - case GREATER_EQUALS: // 2=4 - attributeValue = 4; - break; - case GREATER: // 2=5 - attributeValue = 5; - break; - case NOT_EQUALS: // 2=6 - attributeValue = 6; - break; - case ALL: // 4=6 + case LESS -> // 2=1 + attributeValue = 1; + case LESS_EQUALS -> // 2=2 + attributeValue = 2; + case GREATER_EQUALS -> // 2=4 + attributeValue = 4; + case GREATER -> // 2=5 + attributeValue = 5; + case NOT_EQUALS -> // 2=6 + attributeValue = 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); } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/operations/InitOperation.java b/z3950-common/src/main/java/org/xbib/z3950/common/operations/InitOperation.java index 2b295cc..83bc45e 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/operations/InitOperation.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/operations/InitOperation.java @@ -37,7 +37,10 @@ public class InitOperation extends AbstractOperation