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-03-17 16:42:25 -0800 (Thu, 17 Mar 2005) $'
14
 * '$Revision: 2419 $'
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 containAttributeReturnField()
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
            returnFieldList.add(inputString);
660
            containsExtendedSQL = true;
661
        } else {
662
            // has a attribute return field
663
            // divied the return filed into two parts, one is path and the
664
            // other is attribue name
665
            String returnPath = newPathExpressionWithOutAttribute(inputString);
666
            String attributeName = getAttributeName(inputString);
667
            Vector pathInfo = new Vector();
668
            // the vector has the information about return path and
669
            // attributename
670
            pathInfo.addElement(returnPath);
671
            pathInfo.addElement(attributeName);
672
            // put the vector into a hash table. The reseaon why don't put
673
            // return path or attributename as a key is because they are not
674
            // unique
675
            attributeReturnList.put(new Integer(countAttributeReturnField),
676
                    pathInfo);
677
            countAttributeReturnField++;
678
            hasAttributeReturnField = true;
679
            containsExtendedSQL = true;
680
        }
681
    }
682

    
683
    /**
684
     * create a SQL serialization of the query that this instance represents
685
     */
686
    public String printSQL(boolean useXMLIndex)
687
    {
688

    
689
        StringBuffer self = new StringBuffer();
690
        StringBuffer queryString = new StringBuffer();
691

    
692
        queryString.append("SELECT docid,docname,doctype,");
693
        queryString.append("date_created, date_updated, rev ");
694
        queryString.append("FROM xml_documents WHERE");
695

    
696
        // Get the query from the QueryGroup and check
697
        // if no query has been returned
698
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
699
        if(!queryFromQueryGroup.trim().equals("")){
700
            self.append(" docid IN (");
701
            self.append(queryFromQueryGroup);
702
            self.append(") ");
703
        }
704

    
705
        // Add SQL to filter for doctypes requested in the query
706
        // This is an implicit OR for the list of doctypes. Only doctypes in
707
        // this
708
        // list will be searched if the tag is present
709
        if (!filterDocList.isEmpty()) {
710
            boolean firstdoctype = true;
711
            boolean emptyString = true;
712

    
713
            if(!self.toString().equals("")){
714
                self.append(" AND (");
715
                emptyString = false;
716
            }
717

    
718
            Enumeration en = filterDocList.elements();
719
            while (en.hasMoreElements()) {
720
                String currentDoctype = (String) en.nextElement();
721
                if (firstdoctype) {
722
                    firstdoctype = false;
723
                    self.append(" doctype = '" + currentDoctype + "'");
724
                } else {
725
                    self.append(" OR doctype = '" + currentDoctype + "'");
726
                }
727
            }
728

    
729
            if(!emptyString){
730
                self.append(") ");
731
            }
732
        }
733

    
734
        // Add SQL to filter for owners requested in the query
735
        // This is an implicit OR for the list of owners
736
        if (!ownerList.isEmpty()) {
737
            boolean first = true;
738
            boolean emptyString = true;
739

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

    
745
            Enumeration en = ownerList.elements();
746
            while (en.hasMoreElements()) {
747
                String current = (String) en.nextElement();
748
                if (current != null) {
749
                    current = current.toLowerCase();
750
                }
751
                if (first) {
752
                    first = false;
753
                    self.append(" lower(user_owner) = '" + current + "'");
754
                } else {
755
                    self.append(" OR lower(user_owner) = '" + current + "'");
756
                }
757
            }
758

    
759
            if(!emptyString){
760
                self.append(") ");
761
            }
762
        }
763

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

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

    
775
            Enumeration en = siteList.elements();
776
            while (en.hasMoreElements()) {
777
                String current = (String) en.nextElement();
778
                if (first) {
779
                    first = false;
780
                    self.append(" SUBSTR(docid, 1, INSTR(docid, '"
781
                            + accNumberSeparator + "')-1) = '" + current + "'");
782
                } else {
783
                    self.append(" OR SUBSTR(docid, 1, INSTR(docid, '"
784
                            + accNumberSeparator + "')-1) = '" + current + "'");
785
                }
786
            }
787

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

    
793
        // if there is only one percentage search item, this query is a
794
        // percentage
795
        // search query
796
        MetaCatUtil.debugMessage("percentage number: "
797
                + query.getPercentageSymbolCount(), 35);
798
        if (query.getPercentageSymbolCount() == 1) {
799
            MetaCatUtil.debugMessage("it is a percentage search", 30);
800
            percentageSearch = true;
801
        }
802

    
803
        queryString.append(self.toString());
804
        return queryString.toString();
805
    }
