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 745 jones
  /** flag determining whether extended query terms are present */
54 402 berkley
  private boolean containsExtendedSQL=false;
55 745 jones
  /** Identifier for this query document */
56 158 jones
  private String meta_file_id;
57 745 jones
  /** Title of this query */
58
  private String queryTitle;
59
  /** List of document types to be returned using package back tracing */
60 743 jones
  private Vector returnDocList;
61 745 jones
  /** List of document types to be searched */
62 743 jones
  private Vector filterDocList;
63 745 jones
  /** List of fields to be returned in result set */
64 402 berkley
  private Vector returnFieldList;
65 745 jones
  /** List of users owning documents to be searched */
66 535 jones
  private Vector ownerList;
67 745 jones
  /** List of sites/scopes used to constrain search */
68 535 jones
  private Vector siteList;
69 745 jones
  /** The root query group that contains the recursive query constraints */
70 158 jones
  private QueryGroup query = null;
71
72 745 jones
  // Query data structures used temporarily during XML parsing
73 158 jones
  private Stack elementStack;
74
  private Stack queryStack;
75 159 jones
  private String currentValue;
76
  private String currentPathexpr;
77 172 jones
  private String parserName = null;
78 535 jones
  private String accNumberSeparator = null;
79 158 jones
80 155 jones
  /**
81
   * construct an instance of the QuerySpecification class
82
   *
83 172 jones
   * @param queryspec the XML representation of the query (should conform
84
   *                  to pathquery.dtd) as a Reader
85
   * @param parserName the fully qualified name of a Java Class implementing
86 185 jones
   *                  the org.xml.sax.XMLReader interface
87 155 jones
   */
88 535 jones
  public QuerySpecification( Reader queryspec, String parserName,
89
         String accNumberSeparator ) throws IOException {
90 155 jones
    super();
91 402 berkley
92 172 jones
    // Initialize the class variables
93 743 jones
    returnDocList = new Vector();
94
    filterDocList = new Vector();
95 158 jones
    elementStack = new Stack();
96
    queryStack   = new Stack();
97 402 berkley
    returnFieldList = new Vector();
98 535 jones
    ownerList = new Vector();
99
    siteList = new Vector();
100 172 jones
    this.parserName = parserName;
101 535 jones
    this.accNumberSeparator = accNumberSeparator;
102 158 jones
103
    // Initialize the parser and read the queryspec
104 185 jones
    XMLReader parser = initializeParser();
105 181 jones
    if (parser == null) {
106
      System.err.println("SAX parser not instantiated properly.");
107
    }
108 155 jones
    try {
109
      parser.parse(new InputSource(queryspec));
110
    } catch (SAXException e) {
111 675 berkley
      System.err.println("error parsing data in " +
112
                         "QuerySpecification.QuerySpecification");
113 180 jones
      System.err.println(e.getMessage());
114 155 jones
    }
115
  }
116
117
  /**
118
   * construct an instance of the QuerySpecification class
119
   *
120 172 jones
   * @param queryspec the XML representation of the query (should conform
121
   *                  to pathquery.dtd) as a String
122
   * @param parserName the fully qualified name of a Java Class implementing
123
   *                  the org.xml.sax.Parser interface
124 155 jones
   */
125 535 jones
  public QuerySpecification( String queryspec, String parserName,
126
         String accNumberSeparator) throws IOException {
127
    this(new StringReader(queryspec), parserName, accNumberSeparator);
128 155 jones
  }
129
130
  /** Main routine for testing */
131
  static public void main(String[] args) {
132
133
     if (args.length < 1) {
134
       System.err.println("Wrong number of arguments!!!");
135
       System.err.println("USAGE: java QuerySpecification <xmlfile>");
136
       return;
137
     } else {
138 711 jones
       int i = 0;
139
       boolean useXMLIndex = true;
140
       if ( args[i].equals( "-noindex" ) ) {
141
         useXMLIndex = false;
142
         i++;
143
       }
144
       String xmlfile  = args[i];
145
146 155 jones
       try {
147 203 jones
         MetaCatUtil util = new MetaCatUtil();
148 155 jones
         FileReader xml = new FileReader(new File(xmlfile));
149 203 jones
         QuerySpecification qspec =
150 535 jones
                 new QuerySpecification(xml, util.getOption("saxparser"),
151
                                        util.getOption("accNumberSeparator"));
152 706 bojilova
         System.out.println(qspec.printSQL(useXMLIndex));
153 181 jones
154 155 jones
       } catch (IOException e) {
155
         System.err.println(e.getMessage());
156
       }
157
158
     }
159
  }
