Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents a structured query, and can be 
4
 *             constructed from an XML serialization conforming to 
5
 *             pathquery.dtd. The printSQL() method can be used to print 
6
 *             a SQL serialization of the query.
7
 *  Copyright: 2000 Regents of the University of California and the
8
 *             National Center for Ecological Analysis and Synthesis
9
 *    Authors: Matt Jones
10
 *    Release: @release@
11
 *
12
 *   '$Author: jones $'
13
 *     '$Date: 2001-01-18 11:52:00 -0800 (Thu, 18 Jan 2001) $'
14
 * '$Revision: 669 $'
15
 *
16
 * This program is free software; you can redistribute it and/or modify
17
 * it under the terms of the GNU General Public License as published by
18
 * the Free Software Foundation; either version 2 of the License, or
19
 * (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
29
 */
30

    
31
package edu.ucsb.nceas.metacat;
32

    
33
import java.io.*;
34
import java.util.Stack;
35
import java.util.Vector;
36
import java.util.Enumeration;
37

    
38
import org.xml.sax.Attributes;
39
import org.xml.sax.InputSource;
40
import org.xml.sax.SAXException;
41
import org.xml.sax.SAXParseException;
42
import org.xml.sax.XMLReader;
43
import org.xml.sax.helpers.XMLReaderFactory;
44
import org.xml.sax.helpers.DefaultHandler;
45

    
46
/**
47
 * A Class that represents a structured query, and can be 
48
 * constructed from an XML serialization conforming to @see pathquery.dtd. 
49
 * The printSQL() method can be used to print a SQL serialization of the query.
50
 */
51
public class QuerySpecification extends DefaultHandler {
52
 
53
  private boolean containsExtendedSQL=false;
54
 
55
  // Query data structures
56
  private String meta_file_id;
57
  private String querytitle;
58
  private Vector doctypeList;
59
  private Vector returnFieldList;
60
  private Vector ownerList;
61
  private Vector siteList;
62
  private QueryGroup query = null;
63

    
64
  private Stack elementStack;
65
  private Stack queryStack;
66
  private String currentValue;
67
  private String currentPathexpr;
68
  private String parserName = null;
69
  private String accNumberSeparator = null;
70

    
71
  /**
72
   * construct an instance of the QuerySpecification class 
73
   *
74
   * @param queryspec the XML representation of the query (should conform
75
   *                  to pathquery.dtd) as a Reader
76
   * @param parserName the fully qualified name of a Java Class implementing
77
   *                  the org.xml.sax.XMLReader interface
78
   */
79
  public QuerySpecification( Reader queryspec, String parserName,
80
         String accNumberSeparator ) throws IOException {
81
    super();
82
    
83
    // Initialize the class variables
84
    doctypeList = new Vector();
85
    elementStack = new Stack();
86
    queryStack   = new Stack();
87
    returnFieldList = new Vector();
88
    ownerList = new Vector();
89
    siteList = new Vector();
90
    this.parserName = parserName;
91
    this.accNumberSeparator = accNumberSeparator;
92

    
93
    // Initialize the parser and read the queryspec
94
    XMLReader parser = initializeParser();
95
    if (parser == null) {
96
      System.err.println("SAX parser not instantiated properly.");
97
    }
98
    try {
99
      parser.parse(new InputSource(queryspec));
100
    } catch (SAXException e) {
101
      System.err.println("error parsing data");
102
      System.err.println(e.getMessage());
103
    }
104
  }
105

    
106
  /**
107
   * construct an instance of the QuerySpecification class 
108
   *
109
   * @param queryspec the XML representation of the query (should conform
110
   *                  to pathquery.dtd) as a String
111
   * @param parserName the fully qualified name of a Java Class implementing
112
   *                  the org.xml.sax.Parser interface
113
   */
114
  public QuerySpecification( String queryspec, String parserName,
115
         String accNumberSeparator) throws IOException {
116
    this(new StringReader(queryspec), parserName, accNumberSeparator);
117
  }
118

    
119
  /** Main routine for testing */
120
  static public void main(String[] args) {
121

    
122
     if (args.length < 1) {
123
       System.err.println("Wrong number of arguments!!!");
124
       System.err.println("USAGE: java QuerySpecification <xmlfile>");
125
       return;
126
     } else {
127
       String xmlfile  = args[0];
128
        
129
       try {
130
         MetaCatUtil util = new MetaCatUtil();
131
         FileReader xml = new FileReader(new File(xmlfile));
132
         QuerySpecification qspec = 
133
                 new QuerySpecification(xml, util.getOption("saxparser"),
134
                                        util.getOption("accNumberSeparator"));
135
         System.out.println(qspec.printSQL());
136

    
137
       } catch (IOException e) {
138
         System.err.println(e.getMessage());
139
       }
140
         
141
     }
142
  }
143
  
144
  /**
145
   * Returns true if the parsed query contains and extended xml query 
146
   * (i.e. there is at least one &lt;returnfield&gt; in the pathquery document)
147
   */
148
  public boolean containsExtendedSQL()
149
  {
150
    if(containsExtendedSQL)
151
    {
152
      return true;
153
    }
154
    else
155
    {
156
      return false;
157
    }
158
  }
159
  
160
  /**
161
   * Accessor method to return a vector of the extended return fields as
162
   * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
163
   */
164
  public Vector getReturnFieldList()
165
  {
166
    return this.returnFieldList; 
167
  }
168

    
169
  /**
170
   * Set up the SAX parser for reading the XML serialized query
171
   */
172
  private XMLReader initializeParser() {
173
    XMLReader parser = null;
174

    
175
    // Set up the SAX document handlers for parsing
176
    try {
177

    
178
      // Get an instance of the parser
179
      parser = XMLReaderFactory.createXMLReader(parserName);
180

    
181
      // Set the ContentHandler to this instance
182
      parser.setContentHandler(this);
183

    
184
      // Set the error Handler to this instance
185
      parser.setErrorHandler(this);
186

    
187
    } catch (Exception e) {
188
       System.err.println(e.toString());
189
    }
190

    
191
    return parser;
192
  }
193

    
194
  /**
195
   * callback method used by the SAX Parser when the start tag of an 
196
   * element is detected. Used in this context to parse and store
197
   * the query information in class variables.
198
   */
199
  public void startElement (String uri, String localName, 
200
                            String qName, Attributes atts) 
201
         throws SAXException {
202
    BasicNode currentNode = new BasicNode(localName);
203
    // add attributes to BasicNode here
204
    if (atts != null) {
205
      int len = atts.getLength();
206
      for (int i = 0; i < len; i++) {
207
        currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i));
208
      }
209
    }
210

    
211
    elementStack.push(currentNode); 
212
    if (currentNode.getTagName().equals("querygroup")) {
213
      QueryGroup currentGroup = new QueryGroup(
214
                                currentNode.getAttribute("operator"));
215
      if (query == null) {
216
        query = currentGroup;
217
      } else {
218
        QueryGroup parentGroup = (QueryGroup)queryStack.peek();
219
        parentGroup.addChild(currentGroup);
220
      }
221
      queryStack.push(currentGroup);
222
    }
223
  }