806

    
807
    /**
808
     * This sql command will selecet startnodeid and endnodeid that user can
809
     * NOT access
810
     */
811
    public String printAccessControlSQLForReturnField(String doclist)
812
    {
813
        StringBuffer sql = new StringBuffer();
814
        String allowString = constructAllowString();
815
        String denyString = constructDenyString();
816
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
817
        sql.append("WHERE docid in (");
818
        sql.append(doclist);
819
        sql.append(") AND startnodeid IS NOT NULL AND ");
820
        sql.append("(");
821
        sql.append("(");
822
        sql
823
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
824
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
825
        sql.append(" AND lower(xml_documents.user_owner) ='");
826
        sql.append(userName);
827
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
828
        sql.append(")");
829
        sql.append(" AND ");
830
        sql.append("(");
831
        sql
832
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
833
        sql.append(allowString);
834
        sql.append(") AND (startnodeid IS NOT NULL))");
835
        sql.append(")");
836
        sql
837
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
838
        sql.append(denyString);
839
        sql.append(") AND (startnodeid IS NOT NULL))");
840
        sql.append(")");
841
        sql.append(")");
842
        sql.append(")");
843
        MetaCatUtil.debugMessage("accessControlSQLForReturnField: "
844
                + sql.toString(), 30);
845
        return sql.toString();
846
    }
847

    
848
    /**
849
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
850
     * pathquery document. This allows for customization of the returned fields.
851
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
852
     * xml_nodes to find the fields to be included by their path expression, and
853
     * avoids the use of the xml_index table.
854
     *
855
     * @param doclist the list of document ids to search
856
     * @param unaccessableNodePair the node pairs (start id and end id) which
857
     *            this user should not access
858
     * @param useXMLIndex a boolean flag indicating whether to search using
859
     *            xml_index
860
     */
861
    public String printExtendedSQL(String doclist,
862
            Hashtable unaccessableNodePair, boolean useXMLIndex)
863
    {
864
        if (useXMLIndex) {
865
            return printExtendedSQL(doclist, unaccessableNodePair);
866
        } else {
867
            StringBuffer self = new StringBuffer();
868

    
869
            boolean firstfield = true;
870
            //put the returnfields into the query
871
            //the for loop allows for multiple fields
872
            for (int i = 0; i < returnFieldList.size(); i++) {
873
                if (firstfield) {
874
                    firstfield = false;
875
                } else {
876
                    self.append(" UNION ");
877
                }
878
                String path  = (String) returnFieldList.elementAt(i);
879
                self.append("select xml_nodes.docid, ");
880
                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
881
                self.append("xml_nodes.parentnodeid ");
882
                self.append("from xml_nodes, xml_documents ");
883
                self.append("where parentnodeid IN ");
884
                self.append(QueryTerm.useNestedStatements(path));
885

    
886
                self.append(" AND xml_nodes.docid in (");
887
                self.append(doclist);
888
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
889
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
890

    
891
                addAccessRestrictionSQL(unaccessableNodePair, self);
892
            }
893

    
894
            return self.toString();
895
        }
896
    }
897

    
898
    /**
899
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
900
     * pathquery document. This allows for customization of the returned fields.
901
     * It uses the xml_index table and so assumes that this table has been
902
     * built.
903
     *
904
     * @param doclist the list of document ids to search
905
     * @param unaccessableNodePair the node pairs (start id and end id)
906
     *            which this user should not access
907
     */
908
    public String printExtendedSQL(String doclist,
909
            Hashtable unaccessableNodePair)
910
    {
911
        StringBuffer self = new StringBuffer();
912
        self.append("select docid, path, nodedata, ");
913
        self.append("nodeid ");
914
        self.append("from xml_index where (path like '");
915
        boolean firstfield = true;
916
        //put the returnfields into the query
917
        //the for loop allows for multiple fields
918
        for (int i = 0; i < returnFieldList.size(); i++) {
919
            if (firstfield) {
920
                firstfield = false;
921
                self.append((String) returnFieldList.elementAt(i));
922
                self.append("' ");
923
            } else {
924
                self.append("or path like '");
925
                self.append((String) returnFieldList.elementAt(i));
926
                self.append("' ");
927
            }
928
        }
929
        self.append(") AND docid in (");
930
        self.append(doclist);
931
        self.append(")");
932
        //self.append(") AND xml_nodes.nodetype = 'TEXT'");
933

    
934
        addAccessRestrictionSQL(unaccessableNodePair, self);
935

    
936
        return self.toString();
937
    }
