Project

General

Profile

1
/**
2
 *  '$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
 *    Release: @release@
11
 *
12
 *   '$Author: sgarg $'
13
 *     '$Date: 2005-07-25 11:54:26 -0700 (Mon, 25 Jul 2005) $'
14
 * '$Revision: 2523 $'
15
 *
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
 */
30

    
31
package edu.ucsb.nceas.metacat;
32

    
33
import java.io.IOException;
34
import java.io.Reader;
35
import java.io.StringReader;
36
import java.util.Enumeration;
37
import java.util.Hashtable;
38
import java.util.Stack;
39
import java.util.Vector;
40

    
41
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
42

    
43
import org.xml.sax.Attributes;
44
import org.xml.sax.InputSource;
45
import org.xml.sax.SAXException;
46
import org.xml.sax.XMLReader;
47
import org.xml.sax.helpers.DefaultHandler;
48
import org.xml.sax.helpers.XMLReaderFactory;
49
import java.util.Iterator;
50

    
51
/**
52
 * A Class that represents a structured query, and can be constructed from an
53
 * XML serialization conforming to
54
 *
55
 * @see pathquery.dtd. The printSQL() method can be used to print a SQL
56
 *      serialization of the query.
57
 */
58
public class QuerySpecification extends DefaultHandler
59
{
60

    
61
    /** flag determining whether extended query terms are present */
62
    private boolean containsExtendedSQL = false;
63

    
64
    /** Identifier for this query document */
65
    private String meta_file_id;
66

    
67
    /** Title of this query */
68
    private String queryTitle;
69

    
70
    /** List of document types to be returned using package back tracing */
71
    private Vector returnDocList;
72

    
73
    /** List of document types to be searched */
74
    private Vector filterDocList;
75

    
76
    /** List of fields to be returned in result set */
77
    private Vector returnFieldList;
78

    
79
    /** List of users owning documents to be searched */
80
    private Vector ownerList;
81

    
82
    /** List of sites/scopes used to constrain search */
83
    private Vector siteList;
84

    
85
    /** The root query group that contains the recursive query constraints */
86
    private QueryGroup query = null;
87

    
88
    // Query data structures used temporarily during XML parsing
89
    private Stack elementStack;
90

    
91
    private Stack queryStack;
92

    
93
    private String currentValue;
94

    
95
    private String currentPathexpr;
96

    
97
    private String parserName = null;
98

    
99
    private String accNumberSeparator = null;
100

    
101
    private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
102

    
103
    private boolean percentageSearch = false;
104

    
105
    private String userName = null;
106

    
107
    private static final String PUBLIC = "public";
108

    
109
    private String[] group = null;
110

    
111
    public static final String ATTRIBUTESYMBOL = "@";
112

    
113
    private boolean hasAttributeReturnField = false;
114

    
115
    private Hashtable attributeReturnList = new Hashtable();
116

    
117
    private int countAttributeReturnField = 0;
118

    
119
    private StringBuffer textBuffer = new StringBuffer();
120

    
121
    /**
122
     * construct an instance of the QuerySpecification class
123
     *
124
     * @param queryspec
125
     *            the XML representation of the query (should conform to
126
     *            pathquery.dtd) as a Reader
127
     * @param parserName
128
     *            the fully qualified name of a Java Class implementing the
129
     *            org.xml.sax.XMLReader interface
130
     */
131
    public QuerySpecification(Reader queryspec, String parserName,
132
            String accNumberSeparator) throws IOException
133
    {
134
        super();
135

    
136
        // Initialize the class variables
137
        returnDocList = new Vector();
138
        filterDocList = new Vector();
139
        elementStack = new Stack();
140
        queryStack = new Stack();
141
        returnFieldList = new Vector();
142
        ownerList = new Vector();
143
        siteList = new Vector();
144
        this.parserName = parserName;
145
        this.accNumberSeparator = accNumberSeparator;
146

    
147
        // Initialize the parser and read the queryspec
148
        XMLReader parser = initializeParser();
149
        if (parser == null) {
150
            System.err.println("SAX parser not instantiated properly.");
151
        }
152
        try {
153
            parser.parse(new InputSource(queryspec));
154
        } catch (SAXException e) {
155
            System.err.println("error parsing data in "
156
                    + "QuerySpecification.QuerySpecification");
157
            System.err.println(e.getMessage());
158
        }
159
    }
160

    
161
    /**
162
     * construct an instance of the QuerySpecification class
163
     *
164
     * @param queryspec
165
     *            the XML representation of the query (should conform to
166
     *            pathquery.dtd) as a String
167
     * @param parserName
168
     *            the fully qualified name of a Java Class implementing the
169
     *            org.xml.sax.Parser interface
170
     */
171
    public QuerySpecification(String queryspec, String parserName,
172
            String accNumberSeparator) throws IOException
173
    {
174
        this(new StringReader(queryspec), parserName, accNumberSeparator);
175
    }
176

    
177
    /**
178
     * construct an instance of the QuerySpecification class which don't need
179
     * to parser a xml document
180
     *
181
     * @param accNumberSeparator
182
     *            the separator between doc version
183
     */
184
    public QuerySpecification(String accNumberSeparator) throws IOException
185
    {
186
        // Initialize the class variables
187
        returnDocList = new Vector();
188
        filterDocList = new Vector();
189
        elementStack = new Stack();
190
        queryStack = new Stack();
191
        returnFieldList = new Vector();
192
        ownerList = new Vector();
193
        siteList = new Vector();
194
        this.accNumberSeparator = accNumberSeparator;
195
    }
196

    
197
    /**
198
     * Method to set user name
199
     *
200
     * @param myName
201
     *            the user name
202
     */
203
    public void setUserName(String myName)
204
    {
205
        //to lower case
206
        if (myName != null) {
207
            this.userName = myName.toLowerCase();
208
        } else {
209
            this.userName = myName;
210
        }
211
    }
212

    
213
    /**
214
     * Method to set user group
215
     *
216
     * @param myGroup
217
     *            the user group
218
     */
219
    public void setGroup(String[] myGroup)
220
    {
221
        this.group = myGroup;
222
    }
223

    
224
    /**
225
     * Method to indicate this query is a percentage search
226
     */
227
    public boolean isPercentageSearch()
228
    {
229
        return percentageSearch;
230
    }
231

    
232
    /*
233
     * Method to get owner query. If it is owner it has all permission
234
     */
235
    private String createOwerQuery()
236
    {
237
        String ownerQuery = null;
238
        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
239
        if (userName != null && !userName.equals("")) {
240
            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
241
        }
242

    
243
        MetaCatUtil.debugMessage("OwnerQuery: " + ownerQuery, 30);
244
        return ownerQuery;
245
    }
246

    
247
    /*
248
     * Method to create query for xml_access, this part is to get docid list
249
     * which have a allow rule for a given user
250
     */
251
    private String createAllowRuleQuery()
252
    {
253
        String allowQuery = null;
254
        String allowString = constructAllowString();
255
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
256
        allowQuery = allowQuery + ") AND subtreeid IS NULL";
257
        MetaCatUtil.debugMessage("allow query is: " + allowQuery, 30);
258
        return allowQuery;
259

    
260
    }
261

    
262
    /* Method to construct a allow rule string */
263
    private String constructAllowString()
264
    {
265
        String allowQuery = "";
266
        // add allow rule for user name
267
        if (userName != null && !userName.equals("")) {
268
            allowQuery = allowQuery + "(lower(principal_name) = '" + userName
269
                    + "' AND perm_type = 'allow'"
270
                    + " AND (permission='4' OR permission='7'))";
271
        }
272
        // add allow rule for public
273
        allowQuery = allowQuery + "OR (lower(principal_name) = '" + PUBLIC
274
                + "' AND perm_type = 'allow'"
275
                + " AND (permission='4' OR permission='7'))";
276

    
277
        // add allow rule for group
278
        if (group != null) {
279
            for (int i = 0; i < group.length; i++) {
280
                String groupUint = group[i];
281
                if (groupUint != null && !groupUint.equals("")) {
282
                    groupUint = groupUint.toLowerCase();
283
                    allowQuery = allowQuery + " OR (lower(principal_name) = '"
284
                            + groupUint + "' AND perm_type = 'allow'"
285
                            + " AND (permission='4' OR permission='7'))";
286
                }//if
287
            }//for
288
        }//if
289
        MetaCatUtil.debugMessage("allow string is: " + allowQuery, 40);
290
        return allowQuery;
291
    }
292

    
293
    /*
294
     * Method to create query for xml_access, this part is to get docid list
295
     * which have a deny rule and perm_order is allowFirst for a given user.
296
     * This means the user will be denied to read
297
     */
298
    private String createDenyRuleQuery()
299
    {
300
        String denyQuery = null;
301
        String denyString = constructDenyString();
302
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
303
        denyQuery = denyQuery + ") AND subtreeid IS NULL ";
304
        MetaCatUtil.debugMessage("denyquery is: " + denyQuery, 30);
305
        return denyQuery;
306

    
307
    }
308

    
309
    /* Construct deny string */
310
    private String constructDenyString()
311
    {
312
        String denyQuery = "";
313
        // add deny rule for user name
314
        if (userName != null && !userName.equals("")) {
315
            denyQuery = denyQuery + "(lower(principal_name) = '" + userName
316
                    + "' AND perm_type = 'deny' "
317
                    + "AND perm_order ='allowFirst'"
318
                    + " AND (permission='4' OR permission='7'))";
319
        }
320
        // add deny rule for public
321
        denyQuery = denyQuery + "OR (lower(principal_name) = '" + PUBLIC
322
                + "' AND perm_type = 'deny' " + "AND perm_order ='allowFirst'"
323
                + " AND (permission='4' OR permission='7'))";
324

    
325
        // add allow rule for group
326
        if (group != null) {
327
            for (int i = 0; i < group.length; i++) {
328
                String groupUint = group[i];
329
                if (groupUint != null && !groupUint.equals("")) {
330
                    groupUint = groupUint.toLowerCase();
331
                    denyQuery = denyQuery + " OR (lower(principal_name) = '"
332
                            + groupUint + "' AND perm_type = 'deny' "
333
                            + "AND perm_order ='allowFirst'"
334
                            + " AND (permission='4' OR permission='7'))";
335
                }//if
336
            }//for
337
        }//if
338
        return denyQuery;
339
    }
340

    
341
    /**
342
     * Method to append a access control query to SQL. So in DBQuery class, we
343
     * can get docid from both user specified query and access control query.
344
     * We don't need to checking permission after we get the doclist. It will
345
     * be good to performance
346
     *
347
     */
348
    public String getAccessQuery()
349
    {
350
        String accessQuery = null;
351
        String onwer = createOwerQuery();
352
        String allow = createAllowRuleQuery();
353
        String deny = createDenyRuleQuery();
354
        accessQuery = " AND (docid IN(" + onwer + ")";
355
        accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
356
                + " AND docid NOT IN (" + deny + ")))";
357
        MetaCatUtil.debugMessage("accessquery is: " + accessQuery, 30);
358
        return accessQuery;
359
    }
360

    
361
    /**
362
     * Returns true if the parsed query contains and extended xml query (i.e.
363
     * there is at least one &lt;returnfield&gt; in the pathquery document)
364
     */
365
    public boolean containsExtendedSQL()
366
    {
367
        if (containsExtendedSQL) {
368
            return true;
369
        } else {
370
            return false;
371
        }
372
    }
373

    
374
    /**
375
     * A method to get if the query has an attribute return field
376
     */
377
    public boolean containsAttributeReturnField()
378
    {
379
        return hasAttributeReturnField;
380
    }
381

    
382
    /**
383
     * Accessor method to return the identifier of this Query
384
     */
385
    public String getIdentifier()
386
    {
387
        return meta_file_id;
388
    }
389

    
390
    /**
391
     * method to set the identifier of this query
392
     */
393
    public void setIdentifier(String id)
394
    {
395
        this.meta_file_id = id;
396
    }
397

    
398
    /**
399
     * Accessor method to return the title of this Query
400
     */
401
    public String getQueryTitle()
402
    {
403
        return queryTitle;
404
    }
405

    
406
    /**
407
     * method to set the title of this query
408
     */
409
    public void setQueryTitle(String title)
410
    {
411
        this.queryTitle = title;
412
    }
413

    
414
    /**
415
     * Accessor method to return a vector of the return document types as
416
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
417
     */
418
    public Vector getReturnDocList()
419
    {
420
        return this.returnDocList;
421
    }
422

    
423
    /**
424
     * method to set the list of return docs of this query
425
     */
426
    public void setReturnDocList(Vector returnDocList)
427
    {
428
        this.returnDocList = returnDocList;
429
    }
430

    
431
    /**
432
     * Accessor method to return a vector of the filter doc types as defined in
433
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
434
     */
435
    public Vector getFilterDocList()
436
    {
437
        return this.filterDocList;
438
    }
439

    
440
    /**
441
     * method to set the list of filter docs of this query
442
     */
443
    public void setFilterDocList(Vector filterDocList)
444
    {
445
        this.filterDocList = filterDocList;
446
    }
447

    
448
    /**
449
     * Accessor method to return a vector of the extended return fields as
450
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
451
     */
452
    public Vector getReturnFieldList()
453
    {
454
        return this.returnFieldList;
455
    }
456

    
457
    /**
458
     * method to set the list of fields to be returned by this query
459
     */
460
    public void setReturnFieldList(Vector returnFieldList)
461
    {
462
        this.returnFieldList = returnFieldList;
463
    }
464

    
465
    /**
466
     * Accessor method to return a vector of the owner fields as defined in the
467
     * &lt;owner&gt; tag in the pathquery dtd.
468
     */
469
    public Vector getOwnerList()
470
    {
471
        return this.ownerList;
472
    }
473

    
474
    /**
475
     * method to set the list of owners used to constrain this query
476
     */
477
    public void setOwnerList(Vector ownerList)
478
    {
479
        this.ownerList = ownerList;
480
    }
481

    
482
    /**
483
     * Accessor method to return a vector of the site fields as defined in the
484
     * &lt;site&gt; tag in the pathquery dtd.
485
     */
486
    public Vector getSiteList()
487
    {
488
        return this.siteList;
489
    }
490

    
491
    /**
492
     * method to set the list of sites used to constrain this query
493
     */
494
    public void setSiteList(Vector siteList)
495
    {
496
        this.siteList = siteList;
497
    }
498

    
499
    /**
500
     * get the QueryGroup used to express query constraints
501
     */
502
    public QueryGroup getQueryGroup()
503
    {
504
        return query;
505
    }
506

    
507
    /**
508
     * set the querygroup
509
     */
510
    public void setQueryGroup(QueryGroup group)
511
    {
512
        query = group;
513
    }
514

    
515
    /**
516
     * set if this query sepcification has extendQuery(has return doc type or
517
     * not)
518
     */
519
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
520
    {
521
        containsExtendedSQL = hasExtenedQuery;
522
    }
523

    
524
    /**
525
     * Set up the SAX parser for reading the XML serialized query
526
     */
527
    private XMLReader initializeParser()
528
    {
529
        XMLReader parser = null;
530

    
531
        // Set up the SAX document handlers for parsing
532
        try {
533

    
534
            // Get an instance of the parser
535
            parser = XMLReaderFactory.createXMLReader(parserName);
536

    
537
            // Set the ContentHandler to this instance
538
            parser.setContentHandler(this);
539

    
540
            // Set the error Handler to this instance
541
            parser.setErrorHandler(this);
542

    
543
        } catch (Exception e) {
544
            System.err.println("Error in QuerySpcecification.initializeParser "
545
                    + e.toString());
546
        }
547

    
548
        return parser;
549
    }
550

    
551
    /**
552
     * callback method used by the SAX Parser when the start tag of an element
553
     * is detected. Used in this context to parse and store the query
554
     * information in class variables.
555
     */
556
    public void startElement(String uri, String localName, String qName,
557
            Attributes atts) throws SAXException
558
    {
559
        BasicNode currentNode = new BasicNode(localName);
560
        // add attributes to BasicNode here
561
        if (atts != null) {
562
            int len = atts.getLength();
563
            for (int i = 0; i < len; i++) {
564
                currentNode
565
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
566
            }
567
        }
568

    
569
        elementStack.push(currentNode);
570
        if (currentNode.getTagName().equals("querygroup")) {
571
            QueryGroup currentGroup = new QueryGroup(currentNode
572
                    .getAttribute("operator"));
573
            if (query == null) {
574
                query = currentGroup;
575
            } else {
576
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
577
                parentGroup.addChild(currentGroup);
578
            }
579
            queryStack.push(currentGroup);
580
        }
581
    }
582

    
583
    /**
584
     * callback method used by the SAX Parser when the end tag of an element is
585
     * detected. Used in this context to parse and store the query information
586
     * in class variables.
587
     */
588
    public void endElement(String uri, String localName, String qName)
589
            throws SAXException
590
    {
591
        BasicNode leaving = (BasicNode) elementStack.pop();
592
        if (leaving.getTagName().equals("queryterm")) {
593
            boolean isCaseSensitive = (new Boolean(leaving
594
                    .getAttribute("casesensitive"))).booleanValue();
595
            QueryTerm currentTerm = null;
596
            if (currentPathexpr == null) {
597
                currentTerm = new QueryTerm(isCaseSensitive, leaving
598
                        .getAttribute("searchmode"), currentValue);
599
            } else {
600
                currentTerm = new QueryTerm(isCaseSensitive, leaving
601
                        .getAttribute("searchmode"), currentValue,
602
                        currentPathexpr);
603
            }
604
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
605
            currentGroup.addChild(currentTerm);
606
            currentValue = null;
607
            currentPathexpr = null;
608
        } else if (leaving.getTagName().equals("querygroup")) {
609
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
610
        } else if (leaving.getTagName().equals("meta_file_id")) {
611
              meta_file_id = textBuffer.toString().trim();
612
        } else if (leaving.getTagName().equals("querytitle")) {
613
              queryTitle = textBuffer.toString().trim();
614
        } else if (leaving.getTagName().equals("value")) {
615
              currentValue = textBuffer.toString().trim();
616
        } else if (leaving.getTagName().equals("pathexpr")) {
617
              currentPathexpr = textBuffer.toString().trim();
618
        } else if (leaving.getTagName().equals("returndoctype")) {
619
              returnDocList.add(textBuffer.toString().trim());
620
        } else if (leaving.getTagName().equals("filterdoctype")) {
621
              filterDocList.add(textBuffer.toString().trim());
622
        } else if (leaving.getTagName().equals("returnfield")) {
623
              handleReturnField(textBuffer.toString().trim());
624
        } else if (leaving.getTagName().equals("filterdoctype")) {
625
              filterDocList.add(textBuffer.toString().trim());
626
        } else if (leaving.getTagName().equals("owner")) {
627
              ownerList.add(textBuffer.toString().trim());
628
        } else if (leaving.getTagName().equals("site")) {
629
              siteList.add(textBuffer.toString().trim());
630
        }
631

    
632
        //rest textBuffer
633
        textBuffer = null;
634
        textBuffer = new StringBuffer();
635

    
636
    }
637

    
638
    /**
639
     * callback method used by the SAX Parser when the text sequences of an xml
640
     * stream are detected. Used in this context to parse and store the query
641
     * information in class variables.
642
     */
643
    public void characters(char ch[], int start, int length)
644
    {
645
      // buffer all text nodes for same element. This is for text was splited
646
      // into different nodes
647
      textBuffer.append(new String(ch, start, length));
648

    
649
    }
650

    
651
    /**
652
     * Method to transfer string to return field
653
     */
654
    public void handleReturnField(String inputString)
655
    {
656
        // make sure if return fields has an attribute or not
657
        if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
658
            // no attribute value will be returned
659
            MetaCatUtil.debugMessage("QuerySpecification.handleReturnField(): " , 35);
660
            MetaCatUtil.debugMessage("  there are no attributes in the XPATH statement" , 35);
661
            returnFieldList.add(inputString);
662
            containsExtendedSQL = true;
663
        } else {
664

    
665
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
666

    
667
            // case where the return field is solely an attribute
668
            MetaCatUtil.debugMessage("QuerySpecification.handleReturnField(): " , 35);
669
            MetaCatUtil.debugMessage("  there are *only* attributes in the XPATH statement" , 35);
670
            String returnPath = newPathExpressionWithOutAttribute(inputString);
671
            String attributeName = getAttributeName(inputString);
672
            Vector pathInfo = new Vector();
673
            // the vector has the information about return path and
674
            // attributename
675
            pathInfo.addElement(returnPath);
676
            pathInfo.addElement(attributeName);
677
            // put the vector into a hash table. The reseaon why don't put
678
            // return path or attributename as a key is because they are not
679
            // unique
680
            attributeReturnList.put(new Integer(countAttributeReturnField),
681
                    pathInfo);
682
            countAttributeReturnField++;
683
            hasAttributeReturnField = true;
684
            containsExtendedSQL = true;
685
          } else {
686
            // has a attribute return field
687
            // divied the return filed into two parts, one is path and the
688
            // other is attribue name
689
            MetaCatUtil.debugMessage("QuerySpecification.handleReturnField: " , 35);
690
            MetaCatUtil.debugMessage("  there are both attributes and elements" , 35);
691
            MetaCatUtil.debugMessage("  in the XPATH statement" , 35);
692
            String returnPath = newPathExpressionWithOutAttribute(inputString);
693
            String attributeName = getAttributeName(inputString);
694
            Vector pathInfo = new Vector();
695
            // the vector has the information about return path and
696
            // attributename
697
            pathInfo.addElement(returnPath);
698
            pathInfo.addElement(attributeName);
699
            // put the vector into a hash table. The reseaon why don't put
700
            // return path or attributename as a key is because they are not
701
            // unique
702
            attributeReturnList.put(new Integer(countAttributeReturnField),
703
                    pathInfo);
704
            countAttributeReturnField++;
705
            hasAttributeReturnField = true;
706
            containsExtendedSQL = true;
707
          }
708
        }