224

    
225
  /**
226
   * callback method used by the SAX Parser when the end tag of an 
227
   * element is detected. Used in this context to parse and store
228
   * the query information in class variables.
229
   */
230
  public void endElement (String uri, String localName,
231
                          String qName) throws SAXException {
232
    BasicNode leaving = (BasicNode)elementStack.pop(); 
233
    if (leaving.getTagName().equals("queryterm")) {
234
      boolean isCaseSensitive = (new Boolean(
235
              leaving.getAttribute("casesensitive"))).booleanValue();
236
      QueryTerm currentTerm = null;
237
      if (currentPathexpr == null) {
238
        currentTerm = new QueryTerm(isCaseSensitive,
239
                      leaving.getAttribute("searchmode"),currentValue);
240
      } else {
241
        currentTerm = new QueryTerm(isCaseSensitive,
242
                      leaving.getAttribute("searchmode"),currentValue,
243
                      currentPathexpr);
244
      }
245
      QueryGroup currentGroup = (QueryGroup)queryStack.peek();
246
      currentGroup.addChild(currentTerm);
247
      currentValue = null;
248
      currentPathexpr = null;
249
    } else if (leaving.getTagName().equals("querygroup")) {
250
      QueryGroup leavingGroup = (QueryGroup)queryStack.pop();
251
    }
252
  }