938

    
939

    
940
    /**
941
     * Method to return a String generated after sorting the returnFieldList
942
     * Vector
943
     */
944
    public String getSortedReturnFieldString(){
945
        String returnFields = "";
946

    
947
        // Create a temporary vector and copy returnFieldList into it
948
        Vector tempVector = new Vector();
949
        Iterator it = returnFieldList.iterator();
950
        while(it.hasNext()){
951
            tempVector.add(it.next());
952
        }
953

    
954
        // Sort the temporary vector
955
        java.util.Collections.sort(tempVector);
956

    
957
        // Generate the string and return it
958
        it = tempVector.iterator();
959
        while(it.hasNext()){
960
            returnFields = returnFields + it.next() + "|";
961
        }
962
        return returnFields;
963
    }
964

    
965

    
966
    /**
967
     * Create the SQl necessary to restrict access to allowed nodes.  This is
968
     * accomplished by restricting the nodes that are returned to include
969
     * only those whose IDs fall outside of a set of start/stop pairs of
970
     * nodeid values.  These pairs are passed in as a hash, with the key
971
     * containing the start nodeid and the value containing the end nodeid.
972
     * Any nodes between these start and end nodeid values will be excluded
973
     * from the results.
974
     *
975
     * @param unaccessableNodePair hash of start/end nodeid pairs to restrict
976
     * @param self a stringbuffer to which the genrated SQL is appended
977
     */
978
    private void addAccessRestrictionSQL(Hashtable unaccessableNodePair,
979
            StringBuffer self)
980
    {
981
        // add control part for extended query
982
        Enumeration en = unaccessableNodePair.keys();
983

    
984
        while (en.hasMoreElements()) {
985
            // Get control pairs in object
986
            Long startNodeIdObject = (Long) en.nextElement();
987
            Long endNodeIdObject = (Long) unaccessableNodePair
988
                    .get(startNodeIdObject);
989
            // change it to long
990
            long startNodeId = startNodeIdObject.longValue();
991
            long endNodeId = endNodeIdObject.longValue();
992
            // add into query
993
            self.append(" AND ( xml_nodes.nodeid < ");
994
            self.append(startNodeId);
995
            self.append(" OR xml_nodes.nodeid > ");
996
            self.append(endNodeId);
997
            self.append(")");
998
        }
999
    }
1000

    
1001
    /**
1002
     * This method prints sql that finds the values of attributes in the xml
1003
     * documents based upon the whether the returnfield tag in the pathquery
1004
     * document has an attribute symbol (@). This allows for customization of
1005
     * the returned fields.
1006
     *
1007
     * @param doclist the list of document ids to search
1008
     * @param useXMLIndex a boolean flag indicating whether to search using
1009
     *            xml_index
1010
     */
1011
    public String printAttributeQuery(String doclist, boolean useXMLIndex)
1012
    {
1013
        if (useXMLIndex) {
1014
            return printAttributeQuery(doclist);
1015
        } else {
1016
            StringBuffer self = new StringBuffer();
1017
            boolean firstfield = true;
1018
            //put the returnfields attributes into the query
1019
            //the for loop allows for multiple fields and attributes
1020
            Enumeration returnAttributes = attributeReturnList.elements();
1021
            while (returnAttributes.hasMoreElements()) {
1022
                Vector currentVector = (Vector) returnAttributes.nextElement();
1023
                String returnPath = (String) currentVector.elementAt(0);
1024
                String attributeName = (String) currentVector.elementAt(1);
1025
                if (firstfield) {
1026
                    firstfield = false;
1027
                } else {
1028
                    self.append(" UNION ");
1029
                }
1030
                self.append("select xml_nodes.docid, '");
1031
                self.append(returnPath);
1032
                self.append("' as path, xml_nodes.nodedata, xml_nodes.nodename ");
1033
                self.append("from xml_nodes, xml_documents ");
1034
                self.append("where parentnodeid IN ");
1035
                self.append(QueryTerm.useNestedStatements(returnPath));
1036
                self.append(" AND xml_nodes.nodename like '");
1037
                self.append(attributeName);
1038
                self.append("' AND xml_nodes.docid in (");
1039
                self.append(doclist);
1040
                self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1041
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
1042
            }
1043

    
1044
            MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
1045

    
1046
            return self.toString();
1047
        }
1048
    }
