Temporarily add Ralph's changes to the source, which can generate
authormike <mike>
Wed, 4 Dec 2002 16:54:01 +0000 (16:54 +0000)
committermike <mike>
Wed, 4 Dec 2002 16:54:01 +0000 (16:54 +0000)
BER packets.  I'll remove these as I integrate them into the master
source.

src/org/z3950/zing/ralph/CQLAndNode.java [new file with mode: 0644]
src/org/z3950/zing/ralph/CQLBooleanNode.java [new file with mode: 0644]
src/org/z3950/zing/ralph/CQLNotNode.java [new file with mode: 0644]
src/org/z3950/zing/ralph/CQLOrNode.java [new file with mode: 0644]
src/org/z3950/zing/ralph/CQLPrefix.java [new file with mode: 0644]
src/org/z3950/zing/ralph/CQLPrefixNode.java [new file with mode: 0644]
src/org/z3950/zing/ralph/CQLProxNode.java [new file with mode: 0644]
src/org/z3950/zing/ralph/CQLTermNode.java [new file with mode: 0644]

diff --git a/src/org/z3950/zing/ralph/CQLAndNode.java b/src/org/z3950/zing/ralph/CQLAndNode.java
new file mode 100644 (file)
index 0000000..d55edf9
--- /dev/null
@@ -0,0 +1,32 @@
+// $Id: CQLAndNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+
+
+/**
+ * Represents an AND node in a CQL parse-tree.
+ *
+ * @version    $Id: CQLAndNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public class CQLAndNode extends CQLBooleanNode {
+    /**
+     * Creates a new AND node with the specified left- and right-hand sides.
+     */
+    public CQLAndNode(CQLNode left, CQLNode right) {
+       this.left = left;
+       this.right = right;
+    }
+
+    String op() {
+       return "and";
+    }
+    
+    byte[] opType1() {
+        byte[] op=new byte[5];
+        putTag(CONTEXT, 46, CONSTRUCTED, op, 0); // Operator
+        putLen(2, op, 2);
+        putTag(CONTEXT, 0, PRIMITIVE, op, 3); // and
+        putLen(0, op, 4);
+        return op;
+    }
+}
diff --git a/src/org/z3950/zing/ralph/CQLBooleanNode.java b/src/org/z3950/zing/ralph/CQLBooleanNode.java
new file mode 100644 (file)
index 0000000..ccd2b3f
--- /dev/null
@@ -0,0 +1,82 @@
+// $Id: CQLBooleanNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * Represents a boolean node in a CQL parse-tree.
+ *
+ * @version    $Id: CQLBooleanNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public abstract class CQLBooleanNode extends CQLNode {
+    CQLBooleanNode() {}                // prevent javadoc from documenting this
+
+    /**
+     * The root of a parse-tree representing the left-hand side.
+     */ 
+    public CQLNode left;
+
+    /**
+     * The root of a parse-tree representing the right-hand side.
+     */ 
+    public CQLNode right;
+
+    public String toXCQL(int level, Vector prefixes) {
+       return (indent(level) + "<triple>\n" +
+               renderPrefixes(level+1, prefixes) +
+               opXCQL(level+1) +
+               indent(level+1) + "<leftOperand>\n" +
+               left.toXCQL(level+2, new Vector()) +
+               indent(level+1) + "</leftOperand>\n" +
+               indent(level+1) + "<rightOperand>\n" +
+               right.toXCQL(level+2, new Vector()) +
+               indent(level+1) + "</rightOperand>\n" +
+               indent(level) + "</triple>\n");
+    }
+
+    // Represents the boolean operation itself: overridden for CQLProxNode
+    String opXCQL(int level) {
+       return(indent(level) + "<boolean>\n" +
+              indent(level+1) + "<value>" + op() + "</value>\n" +
+              indent(level) + "</boolean>\n");
+    }
+
+    public String toCQL() {
+       // ### We don't always need parens around the operands
+       return "(" + left.toCQL() + ") " + op() + " (" + right.toCQL() + ")";
+    }
+
+    public String toPQF(Properties config) throws PQFTranslationException {
+       return ("@" + opPQF() +
+               " " + left.toPQF(config) +
+               " " + right.toPQF(config));
+    }
+
+    public byte[] toType1(Properties config) throws PQFTranslationException {
+        System.out.println("in CQLBooleanNode.toType101(): PQF="+toPQF(config));
+        byte[] rpn1=left.toType1(config);
+        byte[] rpn2=right.toType1(config);
+        byte[] op=opType1();
+        byte[] rpnStructure=new byte[rpn1.length+rpn2.length+op.length+4];
+        
+        int offset=putTag(CONTEXT, 1, CONSTRUCTED, rpnStructure, 0); // rpnRpnOp
+        rpnStructure[offset++]=(byte)(0x80&0xff); // indefinite length
+        System.arraycopy(rpn1, 0, rpnStructure, offset, rpn1.length);
+        offset+=rpn1.length;
+        System.arraycopy(rpn2, 0, rpnStructure, offset, rpn2.length);
+        offset+=rpn2.length;
+        System.arraycopy(op, 0, rpnStructure, offset, op.length);
+        offset+=op.length;
+        rpnStructure[offset++]=0x00; // end rpnRpnOp
+        rpnStructure[offset++]=0x00;
+        return rpnStructure;
+    }
+
+    // represents the operation for PQF: overridden for CQLProxNode
+    String opPQF() { return op(); }
+
+    abstract String op();
+    abstract byte[] opType1();
+}
diff --git a/src/org/z3950/zing/ralph/CQLNotNode.java b/src/org/z3950/zing/ralph/CQLNotNode.java
new file mode 100644 (file)
index 0000000..c564509
--- /dev/null
@@ -0,0 +1,33 @@
+// $Id: CQLNotNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+
+
+/**
+ * Represents a NOT node in a CQL parse-tree.
+ *
+ * @version    $Id: CQLNotNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public class CQLNotNode extends CQLBooleanNode {
+    /**
+     * Creates a new NOT node with the specified left- and right-hand sides.
+     */
+    public CQLNotNode(CQLNode left, CQLNode right) {
+       this.left = left;
+       this.right = right;
+    }
+
+    String op() {
+       return "not";
+    }
+    
+    byte[] opType1() {
+        byte[] op=new byte[5];
+        putTag(CONTEXT, 46, CONSTRUCTED, op, 0); // Operator
+        putLen(2, op, 2);
+        putTag(CONTEXT, 2, PRIMITIVE, op, 3); // and-not
+        putLen(0, op, 4);
+        return op;
+    }
+    
+}
diff --git a/src/org/z3950/zing/ralph/CQLOrNode.java b/src/org/z3950/zing/ralph/CQLOrNode.java
new file mode 100644 (file)
index 0000000..e304210
--- /dev/null
@@ -0,0 +1,33 @@
+// $Id: CQLOrNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+
+
+/**
+ * Represents an OR node in a CQL parse-tree.
+ *
+ * @version    $Id: CQLOrNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public class CQLOrNode extends CQLBooleanNode {
+    /**
+     * Creates a new OR node with the specified left- and right-hand sides.
+     */
+    public CQLOrNode(CQLNode left, CQLNode right) {
+       this.left = left;
+       this.right = right;
+    }
+
+    String op() {
+       return "or";
+    }
+    
+    byte[] opType1() {
+        byte[] op=new byte[5];
+        putTag(CONTEXT, 46, CONSTRUCTED, op, 0); // Operator
+        putLen(2, op, 2);
+        putTag(CONTEXT, 1, PRIMITIVE, op, 3); // or
+        putLen(0, op, 4);
+        return op;
+    }
+    
+}
diff --git a/src/org/z3950/zing/ralph/CQLPrefix.java b/src/org/z3950/zing/ralph/CQLPrefix.java
new file mode 100644 (file)
index 0000000..a4bcf7c
--- /dev/null
@@ -0,0 +1,34 @@
+// $Id: CQLPrefix.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+import java.lang.String;
+
+/**
+ * Represents a CQL prefix mapping from short name to long identifier.
+ *
+ * @version    $Id: CQLPrefix.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public class CQLPrefix {
+    /**
+     * The short name of the prefix mapping.  That is, the prefix
+     * itself, such as <TT>dc</TT>, as it might be used in a qualifier
+     * like <TT>dc.title</TT>.
+     */
+    public String name;
+
+    /**
+     * The full identifier name of the prefix mapping.  That is,
+     * typically, a URI permanently allocated to a specific qualifier
+     * set, such as <TT>http://zthes.z3950.org/cql/1.0<TT>.
+     */
+    public String identifier;
+
+    /**
+     * Creates a new CQLPrefix mapping, which maps the specified name
+     * to the specified identifier.
+     */
+    CQLPrefix(String name, String identifier) {
+       this.name = name;
+       this.identifier = identifier;
+    }
+}
diff --git a/src/org/z3950/zing/ralph/CQLPrefixNode.java b/src/org/z3950/zing/ralph/CQLPrefixNode.java
new file mode 100644 (file)
index 0000000..525378a
--- /dev/null
@@ -0,0 +1,72 @@
+// $Id: CQLPrefixNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+import java.lang.String;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * Represents a prefix node in a CQL parse-tree.
+ *
+ * @version    $Id: CQLPrefixNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public class CQLPrefixNode extends CQLNode {
+    /**
+     * The prefix definition that governs the subtree.
+     */
+    public CQLPrefix prefix;
+
+    /**
+     * The root of a parse-tree representing the part of the query
+     * that is governed by this prefix definition.
+     */ 
+    public CQLNode subtree;
+
+    /**
+     * Creates a new CQLPrefixNode inducing a mapping from the
+     * specified qualifier-set name to the specified identifier across
+     * the specified subtree.
+     */
+    public CQLPrefixNode(String name, String identifier, CQLNode subtree) {
+       this.prefix = new CQLPrefix(name, identifier);
+       this.subtree = subtree;
+    }
+
+    public String toXCQL(int level, Vector prefixes) {
+//     String maybeName = "";
+//     if (prefix.name != null)
+//         maybeName = indent(level+1) + "<name>" + prefix.name + "</name>\n";
+//
+//     return (indent(level) + "<prefix>\n" + maybeName +
+//             indent(level+1) +
+//                 "<identifier>" + prefix.identifier + "</identifier>\n" +
+//             subtree.toXCQL(level+1, prefixes) +
+//             indent(level) + "</prefix>\n");
+       Vector tmp = new Vector(prefixes);
+       tmp.add(prefix);
+       return subtree.toXCQL(level, tmp);
+    }
+
+    public String toCQL() {
+       // ### We don't always need parens around the operand
+       return ">" + prefix.name + "=\"" + prefix.identifier + "\" " +
+           "(" + subtree.toCQL() + ")";
+    }
+
+    public String toPQF(Properties config) throws PQFTranslationException {
+       // Prefixes and their identifiers don't actually play any role
+       // in PQF translation, since the meanings of the qualifiers,
+       // including their prefixes if any, are instead wired into
+       // `config'.
+       return subtree.toPQF(config);
+    }
+
+    public byte[] toType1(Properties config) throws PQFTranslationException {
+       // Prefixes and their identifiers don't actually play any role
+       // in PQF translation, since the meanings of the qualifiers,
+       // including their prefixes if any, are instead wired into
+       // `config'.
+       return subtree.toType1(config);
+    }
+}
diff --git a/src/org/z3950/zing/ralph/CQLProxNode.java b/src/org/z3950/zing/ralph/CQLProxNode.java
new file mode 100644 (file)
index 0000000..b4c6fa8
--- /dev/null
@@ -0,0 +1,184 @@
+// $Id: CQLProxNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+import java.util.Vector;
+
+
+/**
+ * Represents a proximity node in a CQL parse-tree.
+ * The left- and right-hand-sides must be satisfied by parts of the
+ * candidate records which are sufficiently close to each other, as
+ * specified by a set of proximity parameters.
+ *
+ * @version    $Id: CQLProxNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public class CQLProxNode extends CQLBooleanNode {
+    ModifierSet ms;
+
+    /**
+     * Creates a new, <I>incomplete</I>, proximity node with the
+     * specified left-hand side.  No right-hand side is specified at
+     * this stage: that must be specified later, using the
+     * <TT>addSecondSubterm()</TT> method.  (That may seem odd, but
+     * it's just easier to write the parser that way.)
+     * <P>
+     * Proximity paramaters may be added at any time, before or after
+     * the right-hand-side sub-tree is added.
+     */
+    public CQLProxNode(CQLNode left) {
+       ms = new ModifierSet("prox");
+       this.left = left;
+       // this.right left unresolved for now ...
+    }
+
+    /**
+     * Sets the right-hand side of the proximity node whose
+     * left-hand-side was specified at creation time.
+     */
+    public void addSecondSubterm(CQLNode right) {
+       this.right = right;
+    }
+
+    /**
+     * Adds a modifier of the specified <TT>type</TT> and
+     * <TT>value</TT> to a proximity node.  Valid types are
+     * <TT>relation</TT>, <TT>distance</TT>, <TT>unit</TT> and
+     * <TT>ordering</TT>.
+     * <P>
+     * For information on the semantics of these paramaters, see
+     * <A href="http://zing.z3950.org/cql/intro.html#3.1"
+     * >section 3.1 (Proximity)</A> of
+     * <I>A Gentle Introduction to CQL</I></A>.
+     */
+    public void addModifier(String type, String value) {
+       ms.addModifier(type, value);
+    }
+
+    /**
+     * Returns an array of the modifiers associated with a proximity
+     * node.
+     * @return
+     * An array of modifiers, each represented by a two-element
+     * <TT>Vector</TT>, in which element 0 is the modifier type
+     * (e.g. <TT>distance</TT> or <TT>ordering</TT>) and element 1 is
+     * the associated value (e.g. <TT>3</TT> or <TT>unordered</TT>).
+     */
+    public Vector[] getModifiers() {
+       return ms.getModifiers();
+    }
+
+    String op() {
+       return ms.toCQL();
+    }
+
+    String opXCQL(int level) {
+       return ms.toXCQL(level, "boolean");
+    }
+
+    /*
+     * proximity ::= exclusion distance ordered relation which-code unit-code.
+     * exclusion ::= '1' | '0' | 'void'.
+     * distance ::= integer.
+     * ordered ::= '1' | '0'.
+     * relation ::= integer.
+     * which-code ::= 'known' | 'private' | integer.
+     * unit-code ::= integer.
+     */
+    String opPQF() {
+       int relCode = getRelCode();
+
+       int unitCode = getProxUnitCode();
+
+       String res = "prox " +
+           "0 " +
+           ms.modifier("distance") + " " +
+           (ms.modifier("ordering").equals("ordered") ? 1 : 0) + " " +
+           relCode + " " +
+           "1 " +
+           unitCode;
+       
+       return res;
+    }
+
+    private int getRelCode() {
+       String rel = ms.modifier("relation");
+       if (rel.equals("<")) {
+           return 1;
+       } else if (rel.equals("<=")) {
+           return 2;
+       } else if (rel.equals("=")) {
+           return 3;
+       } else if (rel.equals(">=")) {
+           return 4;
+       } else if (rel.equals(">")) {
+           return 5;
+       } else if (rel.equals("<>")) {
+           return 6;
+       }
+        return 0;
+    }
+    
+    private int getProxUnitCode() {
+       String unit = ms.modifier("unit");
+       if (unit.equals("word")) {
+           return 2;
+       } else if (unit.equals("sentence")) {
+           return 3;
+       } else if (unit.equals("paragraph")) {
+           return 4;
+       } else if (unit.equals("element")) {
+           return 8;
+       }
+        return 0;
+    }
+    
+    
+    byte[] opType1() {
+        byte[] op=new byte[100];
+        int    offset, value;
+        offset=putTag(CONTEXT, 46, CONSTRUCTED, op, 0); // Operator
+        op[offset++]=(byte)(0x80&0xff); // indefinite length
+
+        offset=putTag(CONTEXT, 3, CONSTRUCTED, op, offset); // prox
+        op[offset++]=(byte)(0x80&0xff); // indefinite length
+
+        offset=putTag(CONTEXT, 1, PRIMITIVE, op, offset); // exclusion
+        value=0; // value=0=false
+        offset=putLen(numLen(value), op, offset);
+        offset=putNum(value, op, offset);
+
+        offset=putTag(CONTEXT, 2, PRIMITIVE, op, offset); // distance
+        value=Integer.parseInt(ms.modifier("distance"));
+        offset=putLen(numLen(value), op, offset);
+        offset=putNum(value, op, offset);
+        
+        offset=putTag(CONTEXT, 3, PRIMITIVE, op, offset); // ordered
+        value=ms.modifier("ordering").equals("ordered") ? 1 : 0;
+        offset=putLen(numLen(value), op, offset);
+        offset=putNum(value, op, offset);
+        
+        offset=putTag(CONTEXT, 4, PRIMITIVE, op, offset); // relationType
+        value=getRelCode();
+        offset=putLen(numLen(value), op, offset);
+        offset=putNum(value, op, offset);
+        
+        offset=putTag(CONTEXT, 5, CONSTRUCTED, op, offset); // proximityUnitCode
+        op[offset++]=(byte)(0x80&0xff); // indefinite length
+        offset=putTag(CONTEXT, 1, PRIMITIVE, op, offset); // known
+        value=getProxUnitCode();
+        offset=putLen(numLen(value), op, offset);
+        offset=putNum(value, op, offset);
+        op[offset++]=0x00; // end of proximityUnitCode
+        op[offset++]=0x00;
+        
+        op[offset++]=0x00; // end of prox
+        op[offset++]=0x00;
+        op[offset++]=0x00; // end of Operator
+        op[offset++]=0x00;
+        
+        byte[] o=new byte[offset];
+        System.arraycopy(op, 0, o, 0, offset);
+        return o;
+    }
+    
+}
diff --git a/src/org/z3950/zing/ralph/CQLTermNode.java b/src/org/z3950/zing/ralph/CQLTermNode.java
new file mode 100644 (file)
index 0000000..8d972ea
--- /dev/null
@@ -0,0 +1,224 @@
+// $Id: CQLTermNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+
+package org.z3950.zing.cql;
+import java.util.Properties;
+import java.util.Vector;
+
+
+/**
+ * Represents a terminal node in a CQL parse-tree.
+ * A term node consists of the term String itself, together with,
+ * optionally, a qualifier string and a relation.  Neither or both of
+ * these must be provided - you can't have a qualifier without a
+ * relation or vice versa.
+ *
+ * @version    $Id: CQLTermNode.java,v 1.1 2002-12-04 16:54:01 mike Exp $
+ */
+public class CQLTermNode extends CQLNode {
+    private String qualifier;
+    private CQLRelation relation;
+    private String term;
+
+    /**
+     * Creates a new term node with the specified <TT>qualifier</TT>,
+     * <TT>relation</TT> and <TT>term</TT>.  The first two may be
+     * <TT>null</TT>, but the <TT>term</TT> may not.
+     */
+    public CQLTermNode(String qualifier, CQLRelation relation, String term) {
+       this.qualifier = qualifier;
+       this.relation = relation;
+       this.term = term;
+    }
+
+    public String getQualifier() { return qualifier; }
+    public CQLRelation getRelation() { return relation; }
+    public String getTerm() { return term; }
+
+    public String toXCQL(int level, Vector prefixes) {
+       return (indent(level) + "<searchClause>\n" +
+               renderPrefixes(level+1, prefixes) +
+               indent(level+1) + "<index>" + xq(qualifier) + "</index>\n" +
+               relation.toXCQL(level+1, new Vector()) +
+               indent(level+1) + "<term>" + xq(term) + "</term>\n" +
+               indent(level) + "</searchClause>\n");
+    }
+
+    public String toCQL() {
+       String quotedQualifier = maybeQuote(qualifier);
+       String quotedTerm = maybeQuote(term);
+       String res = quotedTerm;
+
+       if (!qualifier.equalsIgnoreCase("srw.serverChoice")) {
+           // ### We don't always need spaces around `relation'.
+           res = quotedQualifier + " " + relation.toCQL() + " " + quotedTerm;
+       }
+
+       return res;
+    }
+
+    
+    private Vector getAttrs(Properties config) throws PQFTranslationException {
+       Vector attrs = new Vector();
+
+       String attr;
+       attr = config.getProperty("qualifier." + qualifier);
+       if (attr == null)
+           throw new UnknownQualifierException(qualifier);
+       attrs.add(attr);
+
+       String rel = relation.getBase();
+       if (rel.equals("=")) {
+           rel = "eq";
+       } else if (rel.equals("<=")) {
+           rel = "le";
+       } else if (rel.equals(">=")) {
+           rel = "ge";
+       }
+       // ### Handling "any" and "all" properly would involve breaking
+       // the string down into a bunch of individual words and ORring
+       // or ANDing them together.  Another day.
+       attr = config.getProperty("relation." + rel);
+       if (attr == null)
+           throw new UnknownRelationException(rel);
+       attrs.add(attr);
+
+       String[] mods = relation.getModifiers();
+       for (int i = 0; i < mods.length; i++) {
+           attr = config.getProperty("relationModifier." + mods[i]);
+           if (attr == null)
+               throw new UnknownRelationModifierException(mods[i]);
+           attrs.add(attr);
+       }
+
+       String pos = "unanchored";
+       String text = term;
+       if (text.length() > 0 && text.substring(0, 1).equals("^")) {
+           text = text.substring(1);
+           pos = "anchored";
+       }
+       attr = config.getProperty("position." + pos);
+       if (attr == null)
+           throw new UnknownPositionException(pos);
+       attrs.add(attr);
+
+       attr = config.getProperty("structure." + rel);
+       if (attr == null)
+           attr = config.getProperty("structure.*");
+       attrs.add(attr);
+
+       attr = config.getProperty("always");
+       if (attr != null)
+           attrs.add(attr);
+        return attrs;
+    }
+
+    public String toPQF(Properties config) throws PQFTranslationException {
+        Vector attrs=getAttrs(config);
+        
+        String attr, s = "";
+       for (int i = 0; i < attrs.size(); i++) {
+           attr = (String) attrs.get(i);
+           s += "@attr " + Utils.replaceString(attr, " ", " @attr ") + " ";
+       }
+
+       String text = term;
+       if (text.length() > 0 && text.substring(0, 1).equals("^"))
+           text = text.substring(1);
+       return s + maybeQuote(text);
+    }
+
+    static String maybeQuote(String str) {
+       // There _must_ be a better way to make this test ...
+       if (str.length() == 0 ||
+           str.indexOf('"') != -1 ||
+           str.indexOf(' ') != -1 ||
+           str.indexOf('\t') != -1 ||
+           str.indexOf('=') != -1 ||
+           str.indexOf('<') != -1 ||
+           str.indexOf('>') != -1 ||
+           str.indexOf('/') != -1 ||
+           str.indexOf('(') != -1 ||
+           str.indexOf(')') != -1) {
+           str = '"' + Utils.replaceString(str, "\"", "\\\"") + '"';
+       }
+
+       return str;
+    }
+    
+    /** Renders a parse-tree into a Yaz-style PQF string.
+     * PQF, or Prefix Query Format, is a cryptic but powerful notation
+     * that can be trivially mapped, one-to-one, int Z39.50 Type-1 and
+     * Type-101 queries.  A specification for the format can be found
+     * in
+     * <A href="http://indexdata.dk/yaz/doc/tools.php#PQF"
+     *         >Chapter 7 (Supporting Tools)</A> of the
+     * <A href="http://indexdata.dk/yaz/">YAZ</A> manual.
+     * <P>
+     * @param config
+     *         A <TT>Properties</TT> object containing configuration
+     *         information that specifies the mapping from CQL qualifiers,
+     *         relations, etc. to Type-1 attributes.  The mapping
+     *         specification is described in the cql-java distribution's
+     *         sample PQF-mapping configuration file,
+     *         <TT>etc/pqf.properties</TT>, which see.
+     * @return
+     *         A String containing a PQF query equivalent to the parse-tree
+     *         whose root is this node.  This may be fed into the tool of
+     *         your choice to obtain a BER-encoded packet.
+     */
+    public byte[] toType1(Properties config) throws PQFTranslationException {
+       String text = term;
+       if (text.length() > 0 && text.substring(0, 1).equals("^"))
+           text = text.substring(1);
+        String attr, attrList, term=maybeQuote(text);
+        System.out.println("in CQLTermNode.toType101(): PQF="+toPQF(config));
+        byte[] operand=new byte[text.length()+100];
+        int    i, j, offset, type, value;
+        offset=putTag(CONTEXT, 0, CONSTRUCTED, operand, 0); // op
+        operand[offset++]=(byte)(0x80&0xff); // indefinite length
+        offset=putTag(CONTEXT, 102, CONSTRUCTED, operand, offset); // AttributesPlusTerm
+        operand[offset++]=(byte)(0x80&0xff); // indefinite length
+        offset=putTag(CONTEXT, 44, CONSTRUCTED, operand, offset); // AttributeList
+        operand[offset++]=(byte)(0x80&0xff); // indefinite length
+        offset=putTag(UNIVERSAL, SEQUENCE, CONSTRUCTED, operand, offset);
+        operand[offset++]=(byte)(0x80&0xff);
+
+        Vector attrs=getAttrs(config);
+       for(i = 0; i < attrs.size(); i++) {
+           attrList = (String) attrs.get(i);
+            java.util.StringTokenizer st=new java.util.StringTokenizer(attrList);
+            while(st.hasMoreTokens()) {
+                attr=st.nextToken();
+                j=attr.indexOf('=');
+                offset=putTag(CONTEXT, 120, PRIMITIVE, operand, offset);
+                type=Integer.parseInt(attr.substring(0, j));
+                offset=putLen(numLen(type), operand, offset);
+                offset=putNum(type, operand, offset);
+                
+                offset=putTag(CONTEXT, 121, PRIMITIVE, operand, offset);
+                value=Integer.parseInt(attr.substring(j+1));
+                offset=putLen(numLen(value), operand, offset);
+                offset=putNum(value, operand, offset);
+            }
+       }
+        operand[offset++]=0x00; // end of SEQUENCE
+        operand[offset++]=0x00;
+        operand[offset++]=0x00; // end of AttributeList
+        operand[offset++]=0x00;
+        
+        offset=putTag(CONTEXT, 45, PRIMITIVE, operand, offset); // general Term
+        byte[] t=term.getBytes();
+        offset=putLen(t.length, operand, offset);
+        System.arraycopy(t, 0, operand, offset, t.length);
+        offset+=t.length;
+        
+        operand[offset++]=0x00; // end of AttributesPlusTerm
+        operand[offset++]=0x00;
+        operand[offset++]=0x00; // end of Operand
+        operand[offset++]=0x00;
+        byte[] o=new byte[offset];
+        System.arraycopy(operand, 0, o, 0, offset);
+        return o;
+    }
+    
+}