253

    
254
  /**
255
   * callback method used by the SAX Parser when the text sequences of an 
256
   * xml stream are detected. Used in this context to parse and store
257
   * the query information in class variables.
258
   */
259
  public void characters(char ch[], int start, int length) {
260

    
261
    String inputString = new String(ch, start, length);
262
    BasicNode currentNode = (BasicNode)elementStack.peek(); 
263
    String currentTag = currentNode.getTagName();
264
    if (currentTag.equals("meta_file_id")) {
265
      meta_file_id = inputString;
266
    } else if (currentTag.equals("querytitle")) {
267
      querytitle = inputString;
268
    } else if (currentTag.equals("value")) {
269
      currentValue = inputString;
270
    } else if (currentTag.equals("pathexpr")) {
271
      currentPathexpr = inputString;
272
    } else if (currentTag.equals("returndoctype")) {
273
      doctypeList.add(inputString);
274
    } else if (currentTag.equals("returnfield")) {
275
      returnFieldList.add(inputString);
276
      containsExtendedSQL = true;
277
    } else if (currentTag.equals("owner")) {
278
      ownerList.add(inputString);
279
    } else if (currentTag.equals("site")) {
280
      siteList.add(inputString);
281
    }
282
  }
283

    
284

    
285
  /**
286
   * create a SQL serialization of the query that this instance represents
287
   */
288
  public String printSQL() {
289
    StringBuffer self = new StringBuffer();
290

    
291
    self.append("SELECT docid,docname,doctype,doctitle,");
292
    self.append("date_created, date_updated, rev ");
293
    self.append("FROM xml_documents WHERE docid IN (");
294

    
295
    // This determines the documents that meet the query conditions
296
    self.append(query.printSQL());
297

    
298
    self.append(") ");
299
 
300
    // Add SQL to filter for doctypes requested in the query
301
    // This is an implicit OR for the list of doctypes
302
    if (!doctypeList.isEmpty()) {
303
      boolean firstdoctype = true;
304
      self.append(" AND ("); 
305
      Enumeration en = doctypeList.elements();
306
      while (en.hasMoreElements()) {
307
        String currentDoctype = (String)en.nextElement();
308
        if (firstdoctype) {
309
           firstdoctype = false;
310
           self.append(" doctype = '" + currentDoctype + "'"); 
311
        } else {
312
          self.append(" OR doctype = '" + currentDoctype + "'"); 
313
        }
314
      }
315
      self.append(") ");
316
    }
317
    
318
    // Add SQL to filter for owners requested in the query
319
    // This is an implicit OR for the list of owners
320
    if (!ownerList.isEmpty()) {
321
      boolean first = true;
322
      self.append(" AND ("); 
323
      Enumeration en = ownerList.elements();
324
      while (en.hasMoreElements()) {
325
        String current = (String)en.nextElement();
326
        if (first) {
327
           first = false;
328
           self.append(" user_owner = '" + current + "'"); 
329
        } else {
330
          self.append(" OR user_owner = '" + current + "'"); 
331
        }
332
      }
333
      self.append(") ");
334
    }
335

    
336
    // Add SQL to filter for sites requested in the query
337
    // This is an implicit OR for the list of sites
338
    if (!siteList.isEmpty()) {
339
      boolean first = true;
340
      self.append(" AND ("); 
341
      Enumeration en = siteList.elements();
342
      while (en.hasMoreElements()) {
343
        String current = (String)en.nextElement();
344
        if (first) {
345
           first = false;
346
           self.append(" SUBSTR(docid, 1, INSTR(docid, '" +
347
               accNumberSeparator + "')-1) = '" + current + "'"); 
348
        } else {
349
          self.append(" OR SUBSTR(docid, 1, INSTR(docid, '" +
350
               accNumberSeparator + "')-1) = '" + current + "'"); 
351
        }
352
      }
353
      self.append(") ");
354
    }
355

    
356
    return self.toString();
357
  }