709
    }
710

    
711
    /**
712
     * create a SQL serialization of the query that this instance represents
713
     */
714
    public String printSQL(boolean useXMLIndex)
715
    {
716

    
717
        StringBuffer self = new StringBuffer();
718
        StringBuffer queryString = new StringBuffer();
719

    
720
        queryString.append("SELECT docid,docname,doctype,");
721
        queryString.append("date_created, date_updated, rev ");
722
        queryString.append("FROM xml_documents WHERE");
723

    
724
        // Get the query from the QueryGroup and check
725
        // if no query has been returned
726
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
727
        if(!queryFromQueryGroup.trim().equals("")){
728
            self.append(" docid IN (");
729
            self.append(queryFromQueryGroup);
730
            self.append(") ");
731
        }
732

    
733
        // Add SQL to filter for doctypes requested in the query
734
        // This is an implicit OR for the list of doctypes. Only doctypes in
735
        // this
736
        // list will be searched if the tag is present
737
        if (!filterDocList.isEmpty()) {
738
            boolean firstdoctype = true;
739
            boolean emptyString = true;
740

    
741
            if(!self.toString().equals("")){
742
                self.append(" AND (");
743
                emptyString = false;
744
            }
745

    
746
            Enumeration en = filterDocList.elements();
747
            while (en.hasMoreElements()) {
748
                String currentDoctype = (String) en.nextElement();
749
                if (firstdoctype) {
750
                    firstdoctype = false;
751
                    self.append(" doctype = '" + currentDoctype + "'");
752
                } else {
753
                    self.append(" OR doctype = '" + currentDoctype + "'");
754
                }
755
            }
756

    
757
            if(!emptyString){
758
                self.append(") ");
759
            }
760
        }
761

    
762
        // Add SQL to filter for owners requested in the query
763
        // This is an implicit OR for the list of owners
764
        if (!ownerList.isEmpty()) {
765
            boolean first = true;
766
            boolean emptyString = true;
767

    
768
            if(!self.toString().equals("")){
769
                self.append(" AND (");
770
                emptyString = false;
771
            }
772

    
773
            Enumeration en = ownerList.elements();
774
            while (en.hasMoreElements()) {
775
                String current = (String) en.nextElement();
776
                if (current != null) {
777
                    current = current.toLowerCase();
778
                }
779
                if (first) {
780
                    first = false;
781
                    self.append(" lower(user_owner) = '" + current + "'");
782
                } else {
783
                    self.append(" OR lower(user_owner) = '" + current + "'");
784
                }
785
            }
786

    
787
            if(!emptyString){
788
                self.append(") ");
789
            }
790
        }
791

    
792
        // Add SQL to filter for sites requested in the query
793
        // This is an implicit OR for the list of sites
794
        if (!siteList.isEmpty()) {
795
            boolean first = true;
796
            boolean emptyString = true;
797

    
798
            if(!self.toString().equals("")){
799
                self.append(" AND (");
800
                emptyString = false;
801
            }
802

    
803
            Enumeration en = siteList.elements();
804
            while (en.hasMoreElements()) {
805
                String current = (String) en.nextElement();
806
                if (first) {
807
                    first = false;
808
                    self.append(" SUBSTR(docid, 1, INSTR(docid, '"
809
                            + accNumberSeparator + "')-1) = '" + current + "'");
810
                } else {
811
                    self.append(" OR SUBSTR(docid, 1, INSTR(docid, '"
812
                            + accNumberSeparator + "')-1) = '" + current + "'");
813
                }
814
            }
815

    
816
            if(!emptyString){
817
                self.append(") ");
818
            }
819
        }
820

    
821
        // if there is only one percentage search item, this query is a
822
        // percentage
823
        // search query
824
        MetaCatUtil.debugMessage("percentage number: "
825
                + query.getPercentageSymbolCount(), 35);
826
        if (query.getPercentageSymbolCount() == 1) {
827
            MetaCatUtil.debugMessage("it is a percentage search", 30);
828
            percentageSearch = true;
829
        }
830

    
831
        queryString.append(self.toString());
832
        return queryString.toString();
833
    }