160 402 berkley
161
  /**
162
   * Returns true if the parsed query contains and extended xml query
163
   * (i.e. there is at least one &lt;returnfield&gt; in the pathquery document)
164
   */
165
  public boolean containsExtendedSQL()
166
  {
167
    if(containsExtendedSQL)
168
    {
169
      return true;
170
    }
171
    else
172
    {
173
      return false;
174
    }
175
  }
176
177
  /**
178 745 jones
   * Accessor method to return the identifier of this Query
179
   */
180
  public String getIdentifier()
181
  {
182
    return meta_file_id;
183
  }
184
185
  /**
186
   * method to set the identifier of this query
187
   */
188
  public void setIdentifier(String id) {
189
    this.meta_file_id = id;
190
  }
191
192
  /**
193
   * Accessor method to return the title of this Query
194
   */
195
  public String getQueryTitle()
196
  {
197
    return queryTitle;
198
  }
199
200
  /**
201
   * method to set the title of this query
202
   */
203
  public void setQueryTitle(String title)
204
  {
205
    this.queryTitle = title;
206
  }
207
208
  /**
209
   * Accessor method to return a vector of the return document types as
210
   * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
211
   */
212
  public Vector getReturnDocList()
213
  {
214
    return this.returnDocList;
215
  }
216
217
  /**
218
   * method to set the list of return docs of this query
219
   */
220
  public void setReturnDocList(Vector returnDocList)
221
  {
222
    this.returnDocList = returnDocList;
223
  }
224
225
  /**
226
   * Accessor method to return a vector of the filter doc types as
227
   * defined in the &lt;filterdoctype&gt; tag in the pathquery dtd.
228
   */
229
  public Vector getFilterDocList()
230
  {
231
    return this.filterDocList;
232
  }
233
234
  /**
235
   * method to set the list of filter docs of this query
236
   */
237
  public void setFilterDocList(Vector filterDocList)
238
  {
239
    this.filterDocList = filterDocList;
240
  }
241
242
  /**
243 402 berkley
   * Accessor method to return a vector of the extended return fields as
244
   * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
245
   */
246
  public Vector getReturnFieldList()
247
  {
248
    return this.returnFieldList;
249
  }
250 155 jones
251 172 jones
  /**
252 745 jones
   * method to set the list of fields to be returned by this query
253
   */
254
  public void setReturnFieldList(Vector returnFieldList)
255
  {
256
    this.returnFieldList = returnFieldList;
257
  }
258
259
  /**
260
   * Accessor method to return a vector of the owner fields as
261
   * defined in the &lt;owner&gt; tag in the pathquery dtd.
262
   */
263
  public Vector getOwnerList()
264
  {
265
    return this.ownerList;
266
  }
267
268
  /**
269
   * method to set the list of owners used to constrain this query
270
   */
271
  public void setOwnerList(Vector ownerList)
272
  {
273
    this.ownerList = ownerList;
274
  }
275
276
  /**
277
   * Accessor method to return a vector of the site fields as
278
   * defined in the &lt;site&gt; tag in the pathquery dtd.
279
   */
280
  public Vector getSiteList()
281
  {
282
    return this.siteList;
283
  }
284
285
  /**
286
   * method to set the list of sites used to constrain this query
287
   */
288
  public void setSiteList(Vector siteList)
289
  {
290
    this.siteList = siteList;
291
  }
292
293
  /**
294
   * get the QueryGroup used to express query constraints
295
   */
296
  public QueryGroup getQueryGroup()
297
  {
298
    return query;
299
  }
300
301
  /**
302 172 jones
   * Set up the SAX parser for reading the XML serialized query
303
   */
