Project

General

Profile

1 155 jones
/**
2 203 jones
 *  '$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 349 jones
 *    Release: @release@
11 155 jones
 *
12 203 jones
 *   '$Author$'
13
 *     '$Date$'
14
 * '$Revision$'
15 669 jones
 *
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 155 jones
 */
30
31
package edu.ucsb.nceas.metacat;
32
33
import java.io.*;
34
import java.util.Stack;
35 158 jones
import java.util.Vector;
36 159 jones
import java.util.Enumeration;
37 155 jones
38 185 jones
import org.xml.sax.Attributes;
39 158 jones
import org.xml.sax.InputSource;
40
import org.xml.sax.SAXException;
41
import org.xml.sax.SAXParseException;
42 185 jones
import org.xml.sax.XMLReader;
43
import org.xml.sax.helpers.XMLReaderFactory;
44
import org.xml.sax.helpers.DefaultHandler;
45 155 jones
46 402 berkley
/**
47 172 jones
 * 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 155 jones
 */
51 185 jones
public class QuerySpecification extends DefaultHandler {
52 155 jones
53 402 berkley
  private boolean containsExtendedSQL=false;
54
55 158 jones
  // Query data structures
56
  private String meta_file_id;
57
  private String querytitle;
58 172 jones
  private Vector doctypeList;
59 402 berkley
  private Vector returnFieldList;
60 535 jones
  private Vector ownerList;
61
  private Vector siteList;
62 158 jones
  private QueryGroup query = null;
63
64
  private Stack elementStack;
65
  private Stack queryStack;
66 159 jones
  private String currentValue;
67
  private String currentPathexpr;
68 172 jones
  private String parserName = null;
69 535 jones
  private String accNumberSeparator = null;
70 158 jones
71 155 jones
  /**
72
   * construct an instance of the QuerySpecification class
73
   *
74 172 jones
   * @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 185 jones
   *                  the org.xml.sax.XMLReader interface
78 155 jones
   */
79 535 jones
  public QuerySpecification( Reader queryspec, String parserName,
80
         String accNumberSeparator ) throws IOException {
81 155 jones
    super();
82 402 berkley
83 172 jones
    // Initialize the class variables
84
    doctypeList = new Vector();
85 158 jones
    elementStack = new Stack();
86
    queryStack   = new Stack();
87 402 berkley
    returnFieldList = new Vector();
88 535 jones
    ownerList = new Vector();
89
    siteList = new Vector();
90 172 jones
    this.parserName = parserName;
91 535 jones
    this.accNumberSeparator = accNumberSeparator;
92 158 jones
93
    // Initialize the parser and read the queryspec
94 185 jones
    XMLReader parser = initializeParser();
95 181 jones
    if (parser == null) {
96
      System.err.println("SAX parser not instantiated properly.");
97
    }
98 155 jones
    try {
99
      parser.parse(new InputSource(queryspec));
100
    } catch (SAXException e) {
101 675 berkley
      System.err.println("error parsing data in " +
102
                         "QuerySpecification.QuerySpecification");
103 180 jones
      System.err.println(e.getMessage());
104 155 jones
    }
105
  }
106
107
  /**
108
   * construct an instance of the QuerySpecification class
109
   *
110 172 jones
   * @param queryspec the XML representation of the query (should conform
111
   *                  to pathquery.dtd) as a String
112
   * @param parserName the fully qualified name of a Java Class implementing
113
   *                  the org.xml.sax.Parser interface
114 155 jones
   */
115 535 jones
  public QuerySpecification( String queryspec, String parserName,
116
         String accNumberSeparator) throws IOException {
117
    this(new StringReader(queryspec), parserName, accNumberSeparator);
118 155 jones
  }
119
120
  /** Main routine for testing */
121
  static public void main(String[] args) {
122
123
     if (args.length < 1) {
124
       System.err.println("Wrong number of arguments!!!");
125
       System.err.println("USAGE: java QuerySpecification <xmlfile>");
126
       return;
127
     } else {
128
       String xmlfile  = args[0];
129
130
       try {
131 203 jones
         MetaCatUtil util = new MetaCatUtil();
132 155 jones
         FileReader xml = new FileReader(new File(xmlfile));
133 203 jones
         QuerySpecification qspec =
134 535 jones
                 new QuerySpecification(xml, util.getOption("saxparser"),
135
                                        util.getOption("accNumberSeparator"));
136 172 jones
         System.out.println(qspec.printSQL());
137 181 jones
138 155 jones
       } catch (IOException e) {
139
         System.err.println(e.getMessage());
140
       }
141
142
     }
143
  }
144 402 berkley
145
  /**
146
   * Returns true if the parsed query contains and extended xml query
147
   * (i.e. there is at least one &lt;returnfield&gt; in the pathquery document)
148
   */
149
  public boolean containsExtendedSQL()
150
  {
151
    if(containsExtendedSQL)
152
    {
153
      return true;
154
    }
155
    else
156
    {
157
      return false;
158
    }
159
  }
160
161
  /**
162
   * Accessor method to return a vector of the extended return fields as
163
   * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
164
   */
165
  public Vector getReturnFieldList()
166
  {
167
    return this.returnFieldList;
168
  }
169 155 jones
170 172 jones
  /**
171
   * Set up the SAX parser for reading the XML serialized query
172
   */
173 185 jones
  private XMLReader initializeParser() {
174
    XMLReader parser = null;
175 172 jones
176 155 jones
    // Set up the SAX document handlers for parsing
177
    try {
178
179
      // Get an instance of the parser
180 185 jones
      parser = XMLReaderFactory.createXMLReader(parserName);
181 155 jones
182 185 jones
      // Set the ContentHandler to this instance
183
      parser.setContentHandler(this);
184 155 jones
185 185 jones
      // Set the error Handler to this instance
186 158 jones
      parser.setErrorHandler(this);
187 155 jones
188
    } catch (Exception e) {
189 675 berkley
       System.err.println("Error in QuerySpcecification.initializeParser " +
190
                           e.toString());
191 155 jones
    }
192
193
    return parser;
194
  }
195
196 172 jones
  /**
197
   * callback method used by the SAX Parser when the start tag of an
198
   * element is detected. Used in this context to parse and store
199
   * the query information in class variables.
200
   */
201 185 jones
  public void startElement (String uri, String localName,
202
                            String qName, Attributes atts)
203 155 jones
         throws SAXException {
204 185 jones
    BasicNode currentNode = new BasicNode(localName);
205 159 jones
    // add attributes to BasicNode here
206
    if (atts != null) {
207
      int len = atts.getLength();
208
      for (int i = 0; i < len; i++) {
209 185 jones
        currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i));
210 159 jones
      }
211
    }