834

    
835
    /**
836
     * This sql command will selecet startnodeid and endnodeid that user can
837
     * NOT access
838
     */
839
    public String printAccessControlSQLForReturnField(String doclist)
840
    {
841
        StringBuffer sql = new StringBuffer();
842
        String allowString = constructAllowString();
843
        String denyString = constructDenyString();
844
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
845
        sql.append("WHERE docid in (");
846
        sql.append(doclist);
847
        sql.append(") AND startnodeid IS NOT NULL AND ");
848
        sql.append("(");
849
        sql.append("(");
850
        sql
851
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
852
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
853
        sql.append(" AND lower(xml_documents.user_owner) ='");
854
        sql.append(userName);
855
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
856
        sql.append(")");
857
        sql.append(" AND ");
858
        sql.append("(");
859
        sql
860
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
861
        sql.append(allowString);
862
        sql.append(") AND (startnodeid IS NOT NULL))");
863
        sql.append(")");
864
        sql
865
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
866
        sql.append(denyString);
867
        sql.append(") AND (startnodeid IS NOT NULL))");
868
        sql.append(")");
869
        sql.append(")");
870
        sql.append(")");
871
        MetaCatUtil.debugMessage("accessControlSQLForReturnField: "
872
                + sql.toString(), 30);
873
        return sql.toString();
874
    }