304 185 jones
  private XMLReader initializeParser() {
305
    XMLReader parser = null;
306 172 jones
307 155 jones
    // Set up the SAX document handlers for parsing
308
    try {
309
310
      // Get an instance of the parser
311 185 jones
      parser = XMLReaderFactory.createXMLReader(parserName);
312 155 jones
313 185 jones
      // Set the ContentHandler to this instance
314
      parser.setContentHandler(this);
315 155 jones
316 185 jones
      // Set the error Handler to this instance
317 158 jones
      parser.setErrorHandler(this);
318 155 jones
319
    } catch (Exception e) {
320 675 berkley
       System.err.println("Error in QuerySpcecification.initializeParser " +
321
                           e.toString());
322 155 jones
    }
323
324
    return parser;
325
  }
326
327 172 jones
  /**
328
   * callback method used by the SAX Parser when the start tag of an
329
   * element is detected. Used in this context to parse and store
330
   * the query information in class variables.
331
   */
332 185 jones
  public void startElement (String uri, String localName,
333
                            String qName, Attributes atts)
334 155 jones
         throws SAXException {
335 185 jones
    BasicNode currentNode = new BasicNode(localName);
336 159 jones
    // add attributes to BasicNode here
337
    if (atts != null) {
338
      int len = atts.getLength();
339
      for (int i = 0; i < len; i++) {
340 185 jones
        currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i));
341 159 jones
      }
342
    }
343
344 158 jones
    elementStack.push(currentNode);
345 159 jones
    if (currentNode.getTagName().equals("querygroup")) {
346 158 jones
      QueryGroup currentGroup = new QueryGroup(
347 178 jones
                                currentNode.getAttribute("operator"));
348 158 jones
      if (query == null) {
349
        query = currentGroup;
350 159 jones
      } else {
351
        QueryGroup parentGroup = (QueryGroup)queryStack.peek();
352
        parentGroup.addChild(currentGroup);
353 158 jones
      }
354 159 jones
      queryStack.push(currentGroup);
355 158 jones
    }
356 155 jones
  }
357
358 172 jones
  /**
359
   * callback method used by the SAX Parser when the end tag of an
360
   * element is detected. Used in this context to parse and store
361
   * the query information in class variables.
362
   */
363 185 jones
  public void endElement (String uri, String localName,
364
                          String qName) throws SAXException {
365 158 jones
    BasicNode leaving = (BasicNode)elementStack.pop();
366 159 jones
    if (leaving.getTagName().equals("queryterm")) {
367
      boolean isCaseSensitive = (new Boolean(
368
              leaving.getAttribute("casesensitive"))).booleanValue();
369
      QueryTerm currentTerm = null;
370
      if (currentPathexpr == null) {
371
        currentTerm = new QueryTerm(isCaseSensitive,
372
                      leaving.getAttribute("searchmode"),currentValue);
373
      } else {
374
        currentTerm = new QueryTerm(isCaseSensitive,
375
                      leaving.getAttribute("searchmode"),currentValue,
376
                      currentPathexpr);
377
      }
378
      QueryGroup currentGroup = (QueryGroup)queryStack.peek();
379
      currentGroup.addChild(currentTerm);
380
      currentValue = null;
381
      currentPathexpr = null;
382
    } else if (leaving.getTagName().equals("querygroup")) {
383
      QueryGroup leavingGroup = (QueryGroup)queryStack.pop();
384 158 jones
    }
385 155 jones
  }
386 158 jones
387 172 jones
  /**
388
   * callback method used by the SAX Parser when the text sequences of an
389
   * xml stream are detected. Used in this context to parse and store
390
   * the query information in class variables.
391
   */