212
213 158 jones
    elementStack.push(currentNode);
214 159 jones
    if (currentNode.getTagName().equals("querygroup")) {
215 158 jones
      QueryGroup currentGroup = new QueryGroup(
216 178 jones
                                currentNode.getAttribute("operator"));
217 158 jones
      if (query == null) {
218
        query = currentGroup;
219 159 jones
      } else {
220
        QueryGroup parentGroup = (QueryGroup)queryStack.peek();
221
        parentGroup.addChild(currentGroup);
222 158 jones
      }
223 159 jones
      queryStack.push(currentGroup);
224 158 jones
    }
225 155 jones
  }
226
227 172 jones
  /**
228
   * callback method used by the SAX Parser when the end tag of an
229
   * element is detected. Used in this context to parse and store
230
   * the query information in class variables.
231
   */
232 185 jones
  public void endElement (String uri, String localName,
233
                          String qName) throws SAXException {
234 158 jones
    BasicNode leaving = (BasicNode)elementStack.pop();
235 159 jones
    if (leaving.getTagName().equals("queryterm")) {
236
      boolean isCaseSensitive = (new Boolean(
237
              leaving.getAttribute("casesensitive"))).booleanValue();
238
      QueryTerm currentTerm = null;
239
      if (currentPathexpr == null) {
240
        currentTerm = new QueryTerm(isCaseSensitive,
241
                      leaving.getAttribute("searchmode"),currentValue);
242
      } else {
243
        currentTerm = new QueryTerm(isCaseSensitive,
244
                      leaving.getAttribute("searchmode"),currentValue,
245
                      currentPathexpr);
246
      }
247
      QueryGroup currentGroup = (QueryGroup)queryStack.peek();
248
      currentGroup.addChild(currentTerm);
249
      currentValue = null;
250
      currentPathexpr = null;
251
    } else if (leaving.getTagName().equals("querygroup")) {
252
      QueryGroup leavingGroup = (QueryGroup)queryStack.pop();
253 158 jones
    }
254 155 jones
  }