875

    
876
    /**
877
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
878
     * pathquery document. This allows for customization of the returned fields.
879
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
880
     * xml_nodes to find the fields to be included by their path expression, and
881
     * avoids the use of the xml_index table.
882
     *
883
     * @param doclist the list of document ids to search
884
     * @param unaccessableNodePair the node pairs (start id and end id) which
885
     *            this user should not access
886
     * @param useXMLIndex a boolean flag indicating whether to search using
887
     *            xml_index
888
     */
889
    public String printExtendedSQL(String doclist,
890
            Hashtable unaccessableNodePair, boolean useXMLIndex)
891
    {
892
        if (useXMLIndex) {
893
            return printExtendedSQL(doclist, unaccessableNodePair);
894
        } else {
895
            StringBuffer self = new StringBuffer();
896

    
897
            boolean firstfield = true;
898
            //put the returnfields into the query
899
            //the for loop allows for multiple fields
900
            for (int i = 0; i < returnFieldList.size(); i++) {
901
                if (firstfield) {
902
                    firstfield = false;
903
                } else {
904
                    self.append(" UNION ");
905
                }
906
                String path  = (String) returnFieldList.elementAt(i);
907
                self.append("select xml_nodes.docid, ");
908
                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
909
                self.append("xml_nodes.parentnodeid ");
910
                self.append("from xml_nodes, xml_documents ");
911
                self.append("where parentnodeid IN ");
912
                self.append(QueryTerm.useNestedStatements(path));
913

    
914
                self.append(" AND xml_nodes.docid in (");
915
                self.append(doclist);
916
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
917
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
918

    
919
                addAccessRestrictionSQL(unaccessableNodePair, self);
920
            }
921

    
922
            return self.toString();
923
        }
924
    }