392 158 jones
  public void characters(char ch[], int start, int length) {
393
394
    String inputString = new String(ch, start, length);
395
    BasicNode currentNode = (BasicNode)elementStack.peek();
396
    String currentTag = currentNode.getTagName();
397
    if (currentTag.equals("meta_file_id")) {
398
      meta_file_id = inputString;
399 745 jones
    } else if (currentTag.equals("querytitle")) {
400
      queryTitle = inputString;
401 159 jones
    } else if (currentTag.equals("value")) {
402
      currentValue = inputString;
403
    } else if (currentTag.equals("pathexpr")) {
404
      currentPathexpr = inputString;
405 172 jones
    } else if (currentTag.equals("returndoctype")) {
406 743 jones
      returnDocList.add(inputString);
407 745 jones
    } else if (currentTag.equals("filterdoctype")) {
408
      filterDocList.add(inputString);
409 402 berkley
    } else if (currentTag.equals("returnfield")) {
410
      returnFieldList.add(inputString);
411
      containsExtendedSQL = true;
412 743 jones
    } else if (currentTag.equals("filterdoctype")) {
413
      filterDocList.add(inputString);
414 535 jones
    } else if (currentTag.equals("owner")) {
415
      ownerList.add(inputString);
416
    } else if (currentTag.equals("site")) {
417
      siteList.add(inputString);
418 158 jones
    }
419
  }
420
421 172 jones
  /**
422
   * create a SQL serialization of the query that this instance represents
423
   */
424 706 bojilova
  public String printSQL(boolean useXMLIndex) {
425 170 jones
    StringBuffer self = new StringBuffer();
426
427 692 bojilova
    self.append("SELECT docid,docname,doctype,");
428 626 berkley
    self.append("date_created, date_updated, rev ");
429 172 jones
    self.append("FROM xml_documents WHERE docid IN (");
430 170 jones
431 178 jones
    // This determines the documents that meet the query conditions
432 706 bojilova
    self.append(query.printSQL(useXMLIndex));
433 172 jones
434
    self.append(") ");
435 402 berkley
436 172 jones
    // Add SQL to filter for doctypes requested in the query
437 743 jones
    // This is an implicit OR for the list of doctypes. Only doctypes in this
438
    // list will be searched if the tag is present
439
    if (!filterDocList.isEmpty()) {
440 172 jones
      boolean firstdoctype = true;
441
      self.append(" AND (");
442 743 jones
      Enumeration en = filterDocList.elements();
443 172 jones
      while (en.hasMoreElements()) {
444
        String currentDoctype = (String)en.nextElement();
445
        if (firstdoctype) {
446 402 berkley
           firstdoctype = false;
447
           self.append(" doctype = '" + currentDoctype + "'");
448 172 jones
        } else {
449
          self.append(" OR doctype = '" + currentDoctype + "'");
450
        }
451
      }
452
      self.append(") ");
453
    }
454 743 jones
455 535 jones
    // Add SQL to filter for owners requested in the query
456
    // This is an implicit OR for the list of owners
457
    if (!ownerList.isEmpty()) {
458
      boolean first = true;
459
      self.append(" AND (");
460
      Enumeration en = ownerList.elements();
461
      while (en.hasMoreElements()) {
462
        String current = (String)en.nextElement();
463
        if (first) {
464
           first = false;
465
           self.append(" user_owner = '" + current + "'");
466
        } else {
467
          self.append(" OR user_owner = '" + current + "'");
468
        }
469
      }
470
      self.append(") ");
471
    }
472
473
    // Add SQL to filter for sites requested in the query
474
    // This is an implicit OR for the list of sites
475
    if (!siteList.isEmpty()) {
476
      boolean first = true;
477
      self.append(" AND (");
478
      Enumeration en = siteList.elements();
479
      while (en.hasMoreElements()) {
480
        String current = (String)en.nextElement();
481
        if (first) {
482
           first = false;
483
           self.append(" SUBSTR(docid, 1, INSTR(docid, '" +
484
               accNumberSeparator + "')-1) = '" + current + "'");
485
        } else {
486
          self.append(" OR SUBSTR(docid, 1, INSTR(docid, '" +
487
               accNumberSeparator + "')-1) = '" + current + "'");
488
        }
489
      }
490
      self.append(") ");
491
    }
492 710 berkley
    //System.out.println(self.toString());
493 170 jones
    return self.toString();
494
  }
495 402 berkley
496
  /**
497
   * This method prints sql based upon the &lt;returnfield&gt; tag in the
498
   * pathquery document.  This allows for customization of the
499
   * returned fields
500 465 berkley
   * @param doclist the list of document ids to search by
501 402 berkley
   */
502 465 berkley
  public String printExtendedSQL(String doclist)