255 158 jones
256 172 jones
  /**
257
   * callback method used by the SAX Parser when the text sequences of an
258
   * xml stream are detected. Used in this context to parse and store
259
   * the query information in class variables.
260
   */
261 158 jones
  public void characters(char ch[], int start, int length) {
262
263
    String inputString = new String(ch, start, length);
264
    BasicNode currentNode = (BasicNode)elementStack.peek();
265
    String currentTag = currentNode.getTagName();
266
    if (currentTag.equals("meta_file_id")) {
267
      meta_file_id = inputString;
268
    } else if (currentTag.equals("querytitle")) {
269
      querytitle = inputString;
270 159 jones
    } else if (currentTag.equals("value")) {
271
      currentValue = inputString;
272
    } else if (currentTag.equals("pathexpr")) {
273
      currentPathexpr = inputString;
274 172 jones
    } else if (currentTag.equals("returndoctype")) {
275
      doctypeList.add(inputString);
276 402 berkley
    } else if (currentTag.equals("returnfield")) {
277
      returnFieldList.add(inputString);
278
      containsExtendedSQL = true;
279 535 jones
    } else if (currentTag.equals("owner")) {
280
      ownerList.add(inputString);
281
    } else if (currentTag.equals("site")) {
282
      siteList.add(inputString);
283 158 jones
    }
284
  }
285
286 172 jones
287
  /**
288
   * create a SQL serialization of the query that this instance represents
289
   */
290
  public String printSQL() {
291 170 jones
    StringBuffer self = new StringBuffer();
292
293 402 berkley
    self.append("SELECT docid,docname,doctype,doctitle,");
294 626 berkley
    self.append("date_created, date_updated, rev ");
295 172 jones
    self.append("FROM xml_documents WHERE docid IN (");
296 170 jones
297 178 jones
    // This determines the documents that meet the query conditions
298 172 jones
    self.append(query.printSQL());
299
300
    self.append(") ");
301 402 berkley
302 172 jones
    // Add SQL to filter for doctypes requested in the query
303 535 jones
    // This is an implicit OR for the list of doctypes
304 172 jones
    if (!doctypeList.isEmpty()) {
305
      boolean firstdoctype = true;
306
      self.append(" AND (");
307
      Enumeration en = doctypeList.elements();
308
      while (en.hasMoreElements()) {
309
        String currentDoctype = (String)en.nextElement();
310
        if (firstdoctype) {
311 402 berkley
           firstdoctype = false;
312
           self.append(" doctype = '" + currentDoctype + "'");
313 172 jones
        } else {
314
          self.append(" OR doctype = '" + currentDoctype + "'");
315
        }
316
      }
317
      self.append(") ");
318
    }
319 402 berkley
320 535 jones
    // Add SQL to filter for owners requested in the query
321
    // This is an implicit OR for the list of owners
322
    if (!ownerList.isEmpty()) {
323
      boolean first = true;
324
      self.append(" AND (");
325
      Enumeration en = ownerList.elements();
326
      while (en.hasMoreElements()) {
327
        String current = (String)en.nextElement();
328
        if (first) {
329
           first = false;
330
           self.append(" user_owner = '" + current + "'");
331
        } else {
332
          self.append(" OR user_owner = '" + current + "'");
333
        }
334
      }
335
      self.append(") ");
336
    }
337
338
    // Add SQL to filter for sites requested in the query
339
    // This is an implicit OR for the list of sites
340
    if (!siteList.isEmpty()) {
341
      boolean first = true;
342
      self.append(" AND (");
343
      Enumeration en = siteList.elements();
344
      while (en.hasMoreElements()) {
345
        String current = (String)en.nextElement();
346
        if (first) {
347
           first = false;
348
           self.append(" SUBSTR(docid, 1, INSTR(docid, '" +
349
               accNumberSeparator + "')-1) = '" + current + "'");
350
        } else {
351
          self.append(" OR SUBSTR(docid, 1, INSTR(docid, '" +
352
               accNumberSeparator + "')-1) = '" + current + "'");
353
        }
354
      }
355
      self.append(") ");
356
    }
357
358 170 jones
    return self.toString();
359
  }