925

    
926
    /**
927
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
928
     * pathquery document. This allows for customization of the returned fields.
929
     * It uses the xml_index table and so assumes that this table has been
930
     * built.
931
     *
932
     * @param doclist the list of document ids to search
933
     * @param unaccessableNodePair the node pairs (start id and end id)
934
     *            which this user should not access
935
     */
936
    public String printExtendedSQL(String doclist,
937
            Hashtable unaccessableNodePair)
938
    {
939
        MetaCatUtil.debugMessage("querySpecification.printExtendedSQL called\n", 35);
940
        StringBuffer self = new StringBuffer();
941

    
942
        boolean usePathIndex = true;
943

    
944
        // test if the are elements in the return fields
945
        if ( returnFieldList.size() == 0 ) {
946
            return null;
947
        }
948

    
949
        for (int i = 0; i < returnFieldList.size(); i++) {
950
            if(!MetaCatUtil.pathsForIndexing.contains
951
               (returnFieldList.elementAt(i))){
952
                usePathIndex = false;
953
                break;
954
            }
955
        }
956

    
957
        if(usePathIndex){
958
            self.append("select docid, path, nodedata, parentnodeid ");
959
            self.append("from xml_path_index where (path like '");
960

    
961
            boolean firstfield = true;
962
            //put the returnfields into the query
963
            //the for loop allows for multiple fields
964
            for (int i = 0; i < returnFieldList.size(); i++) {
965
                if (firstfield) {
966
                    firstfield = false;
967
                    self.append( (String) returnFieldList.elementAt(i));
968
                    self.append("' ");
969
                }
970
                else {
971
                    self.append("or path like '");
972
                    self.append( (String) returnFieldList.elementAt(i));
973
                    self.append("' ");
974
                }
975
            }
976
            self.append(") AND docid in (");
977
            self.append(doclist);
978
            self.append(")");
979

    
980
        } else {
981
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
982
            self.append("xml_nodes.parentnodeid ");
983
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
984
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
985

    
986
            boolean firstfield = true;
987
            //put the returnfields into the query
988
            //the for loop allows for multiple fields
989
            for (int i = 0; i < returnFieldList.size(); i++) {
990
                if (firstfield) {
991
                    firstfield = false;
992
                    self.append((String) returnFieldList.elementAt(i));
993
                    self.append("' ");
994
                } else {
995
                    self.append("or xml_index.path like '");
996
                    self.append((String) returnFieldList.elementAt(i));
997
                    self.append("' ");
998
                }
999
            }
1000
            self.append(") AND xml_nodes.docid in (");
1001
            self.append(doclist);
1002
            self.append(") AND xml_nodes.nodetype = 'TEXT'");
1003

    
1004
        }
1005

    
1006
        addAccessRestrictionSQL(unaccessableNodePair, self);
1007

    
1008
        return self.toString();
1009
    }
