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: bojilova $'
13
 *     '$Date: 2001-02-23 14:14:30 -0800 (Fri, 23 Feb 2001) $'
14
 * '$Revision: 706 $'
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
// DOCTITLE attr cleared from the db
58
//  private String querytitle;
59
  private Vector doctypeList;
60
  private Vector returnFieldList;
61
  private Vector ownerList;
62
  private Vector siteList;
63
  private QueryGroup query = null;
64

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

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

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

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

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

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

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

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

    
178
    // Set up the SAX document handlers for parsing
179
    try {
180

    
181
      // Get an instance of the parser
182
      parser = XMLReaderFactory.createXMLReader(parserName);
183

    
184
      // Set the ContentHandler to this instance
185
      parser.setContentHandler(this);
186

    
187
      // Set the error Handler to this instance
188
      parser.setErrorHandler(this);
189

    
190
    } catch (Exception e) {
191
       System.err.println("Error in QuerySpcecification.initializeParser " + 
192
                           e.toString());
193
    }
194

    
195
    return parser;
196
  }
197

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

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

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

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

    
265
    String inputString = new String(ch, start, length);
266
    BasicNode currentNode = (BasicNode)elementStack.peek(); 
267
    String currentTag = currentNode.getTagName();
268
    if (currentTag.equals("meta_file_id")) {
269
      meta_file_id = inputString;
270
// DOCTITLE attr cleared from the db
271
//    } else if (currentTag.equals("querytitle")) {
272
//      querytitle = inputString;
273
    } else if (currentTag.equals("value")) {
274
      currentValue = inputString;
275
    } else if (currentTag.equals("pathexpr")) {
276
      currentPathexpr = inputString;
277
    } else if (currentTag.equals("returndoctype")) {
278
      doctypeList.add(inputString);
279
    } else if (currentTag.equals("returnfield")) {
280
      returnFieldList.add(inputString);
281
      containsExtendedSQL = true;
282
    } else if (currentTag.equals("owner")) {
283
      ownerList.add(inputString);
284
    } else if (currentTag.equals("site")) {
285
      siteList.add(inputString);
286
    }
287
  }
288

    
289

    
290
  /**
291
   * create a SQL serialization of the query that this instance represents
292
   */
293
  public String printSQL(boolean useXMLIndex) {
294
    StringBuffer self = new StringBuffer();
295

    
296
    self.append("SELECT docid,docname,doctype,");
297
    self.append("date_created, date_updated, rev ");
298
    self.append("FROM xml_documents WHERE docid IN (");
299

    
300
    // This determines the documents that meet the query conditions
301
    self.append(query.printSQL(useXMLIndex));
302

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

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

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

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

    
498
    self.append("SELECT docid,docname,doctype,");
499
    self.append("date_created, date_updated ");
500
    self.append("FROM xml_documents WHERE docid IN (");
501
    self.append(docid).append(")");
502
    return self.toString();
503
  }
504
  
505
  /**
506
   * create a String description of the query that this instance represents.
507
   * This should become a way to get the XML serialization of the query.
508
   */
509
  public String toString() {
510
    return "meta_file_id=" + meta_file_id + "\n" + query;
511
//DOCTITLE attr cleared from the db
512
//    return "meta_file_id=" + meta_file_id + "\n" + 
513
//           "querytitle=" + querytitle + "\n" + query;
514
  }
515

    
516
  /** a utility class that represents a group of terms in a query */
517
  private class QueryGroup {
518
    private String operator = null;  // indicates how query terms are combined
519
    private Vector children = null;  // the list of query terms and groups
520

    
521
    /** 
522
     * construct a new QueryGroup 
523
     *
524
     * @param operator the boolean conector used to connect query terms 
525
     *                    in this query group
526
     */
527
    public QueryGroup(String operator) {
528
      this.operator = operator;
529
      children = new Vector();
530
    }
531

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

    
541
    /**
542
     * Add a child QueryTerm to this QueryGroup
543
     *
544
     * @param qterm the query term to be added to the list of terms
545
     */
546
    public void addChild(QueryTerm qterm) {
547
      children.add((Object)qterm); 
548
    }
549

    
550
    /**
551
     * Retrieve an Enumeration of query terms for this QueryGroup
552
     */
553
    public Enumeration getChildren() {
554
      return children.elements();
555
    }
556
   
557
    /**
558
     * create a SQL serialization of the query that this instance represents
559
     */
560
    public String printSQL(boolean useXMLIndex) {
561
      StringBuffer self = new StringBuffer();
562
      boolean first = true;
563

    
564
      self.append("(");
565

    
566
      Enumeration en= getChildren();
567
      while (en.hasMoreElements()) {
568
        Object qobject = en.nextElement();
569
        if (first) {
570
          first = false;
571
        } else {
572
          self.append(" " + operator + " ");
573
        }
574
        if (qobject instanceof QueryGroup) {
575
          QueryGroup qg = (QueryGroup)qobject;
576
          self.append(qg.printSQL(useXMLIndex));
577
        } else if (qobject instanceof QueryTerm) {
578
          QueryTerm qt = (QueryTerm)qobject;
579
          self.append(qt.printSQL(useXMLIndex));
580
        } else {
581
          System.err.println("qobject wrong type: fatal error");
582
        }
583
      }
584
      self.append(") \n");
585
      return self.toString();
586
    }
587

    
588
    /**
589
     * create a String description of the query that this instance represents.
590
     * This should become a way to get the XML serialization of the query.
591
     */
592
    public String toString() {
593
      StringBuffer self = new StringBuffer();
594

    
595
      self.append("  (Query group operator=" + operator + "\n");
596
      Enumeration en= getChildren();
597
      while (en.hasMoreElements()) {
598
        Object qobject = en.nextElement();
599
        self.append(qobject);
600
      }
601
      self.append("  )\n");
602
      return self.toString();
603
    }
604
  }