360 402 berkley
361
  /**
362
   * This method prints sql based upon the &lt;returnfield&gt; tag in the
363
   * pathquery document.  This allows for customization of the
364
   * returned fields
365 465 berkley
   * @param doclist the list of document ids to search by
366 402 berkley
   */
367 465 berkley
  public String printExtendedSQL(String doclist)
368 402 berkley
  {
369
    StringBuffer self = new StringBuffer();
370 423 berkley
    self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata ");
371
    self.append("from xml_index, xml_nodes where xml_index.nodeid=");
372
    self.append("xml_nodes.parentnodeid and (xml_index.path like '");
373 402 berkley
    boolean firstfield = true;
374
    //put the returnfields into the query
375
    //the for loop allows for multiple fields
376
    for(int i=0; i<returnFieldList.size(); i++)
377
    {
378
      if(firstfield)
379
      {
380
        firstfield = false;
381 405 berkley
        self.append((String)returnFieldList.elementAt(i));
382 402 berkley
        self.append("' ");
383
      }
384
      else
385
      {
386 423 berkley
        self.append("or xml_index.path like '");
387 405 berkley
        self.append((String)returnFieldList.elementAt(i));
388 402 berkley
        self.append("' ");
389
      }
390
    }
391 423 berkley
    self.append(") AND xml_nodes.docid in (");
392 465 berkley
    //self.append(query.printSQL());
393
    self.append(doclist);
394 402 berkley
    self.append(")");
395 423 berkley
    self.append(" AND xml_nodes.nodetype = 'TEXT'");
396 405 berkley
397 423 berkley
    //System.out.println(self.toString());
398 402 berkley
    return self.toString();
399
  }
400 465 berkley
401
  public static String printRelationSQL(String docid)
402
  {
403
    StringBuffer self = new StringBuffer();
404 489 berkley
    self.append("select subject, relationship, object, subdoctype, ");
405
    self.append("objdoctype from xml_relation ");
406 465 berkley
    self.append("where subject like '").append(docid).append("'");
407
    return self.toString();
408
  }
409 453 berkley
410 172 jones
  /**
411 465 berkley
   * Prints sql that returns all relations in the database.
412 453 berkley
   */
413
  public static String printPackageSQL()
414
  {
415
    StringBuffer self = new StringBuffer();
416
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
417
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
418
    self.append("'package/relation/subject') s, (select nodeid, parentnodeid ");
419
    self.append("from xml_index where path like ");
420
    self.append("'package/relation/relationship') rel, ");
421
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
422
    self.append("'package/relation/object') o, ");
423
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
424
    self.append("where s.parentnodeid = rel.parentnodeid ");
425
    self.append("and rel.parentnodeid = o.parentnodeid ");
426
    self.append("and x.parentnodeid in rel.nodeid ");
427
    self.append("and y.parentnodeid in o.nodeid ");
428
    self.append("and z.parentnodeid in s.nodeid ");
429
    //self.append("and z.nodedata like '%");
430
    //self.append(docid);
431
    //self.append("%'");
432
    return self.toString();
433
  }
434
435
  /**
436 465 berkley
   * Prints sql that returns all relations in the database that were input
437
   * under a specific docid
438
   * @param docid the docid to search for.
439
   */
440
  public static String printPackageSQL(String docid)
441
  {
442
    StringBuffer self = new StringBuffer();
443
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
444
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
445
    self.append("'package/relation/subject') s, (select nodeid, parentnodeid ");
446
    self.append("from xml_index where path like ");
447
    self.append("'package/relation/relationship') rel, ");
448
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
449
    self.append("'package/relation/object') o, ");
450
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
451
    self.append("where s.parentnodeid = rel.parentnodeid ");
452
    self.append("and rel.parentnodeid = o.parentnodeid ");
453
    self.append("and x.parentnodeid in rel.nodeid ");
454
    self.append("and y.parentnodeid in o.nodeid ");
455
    self.append("and z.parentnodeid in s.nodeid ");
456
    self.append("and z.docid like '").append(docid).append("'");
457
458
    return self.toString();
459
  }
460
461
  /**
462
   * Returns all of the relations that has a certain docid in the subject
463
   * or the object.
464
   *
465
   * @param docid the docid to search for
466
   */
