From 39a962cbdf7e3aeced097e7878f1a671ec964e18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Fri, 31 Jul 2020 10:57:20 +0200 Subject: [PATCH] update netty to 4.1.51, xbib cql to 3.0.2, fix attribute list deriving from CQL search term --- gradle.properties | 6 +- .../z3950/common/cql/CQLRPNGenerator.java | 162 +++++++++++------- .../z3950/common/pqf/PQFRPNGenerator.java | 4 +- .../xbib/z3950/common/v3/AttributeList.java | 2 +- .../org/xbib/z3950/common/v3/Operand.java | 9 +- .../org/xbib/z3950/common/v3/RPNQuery.java | 22 +-- .../xbib/z3950/common/v3/RPNStructure.java | 7 +- .../xbib/z3950/common/cql/CQL2RPNTest.java | 19 +- 8 files changed, 133 insertions(+), 98 deletions(-) diff --git a/gradle.properties b/gradle.properties index 9075dce..79f6cb6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,8 @@ group = org.xbib name = z3950 -version = 2.2.0 +version = 2.2.1 gradle.wrapper.version = 6.4.1 -netty.version = 4.1.50.Final -xbib-cql.version = 3.0.1 +netty.version = 4.1.51.Final +xbib-cql.version = 3.0.2 xbib-bibliographic-character-sets.version = 2.0.0 \ No newline at end of file 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 4cc03be..5d33953 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 @@ -32,7 +32,6 @@ import org.xbib.z3950.common.v3.Operator; import org.xbib.z3950.common.v3.RPNQuery; import org.xbib.z3950.common.v3.RPNStructure; import org.xbib.z3950.common.v3.RPNStructureRpnRpnOp; - import java.util.HashMap; import java.util.Map; import java.util.MissingResourceException; @@ -49,18 +48,25 @@ public final class CQLRPNGenerator implements Visitor { /** * Context map. */ - private final Map contexts = new HashMap() { + private final Map contexts = new HashMap<>() { private static final long serialVersionUID = 8199395368653216950L; + { + put("cql", ResourceBundle.getBundle("org.xbib.z3950.common.cql.cql")); put("bib", ResourceBundle.getBundle("org.xbib.z3950.common.cql.bib-1")); - put("dc", ResourceBundle.getBundle("org.xbib.z3950.common.cql.dc")); - put("gbv", ResourceBundle.getBundle("org.xbib.z3950.common.cql.gbv")); + put("dc", ResourceBundle.getBundle("org.xbib.z3950.common.cql.dc")); + put("gbv", ResourceBundle.getBundle("org.xbib.z3950.common.cql.gbv")); } }; - private Stack result; + + private final Stack attributeElements; + + private final Stack result; + private RPNQuery rpnQuery; public CQLRPNGenerator() { + this.attributeElements = new Stack<>(); this.result = new Stack<>(); } @@ -80,8 +86,8 @@ public final class CQLRPNGenerator implements Visitor { this.rpnQuery = new RPNQuery(); rpnQuery.rpn = (RPNStructure) result.pop(); // Z39.50 BIB-1: urn:oid:1.2.840.10003.3.1 - rpnQuery.attributeSet = new AttributeSetId(); - rpnQuery.attributeSet.value = new ASN1ObjectIdentifier(new int[]{1, 2, 840, 10003, 3, 1}); + rpnQuery.attributeSetId = new AttributeSetId(); + rpnQuery.attributeSetId.value = new ASN1ObjectIdentifier(new int[]{1, 2, 840, 10003, 3, 1}); } else { throw new SyntaxException("unable to generate RPN from CQL"); } @@ -183,15 +189,14 @@ public final class CQLRPNGenerator implements Visitor { Operand operand = new Operand(); operand.attrTerm = new AttributesPlusTerm(); operand.attrTerm.term = new org.xbib.z3950.common.v3.Term(); - operand.attrTerm.term.c_general = new ASN1OctetString(node.getTerm().getValue()); - Stack attrs = new Stack<>(); - ASN1Any any = !result.isEmpty() && result.peek() instanceof AttributeElement ? result.pop() : null; - while (any != null) { - attrs.push((AttributeElement) any); - any = !result.isEmpty() && result.peek() instanceof AttributeElement ? result.pop() : null; + ASN1Any any = result.pop(); + if (any instanceof ASN1OctetString) { + operand.attrTerm.term.c_general = (ASN1OctetString)any; + } else { + operand.attrTerm.term.c_general = new ASN1OctetString(node.getTerm().getValue()); } operand.attrTerm.attributes = new AttributeList(); - operand.attrTerm.attributes.value = attrs.toArray(new AttributeElement[attrs.size()]); + operand.attrTerm.attributes.value = attributeElements.toArray(new AttributeElement[0]); RPNStructure rpn = new RPNStructure(); rpn.c_op = operand; result.push(rpn); @@ -202,43 +207,43 @@ public final class CQLRPNGenerator implements Visitor { if (node.getModifierList() != null) { node.getModifierList().accept(this); } - int t = 2; - int n = 3; + int attributeType = 2; + int attributeValue = 3; switch (node.getComparitor()) { case LESS: // 2=1 - n = 1; + attributeValue = 1; break; case LESS_EQUALS: // 2=2 - n = 2; + attributeValue = 2; break; case EQUALS: // 2=3 - n = 3; + attributeValue = 3; break; case GREATER_EQUALS: // 2=4 - n = 4; + attributeValue = 4; break; case GREATER: // 2=5 - n = 5; + attributeValue = 5; break; case NOT_EQUALS: // 2=6 - n = 6; + attributeValue = 6; break; case ALL: // 4=6 - t = 4; - n = 6; + attributeType = 4; + attributeValue = 6; break; case ANY: // 4=105 - t = 4; - n = 104; + attributeType = 4; + attributeValue = 104; break; default: break; } - if (n != 3) { + if (attributeValue != 3) { AttributeElement ae = new AttributeElement(); - ae.attributeType = new ASN1Integer(t); + ae.attributeType = new ASN1Integer(attributeType); ae.attributeValue = new AttributeElementAttributeValue(); - ae.attributeValue.numeric = new ASN1Integer(n); + ae.attributeValue.numeric = new ASN1Integer(attributeValue); result.push(ae); } } @@ -261,29 +266,9 @@ public final class CQLRPNGenerator implements Visitor { } @Override - public void visit(Term node) { - int t = 5; - int n = 100; - // check for '*' - String v = node.getValue(); - if (v.endsWith("*")) { - if (v.startsWith("*")) { - n = 3; - } else { - n = 1; - } - } else if (v.startsWith("*")) { - n = 2; - } - if (n != 100) { - AttributeElement ae = new AttributeElement(); - ae.attributeType = new ASN1Integer(t); - ae.attributeValue = new AttributeElementAttributeValue(); - ae.attributeValue.numeric = new ASN1Integer(n); - result.push(ae); - v = v.replaceAll("\\*", ""); - } - ASN1OctetString s = new ASN1OctetString(v); + public void visit(Term term) { + // the value + ASN1OctetString s = transformTerm(term); result.push(s); } @@ -298,18 +283,18 @@ public final class CQLRPNGenerator implements Visitor { } @Override - public void visit(Index node) { - String context = node.getContext(); + public void visit(Index index) { + String context = index.getContext(); if (context == null) { context = "dc"; // default context } - int t = 1; - int n = getUseAttr(context, node.getName()); + int attributeType = 1; // use attribute set: bib-1 = 1 + int attributeValue = getUseAttr(context, index.getName()); AttributeElement ae = new AttributeElement(); - ae.attributeType = new ASN1Integer(t); + ae.attributeType = new ASN1Integer(attributeType); ae.attributeValue = new AttributeElementAttributeValue(); - ae.attributeValue.numeric = new ASN1Integer(n); - result.push(ae); + ae.attributeValue.numeric = new ASN1Integer(attributeValue); + attributeElements.push(ae); } private int getUseAttr(String context, String attrName) { @@ -319,4 +304,63 @@ public final class CQLRPNGenerator implements Visitor { throw new SyntaxException("unknown use attribute '" + attrName + "' for context " + context, e); } } + + private ASN1OctetString transformTerm(Term term) { + String v = term.getValue(); + // let's derive attributes from the search term + + // relation attribute = 2 + int attributeType = 2; + int attributeValue = 3; // equal = 3 + AttributeElement ae = new AttributeElement(); + ae.attributeType = new ASN1Integer(attributeType); + ae.attributeValue = new AttributeElementAttributeValue(); + ae.attributeValue.numeric = new ASN1Integer(attributeValue); + attributeElements.push(ae); + + // position attribute = 3 + attributeType = 3; + attributeValue = 3; // any position = 3 + ae = new AttributeElement(); + ae.attributeType = new ASN1Integer(attributeType); + ae.attributeValue = new AttributeElementAttributeValue(); + ae.attributeValue.numeric = new ASN1Integer(attributeValue); + attributeElements.push(ae); + + // structure attribute = 4 + attributeType = 4; + attributeValue = 2; // word = 2 + if (v.startsWith("\"") && v.endsWith("\"")) { + attributeValue = 1; // phrase + v = v.substring(1, v.length()-1); + } + ae = new AttributeElement(); + ae.attributeType = new ASN1Integer(attributeType); + ae.attributeValue = new AttributeElementAttributeValue(); + ae.attributeValue.numeric = new ASN1Integer(attributeValue); + attributeElements.push(ae); + + // truncation attribute = 5 + attributeType = 5; + attributeValue = 100; // do not truncate = 5 + if (v.endsWith("*")) { + if (v.startsWith("*")) { + attributeValue = 3; // Left and right truncation = 3 + v = v.substring(1, v.length() - 1); + } else { + attributeValue = 1; // Right truncation = 1 + v = v.substring(0, v.length() - 1); + } + } else if (v.startsWith("*")) { + attributeValue = 2; // Left truncation = 2 + v = v.substring(1); + } + ae = new AttributeElement(); + ae.attributeType = new ASN1Integer(attributeType); + ae.attributeValue = new AttributeElementAttributeValue(); + ae.attributeValue.numeric = new ASN1Integer(attributeValue); + attributeElements.push(ae); + + return new ASN1OctetString(v); + } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/pqf/PQFRPNGenerator.java b/z3950-common/src/main/java/org/xbib/z3950/common/pqf/PQFRPNGenerator.java index c3174f7..477b363 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/pqf/PQFRPNGenerator.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/pqf/PQFRPNGenerator.java @@ -42,8 +42,8 @@ public class PQFRPNGenerator implements Visitor { rpnQuery.rpn = (RPNStructure) result.pop(); if (pqf.getAttrSet() == null) { // Z39.50 BIB-1: urn:oid:1.2.840.10003.3.1 - rpnQuery.attributeSet = new AttributeSetId(); - rpnQuery.attributeSet.value = new ASN1ObjectIdentifier(new int[]{1, 2, 840, 10003, 3, 1}); + rpnQuery.attributeSetId = new AttributeSetId(); + rpnQuery.attributeSetId.value = new ASN1ObjectIdentifier(new int[]{1, 2, 840, 10003, 3, 1}); } } else { throw new SyntaxException("no valid PQF found"); diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/AttributeList.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/AttributeList.java index 9dbaf0f..8fc0149 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/AttributeList.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/AttributeList.java @@ -89,7 +89,7 @@ public final class AttributeList extends ASN1Any { */ @Override public BEREncoding berEncode(int tagType, int tag) throws ASN1Exception { - BEREncoding fields[] = new BERConstructed[value.length]; + BEREncoding[] fields = new BERConstructed[value.length]; int p; for (p = 0; p < value.length; p++) { fields[p] = value[p].berEncode(); diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/Operand.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/Operand.java index 4f66afb..e198619 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/Operand.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/Operand.java @@ -19,7 +19,9 @@ import org.xbib.asn1.BEREncoding; public final class Operand extends ASN1Any { public AttributesPlusTerm attrTerm; + public ResultSetId resultSet; + public ResultSetPlusAttributes resultAttr; public Operand() { @@ -50,11 +52,9 @@ public final class Operand extends ASN1Any { @Override public void berDecode(BEREncoding ber, boolean checkTag) throws ASN1Exception { // Null out all choices - attrTerm = null; resultSet = null; resultAttr = null; - // Try choice attrTerm try { attrTerm = new AttributesPlusTerm(ber, checkTag); @@ -62,7 +62,6 @@ public final class Operand extends ASN1Any { } catch (ASN1Exception e) { // failed to decode, continue on } - // Try choice resultSet try { resultSet = new ResultSetId(ber, checkTag); @@ -70,7 +69,6 @@ public final class Operand extends ASN1Any { } catch (ASN1Exception e) { // failed to decode, continue on } - // Try choice resultAttr try { resultAttr = new ResultSetPlusAttributes(ber, checkTag); @@ -78,7 +76,6 @@ public final class Operand extends ASN1Any { } catch (ASN1Exception e) { // failed to decode, continue on } - throw new ASN1Exception("bad BER encoding: choice not matched"); } @@ -91,12 +88,10 @@ public final class Operand extends ASN1Any { @Override public BEREncoding berEncode() throws ASN1Exception { BEREncoding chosen = null; - // Encoding choice: c_attrTerm if (attrTerm != null) { chosen = attrTerm.berEncode(); } - // Encoding choice: c_resultSet if (resultSet != null) { if (chosen != null) { diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNQuery.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNQuery.java index e430143..9b6ec70 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNQuery.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNQuery.java @@ -19,7 +19,8 @@ import org.xbib.asn1.BEREncoding; */ public final class RPNQuery extends ASN1Any { - public AttributeSetId attributeSet; + public AttributeSetId attributeSetId; + public RPNStructure rpn; /** @@ -65,7 +66,7 @@ public final class RPNQuery extends ASN1Any { throw new ASN1Exception("incomplete"); } p = berConstructed.elementAt(part); - attributeSet = new AttributeSetId(p, true); + attributeSetId = new AttributeSetId(p, true); part++; if (numParts <= part) { throw new ASN1Exception("incomplete"); @@ -100,9 +101,9 @@ public final class RPNQuery 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; - fields[x++] = attributeSet.berEncode(); + fields[x++] = attributeSetId.berEncode(); fields[x] = rpn.berEncode(); return new BERConstructed(tagType, tag, fields); } @@ -113,17 +114,6 @@ public final class RPNQuery extends ASN1Any { */ @Override public String toString() { - StringBuilder str = new StringBuilder("{"); - int outputted = 0; - str.append("attributeSet "); - str.append(attributeSet); - outputted++; - if (0 < outputted) { - str.append(", "); - } - str.append("rpn "); - str.append(rpn); - str.append("}"); - return str.toString(); + return "{" + "attributeSetId " + attributeSetId + ", " + "rpn " + rpn + "}"; } } diff --git a/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNStructure.java b/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNStructure.java index cc2a281..6b2e237 100644 --- a/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNStructure.java +++ b/z3950-common/src/main/java/org/xbib/z3950/common/v3/RPNStructure.java @@ -19,6 +19,7 @@ import org.xbib.asn1.BEREncoding; public final class RPNStructure extends ASN1Any { public Operand c_op; + public RPNStructureRpnRpnOp c_rpnRpnOp; /** @@ -86,16 +87,13 @@ public final class RPNStructure extends ASN1Any { @Override public BEREncoding berEncode() throws ASN1Exception { BEREncoding chosen = null; - - BEREncoding enc[]; - + BEREncoding[] enc; // Encoding choice: c_op if (c_op != null) { enc = new BEREncoding[1]; enc[0] = c_op.berEncode(); chosen = new BERConstructed(BEREncoding.CONTEXT_SPECIFIC_TAG, 0, enc); } - // Encoding choice: c_rpnRpnOp if (c_rpnRpnOp != null) { if (chosen != null) { @@ -103,7 +101,6 @@ public final class RPNStructure extends ASN1Any { } chosen = c_rpnRpnOp.berEncode(BEREncoding.CONTEXT_SPECIFIC_TAG, 1); } - // Check for error of having none of the choices set if (chosen == null) { throw new ASN1Exception("CHOICE not set"); diff --git a/z3950-common/src/test/java/org/xbib/z3950/common/cql/CQL2RPNTest.java b/z3950-common/src/test/java/org/xbib/z3950/common/cql/CQL2RPNTest.java index f159fa1..08e5d25 100644 --- a/z3950-common/src/test/java/org/xbib/z3950/common/cql/CQL2RPNTest.java +++ b/z3950-common/src/test/java/org/xbib/z3950/common/cql/CQL2RPNTest.java @@ -1,21 +1,30 @@ package org.xbib.z3950.common.cql; +import static org.junit.jupiter.api.Assertions.assertEquals; import org.junit.jupiter.api.Test; import org.xbib.cql.CQLParser; -/** - * - */ class CQL2RPNTest { @Test - void testCQL2RPN() { + void testDublinCoreContext() { String cql = "dc.title = Test"; CQLParser parser = new CQLParser(cql); parser.parse(); CQLRPNGenerator generator = new CQLRPNGenerator(); parser.getCQLQuery().accept(generator); String q = generator.getQueryResult().toString(); - System.err.println("cql2rpn = " + q); + assertEquals("{attributeSetId 1.2.840.10003.3.1, rpn {op {attrTerm {attributes {{attributeType 2, attributeValue {numeric 3}}{attributeType 3, attributeValue {numeric 3}}{attributeType 4, attributeValue {numeric 2}}{attributeType 5, attributeValue {numeric 100}}{attributeType 1, attributeValue {numeric 4}}}, term {general \"Test\"}}}}}", q); + } + + @Test + void testPhrase() { + String cql = "dc.title = \"a phrase\""; + CQLParser parser = new CQLParser(cql); + parser.parse(); + CQLRPNGenerator generator = new CQLRPNGenerator(); + parser.getCQLQuery().accept(generator); + String q = generator.getQueryResult().toString(); + assertEquals("{attributeSetId 1.2.840.10003.3.1, rpn {op {attrTerm {attributes {{attributeType 2, attributeValue {numeric 3}}{attributeType 3, attributeValue {numeric 3}}{attributeType 4, attributeValue {numeric 1}}{attributeType 5, attributeValue {numeric 100}}{attributeType 1, attributeValue {numeric 4}}}, term {general \"a phrase\"}}}}}", q); } }