358
  
359
  /**
360
   * This method prints sql based upon the &lt;returnfield&gt; tag in the
361
   * pathquery document.  This allows for customization of the 
362
   * returned fields
363
   * @param doclist the list of document ids to search by
364
   */
365
  public String printExtendedSQL(String doclist)
366
  {  
367
    StringBuffer self = new StringBuffer();
368
    self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata ");
369
    self.append("from xml_index, xml_nodes where xml_index.nodeid=");
370
    self.append("xml_nodes.parentnodeid and (xml_index.path like '");
371
    boolean firstfield = true;
372
    //put the returnfields into the query
373
    //the for loop allows for multiple fields
374
    for(int i=0; i<returnFieldList.size(); i++)
375
    {
376
      if(firstfield)
377
      {
378
        firstfield = false;
379
        self.append((String)returnFieldList.elementAt(i));
380
        self.append("' ");
381
      }
382
      else
383
      {
384
        self.append("or xml_index.path like '");
385
        self.append((String)returnFieldList.elementAt(i));
386
        self.append("' ");
387
      }
388
    }
389
    self.append(") AND xml_nodes.docid in (");
390
    //self.append(query.printSQL());
391
    self.append(doclist);
392
    self.append(")");
393
    self.append(" AND xml_nodes.nodetype = 'TEXT'");
394

    
395
    //System.out.println(self.toString());
396
    return self.toString();
397
  }
398
  
399
  public static String printRelationSQL(String docid)
400
  {
401
    StringBuffer self = new StringBuffer();
402
    self.append("select subject, relationship, object, subdoctype, ");
403
    self.append("objdoctype from xml_relation ");
404
    self.append("where subject like '").append(docid).append("'");
405
    return self.toString();
406
  }
407
   
408
  /**
409
   * Prints sql that returns all relations in the database.
410
   */
411
  public static String printPackageSQL()
412
  {
413
    StringBuffer self = new StringBuffer();
414
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
415
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
416
    self.append("'package/relation/subject') s, (select nodeid, parentnodeid ");
417
    self.append("from xml_index where path like ");
418
    self.append("'package/relation/relationship') rel, ");
419
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
420
    self.append("'package/relation/object') o, ");
421
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
422
    self.append("where s.parentnodeid = rel.parentnodeid ");
423
    self.append("and rel.parentnodeid = o.parentnodeid ");
424
    self.append("and x.parentnodeid in rel.nodeid ");
425
    self.append("and y.parentnodeid in o.nodeid ");
426
    self.append("and z.parentnodeid in s.nodeid ");
427
    //self.append("and z.nodedata like '%");
428
    //self.append(docid);
429
    //self.append("%'");
430
    return self.toString();
431
  }
432
  
433
  /**
434
   * Prints sql that returns all relations in the database that were input
435
   * under a specific docid
436
   * @param docid the docid to search for.
437
   */
438
  public static String printPackageSQL(String docid)
439
  {
440
    StringBuffer self = new StringBuffer();
441
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
442
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
443
    self.append("'package/relation/subject') s, (select nodeid, parentnodeid ");
444
    self.append("from xml_index where path like ");
445
    self.append("'package/relation/relationship') rel, ");
446
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
447
    self.append("'package/relation/object') o, ");
448
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
449
    self.append("where s.parentnodeid = rel.parentnodeid ");
450
    self.append("and rel.parentnodeid = o.parentnodeid ");
451
    self.append("and x.parentnodeid in rel.nodeid ");
452
    self.append("and y.parentnodeid in o.nodeid ");
453
    self.append("and z.parentnodeid in s.nodeid ");
454
    self.append("and z.docid like '").append(docid).append("'");
455
    
456
    return self.toString();
457
  }
458
  
459
  /**
460
   * Returns all of the relations that has a certain docid in the subject
461
   * or the object.
462
   * 
463
   * @param docid the docid to search for
464
   */