503 402 berkley
  {
504
    StringBuffer self = new StringBuffer();
505 423 berkley
    self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata ");
506
    self.append("from xml_index, xml_nodes where xml_index.nodeid=");
507
    self.append("xml_nodes.parentnodeid and (xml_index.path like '");
508 402 berkley
    boolean firstfield = true;
509
    //put the returnfields into the query
510
    //the for loop allows for multiple fields
511
    for(int i=0; i<returnFieldList.size(); i++)
512
    {
513
      if(firstfield)
514
      {
515
        firstfield = false;
516 405 berkley
        self.append((String)returnFieldList.elementAt(i));
517 402 berkley
        self.append("' ");
518
      }
519
      else
520
      {
521 423 berkley
        self.append("or xml_index.path like '");
522 405 berkley
        self.append((String)returnFieldList.elementAt(i));
523 402 berkley
        self.append("' ");
524
      }
525
    }
526 423 berkley
    self.append(") AND xml_nodes.docid in (");
527 465 berkley
    //self.append(query.printSQL());
528
    self.append(doclist);
529 402 berkley
    self.append(")");
530 423 berkley
    self.append(" AND xml_nodes.nodetype = 'TEXT'");
531 405 berkley
532 423 berkley
    //System.out.println(self.toString());
533 402 berkley
    return self.toString();
534
  }
535 465 berkley
536
  public static String printRelationSQL(String docid)
537
  {
538
    StringBuffer self = new StringBuffer();
539 489 berkley
    self.append("select subject, relationship, object, subdoctype, ");
540
    self.append("objdoctype from xml_relation ");
541 743 jones
    self.append("where docid like '").append(docid).append("'");
542 465 berkley
    return self.toString();
543
  }
544 453 berkley
545 172 jones
  /**
546 465 berkley
   * Prints sql that returns all relations in the database.
547 453 berkley
   */
548
  public static String printPackageSQL()
549
  {
550
    StringBuffer self = new StringBuffer();
551
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
552
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
553 743 jones
    self.append("'triple/subject') s, (select nodeid, parentnodeid ");
554 453 berkley
    self.append("from xml_index where path like ");
555 743 jones
    self.append("'triple/relationship') rel, ");
556 453 berkley
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
557 743 jones
    self.append("'triple/object') o, ");
558 453 berkley
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
559
    self.append("where s.parentnodeid = rel.parentnodeid ");
560
    self.append("and rel.parentnodeid = o.parentnodeid ");
561
    self.append("and x.parentnodeid in rel.nodeid ");
562
    self.append("and y.parentnodeid in o.nodeid ");
563
    self.append("and z.parentnodeid in s.nodeid ");
564
    //self.append("and z.nodedata like '%");
565
    //self.append(docid);
566
    //self.append("%'");
567
    return self.toString();
568
  }
569
570
  /**
571 465 berkley
   * Prints sql that returns all relations in the database that were input
572
   * under a specific docid
573
   * @param docid the docid to search for.
574
   */
575
  public static String printPackageSQL(String docid)
576
  {
577
    StringBuffer self = new StringBuffer();
578
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
579
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
580 743 jones
    self.append("'triple/subject') s, (select nodeid, parentnodeid ");
581 465 berkley
    self.append("from xml_index where path like ");
582 743 jones
    self.append("'triple/relationship') rel, ");
583 465 berkley
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
584 743 jones
    self.append("'triple/object') o, ");
585 465 berkley
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
586
    self.append("where s.parentnodeid = rel.parentnodeid ");
587
    self.append("and rel.parentnodeid = o.parentnodeid ");
588
    self.append("and x.parentnodeid in rel.nodeid ");
589
    self.append("and y.parentnodeid in o.nodeid ");
590
    self.append("and z.parentnodeid in s.nodeid ");
591
    self.append("and z.docid like '").append(docid).append("'");
592
593
    return self.toString();
594
  }
595
596
  /**
597
   * Returns all of the relations that has a certain docid in the subject
598
   * or the object.
599
   *
600
   * @param docid the docid to search for
601
   */
602
  public static String printPackageSQL(String subDocidURL, String objDocidURL)