1010

    
1011

    
1012
    /**
1013
     * Method to return a String generated after sorting the returnFieldList
1014
     * Vector
1015
     */
1016
    public String getSortedReturnFieldString(){
1017
        String returnFields = "";
1018

    
1019
        // Create a temporary vector and copy returnFieldList into it
1020
        Vector tempVector = new Vector();
1021

    
1022
        Iterator it = returnFieldList.iterator();
1023
        while(it.hasNext()){
1024
            tempVector.add(it.next());
1025
        }
1026

    
1027
        Enumeration attEnum = attributeReturnList.elements();
1028
        while(attEnum.hasMoreElements()){
1029
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1030
	    String rfield = "";
1031
            if(tempIt.hasNext()){
1032
		String element = (String)tempIt.next();
1033
		if(element != null) {
1034
		    rfield +=element;
1035
		}
1036
	    }
1037
            if(tempIt.hasNext()){
1038
		String attribute = (String)tempIt.next();
1039
		if(attribute != null) {
1040
  		    rfield = rfield + "@" + attribute;
1041
                }
1042
	    }
1043
            tempVector.add(rfield);
1044
        }
1045

    
1046
        // Sort the temporary vector
1047
        java.util.Collections.sort(tempVector);
1048

    
1049
        // Generate the string and return it
1050
        it = tempVector.iterator();
1051
        while(it.hasNext()){
1052
            returnFields = returnFields + it.next() + "|";
1053
        }
1054
        return returnFields;
1055
    }