465
  public static String printPackageSQL(String subDocidURL, String objDocidURL)
466
  {
467
    StringBuffer self = new StringBuffer();
468
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
469
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
470
    self.append("'package/relation/subject') s, (select nodeid, parentnodeid ");
471
    self.append("from xml_index where path like ");
472
    self.append("'package/relation/relationship') rel, ");
473
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
474
    self.append("'package/relation/object') o, ");
475
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
476
    self.append("where s.parentnodeid = rel.parentnodeid ");
477
    self.append("and rel.parentnodeid = o.parentnodeid ");
478
    self.append("and x.parentnodeid in rel.nodeid ");
479
    self.append("and y.parentnodeid in o.nodeid ");
480
    self.append("and z.parentnodeid in s.nodeid ");
481
    self.append("and (z.nodedata like '");
482
    self.append(subDocidURL);
483
    self.append("' or y.nodedata like '");
484
    self.append(objDocidURL);
485
    self.append("')");
486
    return self.toString();
487
  }
488
  
489
  public static String printGetDocByDoctypeSQL(String docid)
490
  {
491
    StringBuffer self = new StringBuffer();
492

    
493
    self.append("SELECT docid,docname,doctype,doctitle,");
494
    self.append("date_created, date_updated ");
495
    self.append("FROM xml_documents WHERE docid IN (");
496
    self.append(docid).append(")");
497
    return self.toString();
498
  }
499
  
500
  /**
501
   * create a String description of the query that this instance represents.
502
   * This should become a way to get the XML serialization of the query.
503
   */
504
  public String toString() {
505
    return "meta_file_id=" + meta_file_id + "\n" + 
506
           "querytitle=" + querytitle + "\n" + query;
507
  }
508

    
509
  /** a utility class that represents a group of terms in a query */
510
  private class QueryGroup {
511
    private String operator = null;  // indicates how query terms are combined
512
    private Vector children = null;  // the list of query terms and groups
513

    
514
    /** 
515
     * construct a new QueryGroup 
516
     *
517
     * @param operator the boolean conector used to connect query terms 
518
     *                    in this query group
519
     */
520
    public QueryGroup(String operator) {
521
      this.operator = operator;
522
      children = new Vector();
523
    }
524

    
525
    /** 
526
     * Add a child QueryGroup to this QueryGroup
527
     *
528
     * @param qgroup the query group to be added to the list of terms
529
     */
530
    public void addChild(QueryGroup qgroup) {
531
      children.add((Object)qgroup); 
532
    }
533

    
534
    /**
535
     * Add a child QueryTerm to this QueryGroup
536
     *
537
     * @param qterm the query term to be added to the list of terms
538
     */
539
    public void addChild(QueryTerm qterm) {
540
      children.add((Object)qterm); 
541
    }
542

    
543
    /**
544
     * Retrieve an Enumeration of query terms for this QueryGroup
545
     */
546
    public Enumeration getChildren() {
547
      return children.elements();
548
    }
549
   
550
    /**
551
     * create a SQL serialization of the query that this instance represents
552
     */
553
    public String printSQL() {
554
      StringBuffer self = new StringBuffer();
555
      boolean first = true;
556

    
557
      self.append("(");
558

    
559
      Enumeration en= getChildren();
560
      while (en.hasMoreElements()) {
561
        Object qobject = en.nextElement();
562
        if (first) {
563
          first = false;
564
        } else {
565
          self.append(" " + operator + " ");
566
        }
567
        if (qobject instanceof QueryGroup) {
568
          QueryGroup qg = (QueryGroup)qobject;
569
          self.append(qg.printSQL());
570
        } else if (qobject instanceof QueryTerm) {
571
          QueryTerm qt = (QueryTerm)qobject;
572
          self.append(qt.printSQL());
573
        } else {
574
          System.err.println("qobject wrong type: fatal error");
575
        }
576
      }
577
      self.append(") \n");
578
      return self.toString();
579
    }
580

    
581
    /**
582
     * create a String description of the query that this instance represents.
583
     * This should become a way to get the XML serialization of the query.
584
     */
585
    public String toString() {
586
      StringBuffer self = new StringBuffer();
587

    
588
      self.append("  (Query group operator=" + operator + "\n");
589
      Enumeration en= getChildren();
590
      while (en.hasMoreElements()) {
591
        Object qobject = en.nextElement();
592
        self.append(qobject);
593
      }
594
      self.append("  )\n");
595
      return self.toString();
596
    }
597
  }