605

    
606
  /** a utility class that represents a single term in a query */
607
  private class QueryTerm {
608
    private boolean casesensitive = false;
609
    private String searchmode = null;
610
    private String value = null;
611
    private String pathexpr = null;
612

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

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

    
645
    /** determine if the QueryTerm is case sensitive */
646
    public boolean isCaseSensitive() {
647
      return casesensitive;
648
    }
649

    
650
    /** get the searchmode parameter */
651
    public String getSearchMode() {
652
      return searchmode;
653
    }
654
 
655
    /** get the Value parameter */
656
    public String getValue() {
657
      return value;
658
    }
659

    
660
    /** get the path expression parameter */
661
    public String getPathExpression() {
662
      return pathexpr;
663
    }
664

    
665
    /**
666
     * create a SQL serialization of the query that this instance represents
667
     */
668
    public String printSQL(boolean useXMLIndex) {
669
      StringBuffer self = new StringBuffer();
670

    
671
      // Uppercase the search string if case match is not important
672
      String casevalue = null;
673
      String nodedataterm = null;
674

    
675
      if (casesensitive) {
676
        nodedataterm = "nodedata";
677
        casevalue = value;
678
      } else {
679
        nodedataterm = "UPPER(nodedata)";
680
        casevalue = value.toUpperCase();
681
      }
682

    
683
      // Add appropriate wildcards to search string
684
      String searchvalue = null;
685
      if (searchmode.equals("starts-with")) {
686
        searchvalue = casevalue + "%";
687
      } else if (searchmode.equals("ends-with")) {
688
        searchvalue = "%" + casevalue;
689
      } else if (searchmode.equals("contains")) {
690
        searchvalue = "%" + casevalue + "%";
691
      } else {
692
        searchvalue = casevalue;
693
      }
694

    
695
      self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n");
696

    
697
      if (pathexpr != null) {
698
        self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
699
        self.append("AND parentnodeid IN ");
700
        // use XML Index
701
        if ( useXMLIndex ) {
702
          self.append("(SELECT nodeid FROM xml_index WHERE path LIKE " + 
703
                      "'" +  pathexpr + "') " );
704
        // without using XML Index; using nested statements instead
705
        } else {
706
          self.append(useNestedStatements(pathexpr));
707
        }
708
      } else {
709
        self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
710
      }
711

    
712
      return self.toString();
713
    }
714

    
715
    /* 
716
     * Constraint the query with @pathexp without using the XML Index,
717
     * but nested SQL statements instead. The query migth be slower.
718
     */
719
    private String useNestedStatements(String pathexpr)
720
    {
721
      StringBuffer nestedStmts = new StringBuffer();
722
      Vector nodes = new Vector();
723
      String path = pathexpr;
724
      int inx = 0;
725

    
726
      do {
727
        inx = path.lastIndexOf("/");
728
//System.out.println(path.substring(inx+1));
729
        nodes.addElement(path.substring(inx+1));
730
        path = path.substring(0, Math.abs(inx));
731
      } while ( inx > 0 );
732
      
733
      // nested statements
734
      int i = 0;
735
      for (i = 0; i < nodes.size()-1; i++) {
736
        nestedStmts.append("(SELECT nodeid FROM xml_nodes" + 
737
                           " WHERE nodename LIKE '" +
738
                             (String)nodes.elementAt(i) + "'" +
739
                           " AND parentnodeid IN ");
740
      }
741
      // for the last statement: it is without " AND parentnodeid IN "
742
      nestedStmts.append("(SELECT nodeid FROM xml_nodes" + 
743
                         " WHERE nodename LIKE '" +
744
                         (String)nodes.elementAt(i) + "'" );
745
      // node.size() number of closing brackets
746
      for (i = 0; i < nodes.size(); i++) {
747
        nestedStmts.append(")");
748
      }
749

    
750

    
751
//System.out.println(nestedStmts.toString());
752
      return nestedStmts.toString();
753
    }
754

    
755
    /**
756
     * create a String description of the query that this instance represents.
757
     * This should become a way to get the XML serialization of the query.
758
     */
759
    public String toString(boolean useXMLIndex) {
760

    
761
      return this.printSQL(useXMLIndex);
762
    }
763
  }
764
}
(39-39/43)