1049

    
1050
    /**
1051
     * This method prints sql that finds the values of attributes in the xml
1052
     * documents based upon the whether the returnfield tag in the pathquery
1053
     * document has an attribute symbol (@). This allows for customization of
1054
     * the returned fields.
1055
     *
1056
     * @param doclist the list of document ids to search
1057
     */
1058
    public String printAttributeQuery(String doclist)
1059
    {
1060
        StringBuffer self = new StringBuffer();
1061
        self.append("select xml_nodes.docid, xml_index.path, ");
1062
        self.append("xml_nodes.nodedata, xml_nodes.nodename ");
1063
        self.append("from xml_index, xml_nodes where xml_index.nodeid=");
1064
        self.append("xml_nodes.parentnodeid and (");
1065
        boolean firstfield = true;
1066
        //put the returnfields attributes into the query
1067
        //the for loop allows for multiple fields and attributes
1068
        Enumeration returnAttributes = attributeReturnList.elements();
1069
        while (returnAttributes.hasMoreElements()) {
1070
            Vector currentVector = (Vector) returnAttributes.nextElement();
1071
            String returnPath = (String) currentVector.elementAt(0);
1072
            String attributeName = (String) currentVector.elementAt(1);
1073
            if (firstfield) {
1074
                firstfield = false;
1075
                self.append("( xml_index.path like '");
1076
                self.append(returnPath);
1077
                self.append("' AND xml_nodes.nodename like '");
1078
                self.append(attributeName);
1079
                self.append("') ");
1080
            } else {
1081
                self.append(" or ( xml_index.path like '");
1082
                self.append(returnPath);
1083
                self.append("' AND xml_nodes.nodename like '");
1084
                self.append(attributeName);
1085
                self.append("') ");
1086
            }
1087
        }
1088
        self.append(") AND xml_nodes.docid in (");
1089
        self.append(doclist);
1090
        self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1091
        MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
1092

    
1093
        return self.toString();
1094
    }
1095

    
1096
    public static String printRelationSQL(String docid)
1097
    {
1098
        StringBuffer self = new StringBuffer();
1099
        self.append("select subject, relationship, object, subdoctype, ");
1100
        self.append("objdoctype from xml_relation ");
1101
        self.append("where docid like '").append(docid).append("'");
1102
        return self.toString();
1103
    }
1104

    
1105
    public static String printGetDocByDoctypeSQL(String docid)
1106
    {
1107
        StringBuffer self = new StringBuffer();
1108

    
1109
        self.append("SELECT docid,docname,doctype,");
1110
        self.append("date_created, date_updated ");
1111
        self.append("FROM xml_documents WHERE docid IN (");
1112
        self.append(docid).append(")");
1113
        return self.toString();
1114
    }
1115

    
1116
    /**
1117
     * create a String description of the query that this instance represents.
1118
     * This should become a way to get the XML serialization of the query.
1119
     */
1120
    public String toString()
1121
    {
1122
        return "meta_file_id=" + meta_file_id + "\n" + query;
1123
        //DOCTITLE attr cleared from the db
1124
        //return "meta_file_id=" + meta_file_id + "\n" +
1125
        //"querytitle=" + querytitle + "\n" + query;
1126
    }
1127

    
1128
    /** A method to get rid of attribute part in path expression */
1129
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1130
    {
1131
        if (pathExpression == null) { return null; }
1132
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1133
        String newExpression = null;
1134
        if (index != 1) {
1135
            newExpression = pathExpression.substring(0, index - 1);
1136
        }
1137
        MetaCatUtil.debugMessage("The path expression without attributes: "
1138
                + newExpression, 30);
1139
        return newExpression;
1140
    }
1141

    
1142
    /** A method to get attribute name from path */
1143
    public static String getAttributeName(String path)
1144
    {
1145
        if (path == null) { return null; }
1146
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1147
        int size = path.length();
1148
        String attributeName = null;
1149
        if (index != 1) {
1150
            attributeName = path.substring(index + 1, size);
1151
        }
1152
        MetaCatUtil.debugMessage("The attirbute name from path: "
1153
                + attributeName, 30);
1154
        return attributeName;
1155
    }
1156

    
1157
}
(52-52/63)