598

    
599
  /** a utility class that represents a single term in a query */
600
  private class QueryTerm {
601
    private boolean casesensitive = false;
602
    private String searchmode = null;
603
    private String value = null;
604
    private String pathexpr = null;
605

    
606
    /**
607
     * Construct a new instance of a query term for a free text search
608
     * (using the value only)
609
     *
610
     * @param casesensitive flag indicating whether case is used to match
611
     * @param searchmode determines what kind of substring match is performed
612
     *        (one of starts-with|ends-with|contains|matches-exactly)
613
     * @param value the text value to match
614
     */
615
    public QueryTerm(boolean casesensitive, String searchmode, 
616
                     String value) {
617
      this.casesensitive = casesensitive;
618
      this.searchmode = searchmode;
619
      this.value = value;
620
    }
621

    
622
    /**
623
     * Construct a new instance of a query term for a structured search
624
     * (matching the value only for those nodes in the pathexpr)
625
     *
626
     * @param casesensitive flag indicating whether case is used to match
627
     * @param searchmode determines what kind of substring match is performed
628
     *        (one of starts-with|ends-with|contains|matches-exactly)
629
     * @param value the text value to match
630
     * @param pathexpr the hierarchical path to the nodes to be searched
631
     */
632
    public QueryTerm(boolean casesensitive, String searchmode, 
633
                     String value, String pathexpr) {
634
      this(casesensitive, searchmode, value);
635
      this.pathexpr = pathexpr;
636
    }
637

    
638
    /** determine if the QueryTerm is case sensitive */
639
    public boolean isCaseSensitive() {
640
      return casesensitive;
641
    }
642

    
643
    /** get the searchmode parameter */
644
    public String getSearchMode() {
645
      return searchmode;
646
    }
647
 
648
    /** get the Value parameter */
649
    public String getValue() {
650
      return value;
651
    }
652

    
653
    /** get the path expression parameter */
654
    public String getPathExpression() {
655
      return pathexpr;
656
    }
657

    
658
    /**
659
     * create a SQL serialization of the query that this instance represents
660
     */
661
    public String printSQL() {
662
      StringBuffer self = new StringBuffer();
663

    
664
      // Uppercase the search string if case match is not important
665
      String casevalue = null;
666
      String nodedataterm = null;
667

    
668
      if (casesensitive) {
669
        nodedataterm = "nodedata";
670
        casevalue = value;
671
      } else {
672
        nodedataterm = "UPPER(nodedata)";
673
        casevalue = value.toUpperCase();
674
      }
675

    
676
      // Add appropriate wildcards to search string
677
      String searchvalue = null;
678
      if (searchmode.equals("starts-with")) {
679
        searchvalue = casevalue + "%";
680
      } else if (searchmode.equals("ends-with")) {
681
        searchvalue = "%" + casevalue;
682
      } else if (searchmode.equals("contains")) {
683
        searchvalue = "%" + casevalue + "%";
684
      } else {
685
        searchvalue = casevalue;
686
      }
687

    
688
      self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n");
689

    
690
      if (pathexpr != null) {
691
        self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
692
        self.append("AND parentnodeid IN ");
693
        self.append("(SELECT nodeid FROM xml_index WHERE path LIKE " + 
694
                    "'" +  pathexpr + "') " );
695
      } else {
696
        self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
697
      }
698

    
699
      return self.toString();
700
    }
701

    
702
    /**
703
     * create a String description of the query that this instance represents.
704
     * This should become a way to get the XML serialization of the query.
705
     */
706
    public String toString() {
707

    
708
      return this.printSQL();
709
    }
710
  }
711
}
(39-39/43)