1056

    
1057

    
1058
    /**
1059
     * Create the SQl necessary to restrict access to allowed nodes.  This is
1060
     * accomplished by restricting the nodes that are returned to include
1061
     * only those whose IDs fall outside of a set of start/stop pairs of
1062
     * nodeid values.  These pairs are passed in as a hash, with the key
1063
     * containing the start nodeid and the value containing the end nodeid.
1064
     * Any nodes between these start and end nodeid values will be excluded
1065
     * from the results.
1066
     *
1067
     * @param unaccessableNodePair hash of start/end nodeid pairs to restrict
1068
     * @param self a stringbuffer to which the genrated SQL is appended
1069
     */
1070
    private void addAccessRestrictionSQL(Hashtable unaccessableNodePair,
1071
            StringBuffer self)
1072
    {
1073
        // add control part for extended query
1074
        Enumeration en = unaccessableNodePair.keys();
1075

    
1076
        while (en.hasMoreElements()) {
1077
            // Get control pairs in object
1078
            Long startNodeIdObject = (Long) en.nextElement();
1079
            Long endNodeIdObject = (Long) unaccessableNodePair
1080
                    .get(startNodeIdObject);
1081
            // change it to long
1082
            long startNodeId = startNodeIdObject.longValue();
1083
            long endNodeId = endNodeIdObject.longValue();
1084
            // add into query
1085
            self.append(" AND ( xml_nodes.nodeid < ");
1086
            self.append(startNodeId);
1087
            self.append(" OR xml_nodes.nodeid > ");
1088
            self.append(endNodeId);
1089
            self.append(")");
1090
        }
1091
    }
1092

    
1093
    /**
1094
     * This method prints sql that finds the values of attributes in the xml
1095
     * documents based upon the whether the returnfield tag in the pathquery
1096
     * document has an attribute symbol (@). This allows for customization of
1097
     * the returned fields.
1098
     *
1099
     * @param doclist the list of document ids to search
1100
     * @param useXMLIndex a boolean flag indicating whether to search using
1101
     *            xml_index
1102
     */
1103
    public String printAttributeQuery(String doclist, boolean useXMLIndex)
1104
    {
1105
        if (useXMLIndex) {
1106
            return printAttributeQuery(doclist);
1107
        } else {
1108
            StringBuffer self = new StringBuffer();
1109
            boolean firstfield = true;
1110
            //put the returnfields attributes into the query
1111
            //the for loop allows for multiple fields and attributes
1112
            Enumeration returnAttributes = attributeReturnList.elements();
1113
            while (returnAttributes.hasMoreElements()) {
1114
                Vector currentVector = (Vector) returnAttributes.nextElement();
1115
                String returnPath = (String) currentVector.elementAt(0);
1116
                String attributeName = (String) currentVector.elementAt(1);
1117
                if (firstfield) {
1118
                    firstfield = false;
1119
                } else {
1120
                    self.append(" UNION ");
1121
                }
1122
                self.append("select xml_nodes.docid, '");
1123
                self.append(returnPath);
1124
                self.append("' as path, xml_nodes.nodedata, xml_nodes.nodename ");
1125
                self.append("from xml_nodes, xml_documents ");
1126
                self.append("where parentnodeid IN ");
1127
                self.append(QueryTerm.useNestedStatements(returnPath));
1128
                self.append(" AND xml_nodes.nodename like '");
1129
                self.append(attributeName);
1130
                self.append("' AND xml_nodes.docid in (");
1131
                self.append(doclist);
1132
                self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1133
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
1134
            }
1135

    
1136
            MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
1137

    
1138
            return self.toString();
1139
        }
1140
    }
