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