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