1141

    
1142
    /**
1143
     * This method prints sql that finds the values of attributes in the xml
1144
     * documents based upon the whether the returnfield tag in the pathquery
1145
     * document has an attribute symbol (@). This allows for customization of
1146
     * the returned fields.
1147
     *
1148
     * @param doclist the list of document ids to search
1149
     */
1150
    public String printAttributeQuery(String doclist)
1151
    {
1152
        StringBuffer self = new StringBuffer();
1153
        self.append("select xml_nodes.docid, xml_index.path, ");
1154
        self.append("xml_nodes.nodedata, xml_nodes.nodename ");
1155
        self.append("from xml_index, xml_nodes where xml_index.nodeid=");
1156
        self.append("xml_nodes.parentnodeid and (");
1157
        boolean firstfield = true;
1158
        //put the returnfields attributes into the query
1159
        //the for loop allows for multiple fields and attributes
1160
        Enumeration returnAttributes = attributeReturnList.elements();
1161
        while (returnAttributes.hasMoreElements()) {
1162
            Vector currentVector = (Vector) returnAttributes.nextElement();
1163
            String returnPath = (String) currentVector.elementAt(0);
1164
            String attributeName = (String) currentVector.elementAt(1);
1165
            if (firstfield) {
1166
                firstfield = false;
1167
                self.append("( ");
1168
                if(returnPath != null){
1169
                    self.append("xml_index.path like '");
1170
                    self.append(returnPath);
1171
                    self.append("' AND ");
1172
                }else {
1173
                  MetaCatUtil.debugMessage("QuerySpecification.printAttributeQuery: "
1174
                   + "returnPath is: " + returnPath, 30);
1175
                }
1176
                self.append("xml_nodes.nodename like '");
1177
                self.append(attributeName);
1178
                self.append("') ");
1179
            } else {
1180
                self.append(" or (");
1181
                if(returnPath != null){
1182
                    self.append("xml_index.path like '");
1183
                    self.append(returnPath);
1184
                    self.append("' AND ");
1185
                }else {
1186
                  MetaCatUtil.debugMessage("QuerySpecification.printAttributeQuery: "
1187
                   + "returnPath is null: " + returnPath, 30);
1188
                }
1189
                self.append("xml_nodes.nodename like '");
1190
                self.append(attributeName);
1191
                self.append("') ");
1192
            }
1193
        }
1194
        self.append(") AND xml_nodes.docid in (");
1195
        self.append(doclist);
1196
        self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1197
        MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
1198

    
1199
        return self.toString();
1200
    }
1201

    
1202
    public static String printRelationSQL(String docid)
1203
    {
1204
        StringBuffer self = new StringBuffer();
1205
        self.append("select subject, relationship, object, subdoctype, ");
1206
        self.append("objdoctype from xml_relation ");
1207
        self.append("where docid like '").append(docid).append("'");
1208
        return self.toString();
1209
    }
1210

    
1211
    public static String printGetDocByDoctypeSQL(String docid)
1212
    {
1213
        StringBuffer self = new StringBuffer();
1214

    
1215
        self.append("SELECT docid,docname,doctype,");
1216
        self.append("date_created, date_updated ");
1217
        self.append("FROM xml_documents WHERE docid IN (");
1218
        self.append(docid).append(")");
1219
        return self.toString();
1220
    }
1221

    
1222
    /**
1223
     * create a String description of the query that this instance represents.
1224
     * This should become a way to get the XML serialization of the query.
1225
     */
1226
    public String toString()
1227
    {
1228
        return "meta_file_id=" + meta_file_id + "\n" + query;
1229
        //DOCTITLE attr cleared from the db
1230
        //return "meta_file_id=" + meta_file_id + "\n" +
1231
        //"querytitle=" + querytitle + "\n" + query;
1232
    }
1233

    
1234
    /** A method to get rid of attribute part in path expression */
1235
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1236
    {
1237
        if (pathExpression == null) { return null; }
1238
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1239
        String newExpression = null;
1240
        if (index != 0) {
1241
            newExpression = pathExpression.substring(0, index - 1);
1242
        }
1243
        MetaCatUtil.debugMessage("The path expression without attributes: "
1244
                + newExpression, 30);
1245
        return newExpression;
1246
    }
1247

    
1248
    /** A method to get attribute name from path */
1249
    public static String getAttributeName(String path)
1250
    {
1251
        if (path == null) { return null; }
1252
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1253
        int size = path.length();
1254
        String attributeName = null;
1255
        if (index != 1) {
1256
            attributeName = path.substring(index + 1, size);
1257
        }
1258
        MetaCatUtil.debugMessage("The attirbute name from path: "
1259
                + attributeName, 30);
1260
        return attributeName;
1261
    }
1262

    
1263
}
(52-52/63)