603
  {
604
    StringBuffer self = new StringBuffer();
605
    self.append("select z.nodedata, x.nodedata, y.nodedata from ");
606
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
607 743 jones
    self.append("'triple/subject') s, (select nodeid, parentnodeid ");
608 465 berkley
    self.append("from xml_index where path like ");
609 743 jones
    self.append("'triple/relationship') rel, ");
610 465 berkley
    self.append("(select nodeid, parentnodeid from xml_index where path like ");
611 743 jones
    self.append("'triple/object') o, ");
612 465 berkley
    self.append("xml_nodes x, xml_nodes y, xml_nodes z ");
613
    self.append("where s.parentnodeid = rel.parentnodeid ");
614
    self.append("and rel.parentnodeid = o.parentnodeid ");
615
    self.append("and x.parentnodeid in rel.nodeid ");
616
    self.append("and y.parentnodeid in o.nodeid ");
617
    self.append("and z.parentnodeid in s.nodeid ");
618
    self.append("and (z.nodedata like '");
619
    self.append(subDocidURL);
620
    self.append("' or y.nodedata like '");
621
    self.append(objDocidURL);
622
    self.append("')");
623
    return self.toString();
624
  }
625
626
  public static String printGetDocByDoctypeSQL(String docid)
627
  {
628
    StringBuffer self = new StringBuffer();
629
630 692 bojilova
    self.append("SELECT docid,docname,doctype,");
631 465 berkley
    self.append("date_created, date_updated ");
632
    self.append("FROM xml_documents WHERE docid IN (");
633
    self.append(docid).append(")");
634
    return self.toString();
635
  }
636
637
  /**
638 172 jones
   * create a String description of the query that this instance represents.
639
   * This should become a way to get the XML serialization of the query.
640
   */
641 159 jones
  public String toString() {
642 692 bojilova
    return "meta_file_id=" + meta_file_id + "\n" + query;
643
//DOCTITLE attr cleared from the db
644
//    return "meta_file_id=" + meta_file_id + "\n" +
645
//           "querytitle=" + querytitle + "\n" + query;
646 159 jones
  }
647
648 158 jones
  /** a utility class that represents a group of terms in a query */
649
  private class QueryGroup {
650 178 jones
    private String operator = null;  // indicates how query terms are combined
651
    private Vector children = null;  // the list of query terms and groups
652 158 jones
653 172 jones
    /**
654
     * construct a new QueryGroup
655
     *
656 178 jones
     * @param operator the boolean conector used to connect query terms
657 172 jones
     *                    in this query group
658
     */
659 178 jones
    public QueryGroup(String operator) {
660
      this.operator = operator;
661 158 jones
      children = new Vector();
662
    }
663
664 172 jones
    /**
665
     * Add a child QueryGroup to this QueryGroup
666
     *
667
     * @param qgroup the query group to be added to the list of terms
668
     */
669 158 jones
    public void addChild(QueryGroup qgroup) {
670
      children.add((Object)qgroup);
671
    }
672
673 172 jones
    /**
674
     * Add a child QueryTerm to this QueryGroup
675
     *
676
     * @param qterm the query term to be added to the list of terms
677
     */
678 158 jones
    public void addChild(QueryTerm qterm) {
679
      children.add((Object)qterm);
680
    }
681
682 172 jones
    /**
683
     * Retrieve an Enumeration of query terms for this QueryGroup
684
     */
685 158 jones
    public Enumeration getChildren() {
686
      return children.elements();
687
    }
688 159 jones
689 172 jones
    /**
690
     * create a SQL serialization of the query that this instance represents
691
     */
692 706 bojilova
    public String printSQL(boolean useXMLIndex) {
693 170 jones
      StringBuffer self = new StringBuffer();
694
      boolean first = true;
695
696
      self.append("(");
697
698
      Enumeration en= getChildren();
699
      while (en.hasMoreElements()) {
700
        Object qobject = en.nextElement();
701
        if (first) {
702
          first = false;
703
        } else {
704 178 jones
          self.append(" " + operator + " ");
705 170 jones
        }
706
        if (qobject instanceof QueryGroup) {
707
          QueryGroup qg = (QueryGroup)qobject;
708 706 bojilova
          self.append(qg.printSQL(useXMLIndex));
709 170 jones
        } else if (qobject instanceof QueryTerm) {
710
          QueryTerm qt = (QueryTerm)qobject;
711 706 bojilova
          self.append(qt.printSQL(useXMLIndex));
712 170 jones
        } else {
713
          System.err.println("qobject wrong type: fatal error");
714
        }
715
      }
716
      self.append(") \n");
717
      return self.toString();
718
    }
719
720 172 jones
    /**
721
     * create a String description of the query that this instance represents.
722
     * This should become a way to get the XML serialization of the query.
723
     */
724 159 jones
    public String toString() {
725
      StringBuffer self = new StringBuffer();
726
727 178 jones
      self.append("  (Query group operator=" + operator + "\n");
728 159 jones
      Enumeration en= getChildren();
729
      while (en.hasMoreElements()) {
730
        Object qobject = en.nextElement();
731
        self.append(qobject);
732
      }
733
      self.append("  )\n");
734
      return self.toString();
735
    }
736 158 jones
  }