467
  public static String printPackageSQL(String subDocidURL, String objDocidURL)
468
  {
469
    StringBuffer self = new StringBuffer();
470
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
471
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
472
    self.append("'package/relation/subject') s, (select nodeid, parentnodeid ");
473
    self.append("from xml_index where path like ");
474
    self.append("'package/relation/relationship') rel, ");
475
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
476
    self.append("'package/relation/object') o, ");
477
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
478
    self.append("where s.parentnodeid = rel.parentnodeid ");
479
    self.append("and rel.parentnodeid = o.parentnodeid ");
480
    self.append("and x.parentnodeid in rel.nodeid ");
481
    self.append("and y.parentnodeid in o.nodeid ");
482
    self.append("and z.parentnodeid in s.nodeid ");
483
    self.append("and (z.nodedata like '");
484
    self.append(subDocidURL);
485
    self.append("' or y.nodedata like '");
486
    self.append(objDocidURL);
487
    self.append("')");
488
    return self.toString();
489
  }
490
491
  public static String printGetDocByDoctypeSQL(String docid)
492
  {
493
    StringBuffer self = new StringBuffer();
494
495
    self.append("SELECT docid,docname,doctype,doctitle,");
496
    self.append("date_created, date_updated ");
497
    self.append("FROM xml_documents WHERE docid IN (");
498
    self.append(docid).append(")");
499
    return self.toString();
500
  }
501
502
  /**
503 172 jones
   * create a String description of the query that this instance represents.
504
   * This should become a way to get the XML serialization of the query.
505
   */
506 159 jones
  public String toString() {
507
    return "meta_file_id=" + meta_file_id + "\n" +
508
           "querytitle=" + querytitle + "\n" + query;
509
  }
510
511 158 jones
  /** a utility class that represents a group of terms in a query */
512
  private class QueryGroup {
513 178 jones
    private String operator = null;  // indicates how query terms are combined
514
    private Vector children = null;  // the list of query terms and groups
515 158 jones
516 172 jones
    /**
517
     * construct a new QueryGroup
518
     *
519 178 jones
     * @param operator the boolean conector used to connect query terms
520 172 jones
     *                    in this query group
521
     */
522 178 jones
    public QueryGroup(String operator) {
523
      this.operator = operator;
524 158 jones
      children = new Vector();
525
    }
526
527 172 jones
    /**
528
     * Add a child QueryGroup to this QueryGroup
529
     *
530
     * @param qgroup the query group to be added to the list of terms
531
     */
532 158 jones
    public void addChild(QueryGroup qgroup) {
533
      children.add((Object)qgroup);
534
    }
535
536 172 jones
    /**
537
     * Add a child QueryTerm to this QueryGroup
538
     *
539
     * @param qterm the query term to be added to the list of terms
540
     */
541 158 jones
    public void addChild(QueryTerm qterm) {
542
      children.add((Object)qterm);
543
    }
544
545 172 jones
    /**
546
     * Retrieve an Enumeration of query terms for this QueryGroup
547
     */
548 158 jones
    public Enumeration getChildren() {
549
      return children.elements();
550
    }
551 159 jones
552 172 jones
    /**
553
     * create a SQL serialization of the query that this instance represents
554
     */
555
    public String printSQL() {
556 170 jones
      StringBuffer self = new StringBuffer();
557
      boolean first = true;
558
559
      self.append("(");
560
561
      Enumeration en= getChildren();
562
      while (en.hasMoreElements()) {
563
        Object qobject = en.nextElement();
564
        if (first) {
565
          first = false;
566
        } else {
567 178 jones
          self.append(" " + operator + " ");
568 170 jones
        }
569
        if (qobject instanceof QueryGroup) {
570
          QueryGroup qg = (QueryGroup)qobject;
571 172 jones
          self.append(qg.printSQL());
572 170 jones
        } else if (qobject instanceof QueryTerm) {
573
          QueryTerm qt = (QueryTerm)qobject;
574 172 jones
          self.append(qt.printSQL());
575 170 jones
        } else {
576
          System.err.println("qobject wrong type: fatal error");
577
        }
578
      }
579
      self.append(") \n");
580
      return self.toString();
581
    }
582
583 172 jones
    /**
584
     * create a String description of the query that this instance represents.
585
     * This should become a way to get the XML serialization of the query.
586
     */
587 159 jones
    public String toString() {
588
      StringBuffer self = new StringBuffer();
589
590 178 jones
      self.append("  (Query group operator=" + operator + "\n");
591 159 jones
      Enumeration en= getChildren();
592
      while (en.hasMoreElements()) {
593
        Object qobject = en.nextElement();
594
        self.append(qobject);
595
      }
596
      self.append("  )\n");
597
      return self.toString();
598
    }
599 158 jones
  }
