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