737
738
  /** a utility class that represents a single term in a query */
739
  private class QueryTerm {
740
    private boolean casesensitive = false;
741
    private String searchmode = null;
742
    private String value = null;
743
    private String pathexpr = null;
744
745 172 jones
    /**
746
     * Construct a new instance of a query term for a free text search
747
     * (using the value only)
748
     *
749
     * @param casesensitive flag indicating whether case is used to match
750
     * @param searchmode determines what kind of substring match is performed
751
     *        (one of starts-with|ends-with|contains|matches-exactly)
752
     * @param value the text value to match
753
     */
754 158 jones
    public QueryTerm(boolean casesensitive, String searchmode,
755
                     String value) {
756
      this.casesensitive = casesensitive;
757
      this.searchmode = searchmode;
758
      this.value = value;
759
    }
760
761 172 jones
    /**
762
     * Construct a new instance of a query term for a structured search
763
     * (matching the value only for those nodes in the pathexpr)
764
     *
765
     * @param casesensitive flag indicating whether case is used to match
766
     * @param searchmode determines what kind of substring match is performed
767
     *        (one of starts-with|ends-with|contains|matches-exactly)
768
     * @param value the text value to match
769
     * @param pathexpr the hierarchical path to the nodes to be searched
770
     */
771 158 jones
    public QueryTerm(boolean casesensitive, String searchmode,
772
                     String value, String pathexpr) {
773
      this(casesensitive, searchmode, value);
774
      this.pathexpr = pathexpr;
775
    }
776
777 172 jones
    /** determine if the QueryTerm is case sensitive */
778 158 jones
    public boolean isCaseSensitive() {
779
      return casesensitive;
780
    }
781
782 172 jones
    /** get the searchmode parameter */
783 158 jones
    public String getSearchMode() {
784
      return searchmode;
785
    }
786
787 172 jones
    /** get the Value parameter */
788 158 jones
    public String getValue() {
789
      return value;
790
    }
791
792 172 jones
    /** get the path expression parameter */
793 158 jones
    public String getPathExpression() {
794
      return pathexpr;
795
    }
796 159 jones
797 172 jones
    /**
798
     * create a SQL serialization of the query that this instance represents
799
     */
800 706 bojilova
    public String printSQL(boolean useXMLIndex) {
801 170 jones
      StringBuffer self = new StringBuffer();
802
803
      // Uppercase the search string if case match is not important
804
      String casevalue = null;
805
      String nodedataterm = null;
806
807
      if (casesensitive) {
808
        nodedataterm = "nodedata";
809
        casevalue = value;
810
      } else {
811
        nodedataterm = "UPPER(nodedata)";
812
        casevalue = value.toUpperCase();
813
      }
814
815
      // Add appropriate wildcards to search string
816 795 bojilova
      //String searchvalue = null;
817
      String searchexpr = null;
818 170 jones
      if (searchmode.equals("starts-with")) {
819 795 bojilova
        //searchvalue = casevalue + "%";
820
        searchexpr = nodedataterm + " LIKE '" + casevalue + "%' ";
821 170 jones
      } else if (searchmode.equals("ends-with")) {
822 795 bojilova
        //searchvalue = "%" + casevalue;
823
        searchexpr = nodedataterm + " LIKE '%" + casevalue + "' ";
824 170 jones
      } else if (searchmode.equals("contains")) {
825 795 bojilova
        //searchvalue = "%" + casevalue + "%";
826
        searchexpr = nodedataterm + " LIKE '%" + casevalue + "%' ";
827
      } else if (searchmode.equals("equals")) {
828
        //searchvalue = casevalue;
829
        searchexpr = nodedataterm + " = '" + casevalue + "' ";
830
      } else if (searchmode.equals("isnot-equal")) {
831
        //searchvalue = casevalue;
832
        searchexpr = nodedataterm + " != '" + casevalue + "' ";
833
      } else {
834
        //searchvalue = casevalue;
835
        String oper = null;
836
        if (searchmode.equals("greater-than")) {
837
          oper = ">";
838
        } else if (searchmode.equals("greater-than-equals")) {
839
          oper = ">=";
840
        } else if (searchmode.equals("less-than")) {
841
          oper = "<";
842
        } else if (searchmode.equals("less-than-equals")) {
843
          oper = "<=";
844
        } else {
845
          System.out.println("NOT expected case. NOT recognized operator: " +
846
                             searchmode);
847
          return null;
848
        }
849
        try {
850
          // it is number; numeric comparison
851
          searchexpr = nodedataterm + " " + oper + " " +
852
                       new Double(casevalue) + " ";
853
        } catch (NumberFormatException nfe) {
854
          // these are characters; character comparison
855
          searchexpr = nodedataterm + " " + oper + " '" + casevalue + "' ";
856
        }
857 170 jones
      }
858
859 178 jones
      self.append("SELECT DISTINCT docid FROM xml_nodes WHERE \n");
860 795 bojilova
      //self.append(nodedataterm + " LIKE " + "'" + searchvalue + "' ");
861
      self.append(searchexpr);
862 170 jones
863
      if (pathexpr != null) {
864
        self.append("AND parentnodeid IN ");
865 706 bojilova
        // use XML Index
866
        if ( useXMLIndex ) {
867
          self.append("(SELECT nodeid FROM xml_index WHERE path LIKE " +
868
                      "'" +  pathexpr + "') " );
869
        // without using XML Index; using nested statements instead
870
        } else {
871
          self.append(useNestedStatements(pathexpr));
872
        }
873 170 jones
      }
874
875
      return self.toString();
876
    }
877
878 706 bojilova
    /*
879
     * Constraint the query with @pathexp without using the XML Index,
880
     * but nested SQL statements instead. The query migth be slower.
881
     */
882
    private String useNestedStatements(String pathexpr)
883
    {
884
      StringBuffer nestedStmts = new StringBuffer();
885
      Vector nodes = new Vector();
886
      String path = pathexpr;
887
      int inx = 0;
888
889
      do {
890
        inx = path.lastIndexOf("/");
891
//System.out.println(path.substring(inx+1));
892
        nodes.addElement(path.substring(inx+1));
893
        path = path.substring(0, Math.abs(inx));
894
      } while ( inx > 0 );
895
896
      // nested statements
897
      int i = 0;
898
      for (i = 0; i < nodes.size()-1; i++) {
899
        nestedStmts.append("(SELECT nodeid FROM xml_nodes" +
900
                           " WHERE nodename LIKE '" +
901
                             (String)nodes.elementAt(i) + "'" +
902
                           " AND parentnodeid IN ");
903
      }
904
      // for the last statement: it is without " AND parentnodeid IN "
905
      nestedStmts.append("(SELECT nodeid FROM xml_nodes" +
906
                         " WHERE nodename LIKE '" +
907
                         (String)nodes.elementAt(i) + "'" );
908
      // node.size() number of closing brackets
909
      for (i = 0; i < nodes.size(); i++) {
910
        nestedStmts.append(")");
911
      }
912
913
914 710 berkley
//System.out.println(nestedStmts.toString());
915 706 bojilova
      return nestedStmts.toString();
916
    }
917
918 172 jones
    /**
919
     * create a String description of the query that this instance represents.
920
     * This should become a way to get the XML serialization of the query.
921
     */
922 709 bojilova
    public String toString() {
923 159 jones
924 709 bojilova
      return this.printSQL(true);
925 159 jones
    }
926 158 jones
  }
927 155 jones
}