600
601
  /** a utility class that represents a single term in a query */
602
  private class QueryTerm {
603
    private boolean casesensitive = false;
604
    private String searchmode = null;
605
    private String value = null;
606
    private String pathexpr = null;
607
608 172 jones
    /**
609
     * Construct a new instance of a query term for a free text search
610
     * (using the value only)
611
     *
612
     * @param casesensitive flag indicating whether case is used to match
613
     * @param searchmode determines what kind of substring match is performed
614
     *        (one of starts-with|ends-with|contains|matches-exactly)
615
     * @param value the text value to match
616
     */
617 158 jones
    public QueryTerm(boolean casesensitive, String searchmode,
618
                     String value) {
619
      this.casesensitive = casesensitive;
620
      this.searchmode = searchmode;
621
      this.value = value;
622
    }
623
624 172 jones
    /**
625
     * Construct a new instance of a query term for a structured search
626
     * (matching the value only for those nodes in the pathexpr)
627
     *
628
     * @param casesensitive flag indicating whether case is used to match
629
     * @param searchmode determines what kind of substring match is performed
630
     *        (one of starts-with|ends-with|contains|matches-exactly)
631
     * @param value the text value to match
632
     * @param pathexpr the hierarchical path to the nodes to be searched
633
     */
634 158 jones
    public QueryTerm(boolean casesensitive, String searchmode,
635
                     String value, String pathexpr) {
636
      this(casesensitive, searchmode, value);
637
      this.pathexpr = pathexpr;
638
    }
639
640 172 jones
    /** determine if the QueryTerm is case sensitive */
641 158 jones
    public boolean isCaseSensitive() {
642
      return casesensitive;
643
    }
644
645 172 jones
    /** get the searchmode parameter */
646 158 jones
    public String getSearchMode() {
647
      return searchmode;
648
    }
649
650 172 jones
    /** get the Value parameter */
651 158 jones
    public String getValue() {
652
      return value;
653
    }
654
655 172 jones
    /** get the path expression parameter */
656 158 jones
    public String getPathExpression() {
657
      return pathexpr;
658
    }
659 159 jones
660 172 jones
    /**
661
     * create a SQL serialization of the query that this instance represents
662
     */
663
    public String printSQL() {
664 170 jones
      StringBuffer self = new StringBuffer();
665
666
      // Uppercase the search string if case match is not important
667
      String casevalue = null;
668
      String nodedataterm = null;
669
670
      if (casesensitive) {
671
        nodedataterm = "nodedata";
672
        casevalue = value;
673
      } else {
674
        nodedataterm = "UPPER(nodedata)";
675
        casevalue = value.toUpperCase();
676
      }
677
678
      // Add appropriate wildcards to search string
679
      String searchvalue = null;
680
      if (searchmode.equals("starts-with")) {
681
        searchvalue = casevalue + "%";
682
      } else if (searchmode.equals("ends-with")) {
683
        searchvalue = "%" + casevalue;
684
      } else if (searchmode.equals("contains")) {
685
        searchvalue = "%" + casevalue + "%";
686
      } else {
687
        searchvalue = casevalue;
688
      }
689
690 178 jones
      self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n");
691 170 jones
692
      if (pathexpr != null) {
693
        self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
694
        self.append("AND parentnodeid IN ");
695
        self.append("(SELECT nodeid FROM xml_index WHERE path LIKE " +
696
                    "'" +  pathexpr + "') " );
697
      } else {
698
        self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
699
      }
700
701
      return self.toString();
702
    }
703
704 172 jones
    /**
705
     * create a String description of the query that this instance represents.
706
     * This should become a way to get the XML serialization of the query.
707
     */
708 159 jones
    public String toString() {
709
710 535 jones
      return this.printSQL();
711 159 jones
    }
712 158 jones
  }
713 155 jones
}