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-09-07 13:07:14 -0700 (Wed, 07 Sep 2005) $'
14
 * '$Revision: 2561 $'
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
    /** The root query group that contains the recursive query constraints */
83
    private QueryGroup query = null;
84

    
85
    // Query data structures used temporarily during XML parsing
86
    private Stack elementStack;
87

    
88
    private Stack queryStack;
89

    
90
    private String currentValue;
91

    
92
    private String currentPathexpr;
93

    
94
    private String parserName = null;
95

    
96
    private String accNumberSeparator = null;
97

    
98
    private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
99

    
100
    private boolean percentageSearch = false;
101

    
102
    private String userName = null;
103

    
104
    private static final String PUBLIC = "public";
105

    
106
    private String[] group = null;
107

    
108
    public static final String ATTRIBUTESYMBOL = "@";
109

    
110
    private boolean hasAttributeReturnField = false;
111

    
112
    private Hashtable attributeReturnList = new Hashtable();
113

    
114
    private int countAttributeReturnField = 0;
115

    
116
    private StringBuffer textBuffer = new StringBuffer();
117

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

    
133
        // Initialize the class variables
134
        returnDocList = new Vector();
135
        filterDocList = new Vector();
136
        elementStack = new Stack();
137
        queryStack = new Stack();
138
        returnFieldList = new Vector();
139
        ownerList = new Vector();
140
        this.parserName = parserName;
141
        this.accNumberSeparator = accNumberSeparator;
142

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

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

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

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

    
208
    /**
209
     * Method to set user group
210
     *
211
     * @param myGroup
212
     *            the user group
213
     */
214
    public void setGroup(String[] myGroup)
215
    {
216
        this.group = myGroup;
217
    }
218

    
219
    /**
220
     * Method to indicate this query is a percentage search
221
     */
222
    public boolean isPercentageSearch()
223
    {
224
        return percentageSearch;
225
    }
226

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

    
238
        MetaCatUtil.debugMessage("OwnerQuery: " + ownerQuery, 30);
239
        return ownerQuery;
240
    }
241

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

    
255
    }
256

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

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

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

    
302
    }
303

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

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

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

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

    
369
    /**
370
     * A method to get if the query has an attribute return field
371
     */
372
    public boolean containsAttributeReturnField()
373
    {
374
        return hasAttributeReturnField;
375
    }
376

    
377
    /**
378
     * Accessor method to return the identifier of this Query
379
     */
380
    public String getIdentifier()
381
    {
382
        return meta_file_id;
383
    }
384

    
385
    /**
386
     * method to set the identifier of this query
387
     */
388
    public void setIdentifier(String id)
389
    {
390
        this.meta_file_id = id;
391
    }
392

    
393
    /**
394
     * Accessor method to return the title of this Query
395
     */
396
    public String getQueryTitle()
397
    {
398
        return queryTitle;
399
    }
400

    
401
    /**
402
     * method to set the title of this query
403
     */
404
    public void setQueryTitle(String title)
405
    {
406
        this.queryTitle = title;
407
    }
408

    
409
    /**
410
     * Accessor method to return a vector of the return document types as
411
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
412
     */
413
    public Vector getReturnDocList()
414
    {
415
        return this.returnDocList;
416
    }
417

    
418
    /**
419
     * method to set the list of return docs of this query
420
     */
421
    public void setReturnDocList(Vector returnDocList)
422
    {
423
        this.returnDocList = returnDocList;
424
    }
425

    
426
    /**
427
     * Accessor method to return a vector of the filter doc types as defined in
428
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
429
     */
430
    public Vector getFilterDocList()
431
    {
432
        return this.filterDocList;
433
    }
434

    
435
    /**
436
     * method to set the list of filter docs of this query
437
     */
438
    public void setFilterDocList(Vector filterDocList)
439
    {
440
        this.filterDocList = filterDocList;
441
    }
442

    
443
    /**
444
     * Accessor method to return a vector of the extended return fields as
445
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
446
     */
447
    public Vector getReturnFieldList()
448
    {
449
        return this.returnFieldList;
450
    }
451

    
452
    /**
453
     * method to set the list of fields to be returned by this query
454
     */
455
    public void setReturnFieldList(Vector returnFieldList)
456
    {
457
        this.returnFieldList = returnFieldList;
458
    }
459

    
460
    /**
461
     * Accessor method to return a vector of the owner fields as defined in the
462
     * &lt;owner&gt; tag in the pathquery dtd.
463
     */
464
    public Vector getOwnerList()
465
    {
466
        return this.ownerList;
467
    }
468

    
469
    /**
470
     * method to set the list of owners used to constrain this query
471
     */
472
    public void setOwnerList(Vector ownerList)
473
    {
474
        this.ownerList = ownerList;
475
    }
476

    
477
    /**
478
     * get the QueryGroup used to express query constraints
479
     */
480
    public QueryGroup getQueryGroup()
481
    {
482
        return query;
483
    }
484

    
485
    /**
486
     * set the querygroup
487
     */
488
    public void setQueryGroup(QueryGroup group)
489
    {
490
        query = group;
491
    }
492

    
493
    /**
494
     * set if this query sepcification has extendQuery(has return doc type or
495
     * not)
496
     */
497
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
498
    {
499
        containsExtendedSQL = hasExtenedQuery;
500
    }
501

    
502
    /**
503
     * Set up the SAX parser for reading the XML serialized query
504
     */
505
    private XMLReader initializeParser()
506
    {
507
        XMLReader parser = null;
508

    
509
        // Set up the SAX document handlers for parsing
510
        try {
511

    
512
            // Get an instance of the parser
513
            parser = XMLReaderFactory.createXMLReader(parserName);
514

    
515
            // Set the ContentHandler to this instance
516
            parser.setContentHandler(this);
517

    
518
            // Set the error Handler to this instance
519
            parser.setErrorHandler(this);
520

    
521
        } catch (Exception e) {
522
            System.err.println("Error in QuerySpcecification.initializeParser "
523
                    + e.toString());
524
        }
525

    
526
        return parser;
527
    }
528

    
529
    /**
530
     * callback method used by the SAX Parser when the start tag of an element
531
     * is detected. Used in this context to parse and store the query
532
     * information in class variables.
533
     */
534
    public void startElement(String uri, String localName, String qName,
535
            Attributes atts) throws SAXException
536
    {
537
        BasicNode currentNode = new BasicNode(localName);
538
        // add attributes to BasicNode here
539
        if (atts != null) {
540
            int len = atts.getLength();
541
            for (int i = 0; i < len; i++) {
542
                currentNode
543
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
544
            }
545
        }
546

    
547
        elementStack.push(currentNode);
548
        if (currentNode.getTagName().equals("querygroup")) {
549
            QueryGroup currentGroup = new QueryGroup(currentNode
550
                    .getAttribute("operator"));
551
            if (query == null) {
552
                query = currentGroup;
553
            } else {
554
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
555
                parentGroup.addChild(currentGroup);
556
            }
557
            queryStack.push(currentGroup);
558
        }
559
    }
560

    
561
    /**
562
     * callback method used by the SAX Parser when the end tag of an element is
563
     * detected. Used in this context to parse and store the query information
564
     * in class variables.
565
     */
566
    public void endElement(String uri, String localName, String qName)
567
            throws SAXException
568
    {
569
        BasicNode leaving = (BasicNode) elementStack.pop();
570
        if (leaving.getTagName().equals("queryterm")) {
571
            boolean isCaseSensitive = (new Boolean(leaving
572
                    .getAttribute("casesensitive"))).booleanValue();
573
            QueryTerm currentTerm = null;
574
            if (currentPathexpr == null) {
575
                currentTerm = new QueryTerm(isCaseSensitive, leaving
576
                        .getAttribute("searchmode"), currentValue);
577
            } else {
578
                currentTerm = new QueryTerm(isCaseSensitive, leaving
579
                        .getAttribute("searchmode"), currentValue,
580
                        currentPathexpr);
581
            }
582
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
583
            currentGroup.addChild(currentTerm);
584
            currentValue = null;
585
            currentPathexpr = null;
586
        } else if (leaving.getTagName().equals("querygroup")) {
587
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
588
        } else if (leaving.getTagName().equals("meta_file_id")) {
589
              meta_file_id = textBuffer.toString().trim();
590
        } else if (leaving.getTagName().equals("querytitle")) {
591
              queryTitle = textBuffer.toString().trim();
592
        } else if (leaving.getTagName().equals("value")) {
593
              currentValue = textBuffer.toString().trim();
594
        } else if (leaving.getTagName().equals("pathexpr")) {
595
              currentPathexpr = textBuffer.toString().trim();
596
        } else if (leaving.getTagName().equals("returndoctype")) {
597
              returnDocList.add(textBuffer.toString().trim());
598
        } else if (leaving.getTagName().equals("filterdoctype")) {
599
              filterDocList.add(textBuffer.toString().trim());
600
        } else if (leaving.getTagName().equals("returnfield")) {
601
              handleReturnField(textBuffer.toString().trim());
602
        } else if (leaving.getTagName().equals("filterdoctype")) {
603
              filterDocList.add(textBuffer.toString().trim());
604
        } else if (leaving.getTagName().equals("owner")) {
605
              ownerList.add(textBuffer.toString().trim());
606
        }
607

    
608
        //rest textBuffer
609
        textBuffer = null;
610
        textBuffer = new StringBuffer();
611

    
612
    }
613

    
614
    /**
615
     * callback method used by the SAX Parser when the text sequences of an xml
616
     * stream are detected. Used in this context to parse and store the query
617
     * information in class variables.
618
     */
619
    public void characters(char ch[], int start, int length)
620
    {
621
      // buffer all text nodes for same element. This is for text was splited
622
      // into different nodes
623
      textBuffer.append(new String(ch, start, length));
624

    
625
    }
626

    
627
    /**
628
     * Method to transfer string to return field
629
     */
630
    public void handleReturnField(String inputString)
631
    {
632
        // make sure if return fields has an attribute or not
633
        if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
634
            // no attribute value will be returned
635
            MetaCatUtil.debugMessage("QuerySpecification.handleReturnField(): " , 35);
636
            MetaCatUtil.debugMessage("  there are no attributes in the XPATH statement" , 35);
637
            returnFieldList.add(inputString);
638
            containsExtendedSQL = true;
639
        } else {
640

    
641
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
642

    
643
            // case where the return field is solely an attribute
644
            MetaCatUtil.debugMessage("QuerySpecification.handleReturnField(): " , 35);
645
            MetaCatUtil.debugMessage("  there are *only* attributes in the XPATH statement" , 35);
646
            String returnPath = newPathExpressionWithOutAttribute(inputString);
647
            String attributeName = getAttributeName(inputString);
648
            Vector pathInfo = new Vector();
649
            // the vector has the information about return path and
650
            // attributename
651
            pathInfo.addElement(returnPath);
652
            pathInfo.addElement(attributeName);
653
            // put the vector into a hash table. The reseaon why don't put
654
            // return path or attributename as a key is because they are not
655
            // unique
656
            attributeReturnList.put(new Integer(countAttributeReturnField),
657
                    pathInfo);
658
            countAttributeReturnField++;
659
            hasAttributeReturnField = true;
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
            MetaCatUtil.debugMessage("QuerySpecification.handleReturnField: " , 35);
666
            MetaCatUtil.debugMessage("  there are both attributes and elements" , 35);
667
            MetaCatUtil.debugMessage("  in the XPATH statement" , 35);
668
            String returnPath = newPathExpressionWithOutAttribute(inputString);
669
            String attributeName = getAttributeName(inputString);
670
            Vector pathInfo = new Vector();
671
            // the vector has the information about return path and
672
            // attributename
673
            pathInfo.addElement(returnPath);
674
            pathInfo.addElement(attributeName);
675
            // put the vector into a hash table. The reseaon why don't put
676
            // return path or attributename as a key is because they are not
677
            // unique
678
            attributeReturnList.put(new Integer(countAttributeReturnField),
679
                    pathInfo);
680
            countAttributeReturnField++;
681
            hasAttributeReturnField = true;
682
            containsExtendedSQL = true;
683
          }
684
        }
685
    }
686

    
687
    /**
688
     * create a SQL serialization of the query that this instance represents
689
     */
690
    public String printSQL(boolean useXMLIndex)
691
    {
692

    
693
        StringBuffer self = new StringBuffer();
694
        StringBuffer queryString = new StringBuffer();
695

    
696
        queryString.append("SELECT docid,docname,doctype,");
697
        queryString.append("date_created, date_updated, rev ");
698
        queryString.append("FROM xml_documents WHERE");
699

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

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

    
717
            if(!self.toString().equals("")){
718
                self.append(" AND (");
719
                emptyString = false;
720
            }
721

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

    
733
            if(!emptyString){
734
                self.append(") ");
735
            }
736
        }
737

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

    
744
            if(!self.toString().equals("")){
745
                self.append(" AND (");
746
                emptyString = false;
747
            }
748

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

    
763
            if(!emptyString){
764
                self.append(") ");
765
            }
766
        }
767

    
768
        // if there is only one percentage search item, this query is a
769
        // percentage
770
        // search query
771
        MetaCatUtil.debugMessage("percentage number: "
772
                + query.getPercentageSymbolCount(), 35);
773
        if (query.getPercentageSymbolCount() == 1) {
774
            MetaCatUtil.debugMessage("it is a percentage search", 30);
775
            percentageSearch = true;
776
        }
777

    
778
        queryString.append(self.toString());
779
        return queryString.toString();
780
    }
781

    
782
    /**
783
     * This sql command will selecet startnodeid and endnodeid that user can
784
     * NOT access
785
     */
786
    public String printAccessControlSQLForReturnField(String doclist)
787
    {
788
        StringBuffer sql = new StringBuffer();
789
        String allowString = constructAllowString();
790
        String denyString = constructDenyString();
791
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
792
        sql.append("WHERE docid in (");
793
        sql.append(doclist);
794
        sql.append(") AND startnodeid IS NOT NULL AND ");
795
        sql.append("(");
796
        sql.append("(");
797
        sql
798
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
799
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
800
        sql.append(" AND lower(xml_documents.user_owner) ='");
801
        sql.append(userName);
802
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
803
        sql.append(")");
804
        sql.append(" AND ");
805
        sql.append("(");
806
        sql
807
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
808
        sql.append(allowString);
809
        sql.append(") AND (startnodeid IS NOT NULL))");
810
        sql.append(")");
811
        sql
812
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
813
        sql.append(denyString);
814
        sql.append(") AND (startnodeid IS NOT NULL))");
815
        sql.append(")");
816
        sql.append(")");
817
        sql.append(")");
818
        MetaCatUtil.debugMessage("accessControlSQLForReturnField: "
819
                + sql.toString(), 30);
820
        return sql.toString();
821
    }
822

    
823
    /**
824
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
825
     * pathquery document. This allows for customization of the returned fields.
826
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
827
     * xml_nodes to find the fields to be included by their path expression, and
828
     * avoids the use of the xml_index table.
829
     *
830
     * @param doclist the list of document ids to search
831
     * @param unaccessableNodePair the node pairs (start id and end id) which
832
     *            this user should not access
833
     * @param useXMLIndex a boolean flag indicating whether to search using
834
     *            xml_index
835
     */
836
    public String printExtendedSQL(String doclist,
837
            Hashtable unaccessableNodePair, boolean useXMLIndex)
838
    {
839
        if (useXMLIndex) {
840
            return printExtendedSQL(doclist, unaccessableNodePair);
841
        } else {
842
            StringBuffer self = new StringBuffer();
843

    
844
            boolean firstfield = true;
845
            //put the returnfields into the query
846
            //the for loop allows for multiple fields
847
            for (int i = 0; i < returnFieldList.size(); i++) {
848
                if (firstfield) {
849
                    firstfield = false;
850
                } else {
851
                    self.append(" UNION ");
852
                }
853
                String path  = (String) returnFieldList.elementAt(i);
854
                self.append("select xml_nodes.docid, ");
855
                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
856
                self.append("xml_nodes.parentnodeid ");
857
                self.append("from xml_nodes, xml_documents ");
858
                self.append("where parentnodeid IN ");
859
                self.append(QueryTerm.useNestedStatements(path));
860

    
861
                self.append(" AND xml_nodes.docid in (");
862
                self.append(doclist);
863
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
864
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
865

    
866
                addAccessRestrictionSQL(unaccessableNodePair, self);
867
            }
868

    
869
            return self.toString();
870
        }
871
    }
872

    
873
    /**
874
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
875
     * pathquery document. This allows for customization of the returned fields.
876
     * It uses the xml_index table and so assumes that this table has been
877
     * built.
878
     *
879
     * @param doclist the list of document ids to search
880
     * @param unaccessableNodePair the node pairs (start id and end id)
881
     *            which this user should not access
882
     */
883
    public String printExtendedSQL(String doclist,
884
            Hashtable unaccessableNodePair)
885
    {
886
        MetaCatUtil.debugMessage("querySpecification.printExtendedSQL called\n", 35);
887
        StringBuffer self = new StringBuffer();
888

    
889
        boolean usePathIndex = true;
890

    
891
        // test if the are elements in the return fields
892
        if ( returnFieldList.size() == 0 ) {
893
            return null;
894
        }
895

    
896
        for (int i = 0; i < returnFieldList.size(); i++) {
897
            if(!MetaCatUtil.pathsForIndexing.contains
898
               (returnFieldList.elementAt(i))){
899
                usePathIndex = false;
900
                break;
901
            }
902
        }
903

    
904
        if(usePathIndex){
905
            self.append("select docid, path, nodedata, parentnodeid ");
906
            self.append("from xml_path_index where (path like '");
907

    
908
            boolean firstfield = true;
909
            //put the returnfields into the query
910
            //the for loop allows for multiple fields
911
            for (int i = 0; i < returnFieldList.size(); i++) {
912
                if (firstfield) {
913
                    firstfield = false;
914
                    self.append( (String) returnFieldList.elementAt(i));
915
                    self.append("' ");
916
                }
917
                else {
918
                    self.append("or path like '");
919
                    self.append( (String) returnFieldList.elementAt(i));
920
                    self.append("' ");
921
                }
922
            }
923
            self.append(") AND docid in (");
924
            self.append(doclist);
925
            self.append(")");
926

    
927
        } else {
928
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
929
            self.append("xml_nodes.parentnodeid ");
930
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
931
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
932

    
933
            boolean firstfield = true;
934
            //put the returnfields into the query
935
            //the for loop allows for multiple fields
936
            for (int i = 0; i < returnFieldList.size(); i++) {
937
                if (firstfield) {
938
                    firstfield = false;
939
                    self.append((String) returnFieldList.elementAt(i));
940
                    self.append("' ");
941
                } else {
942
                    self.append("or xml_index.path like '");
943
                    self.append((String) returnFieldList.elementAt(i));
944
                    self.append("' ");
945
                }
946
            }
947
            self.append(") AND xml_nodes.docid in (");
948
            self.append(doclist);
949
            self.append(") AND xml_nodes.nodetype = 'TEXT'");
950

    
951
        }
952

    
953
        addAccessRestrictionSQL(unaccessableNodePair, self);
954

    
955
        return self.toString();
956
    }
957

    
958

    
959
    /**
960
     * Method to return a String generated after sorting the returnFieldList
961
     * Vector
962
     */
963
    public String getSortedReturnFieldString(){
964
        String returnFields = "";
965

    
966
        // Create a temporary vector and copy returnFieldList into it
967
        Vector tempVector = new Vector();
968

    
969
        Iterator it = returnFieldList.iterator();
970
        while(it.hasNext()){
971
            tempVector.add(it.next());
972
        }
973

    
974
        Enumeration attEnum = attributeReturnList.elements();
975
        while(attEnum.hasMoreElements()){
976
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
977
	    String rfield = "";
978
            if(tempIt.hasNext()){
979
		String element = (String)tempIt.next();
980
		if(element != null) {
981
		    rfield +=element;
982
		}
983
	    }
984
            if(tempIt.hasNext()){
985
		String attribute = (String)tempIt.next();
986
		if(attribute != null) {
987
  		    rfield = rfield + "@" + attribute;
988
                }
989
	    }
990
            tempVector.add(rfield);
991
        }
992

    
993
        // Sort the temporary vector
994
        java.util.Collections.sort(tempVector);
995

    
996
        // Generate the string and return it
997
        it = tempVector.iterator();
998
        while(it.hasNext()){
999
            returnFields = returnFields + it.next() + "|";
1000
        }
1001
        return returnFields;
1002
    }
1003

    
1004

    
1005
    /**
1006
     * Create the SQl necessary to restrict access to allowed nodes.  This is
1007
     * accomplished by restricting the nodes that are returned to include
1008
     * only those whose IDs fall outside of a set of start/stop pairs of
1009
     * nodeid values.  These pairs are passed in as a hash, with the key
1010
     * containing the start nodeid and the value containing the end nodeid.
1011
     * Any nodes between these start and end nodeid values will be excluded
1012
     * from the results.
1013
     *
1014
     * @param unaccessableNodePair hash of start/end nodeid pairs to restrict
1015
     * @param self a stringbuffer to which the genrated SQL is appended
1016
     */
1017
    private void addAccessRestrictionSQL(Hashtable unaccessableNodePair,
1018
            StringBuffer self)
1019
    {
1020
        // add control part for extended query
1021
        Enumeration en = unaccessableNodePair.keys();
1022

    
1023
        while (en.hasMoreElements()) {
1024
            // Get control pairs in object
1025
            Long startNodeIdObject = (Long) en.nextElement();
1026
            Long endNodeIdObject = (Long) unaccessableNodePair
1027
                    .get(startNodeIdObject);
1028
            // change it to long
1029
            long startNodeId = startNodeIdObject.longValue();
1030
            long endNodeId = endNodeIdObject.longValue();
1031
            // add into query
1032
            self.append(" AND ( xml_nodes.nodeid < ");
1033
            self.append(startNodeId);
1034
            self.append(" OR xml_nodes.nodeid > ");
1035
            self.append(endNodeId);
1036
            self.append(")");
1037
        }
1038
    }
1039

    
1040
    /**
1041
     * This method prints sql that finds the values of attributes in the xml
1042
     * documents based upon the whether the returnfield tag in the pathquery
1043
     * document has an attribute symbol (@). This allows for customization of
1044
     * the returned fields.
1045
     *
1046
     * @param doclist the list of document ids to search
1047
     * @param useXMLIndex a boolean flag indicating whether to search using
1048
     *            xml_index
1049
     */
1050
    public String printAttributeQuery(String doclist, boolean useXMLIndex)
1051
    {
1052
        if (useXMLIndex) {
1053
            return printAttributeQuery(doclist);
1054
        } else {
1055
            StringBuffer self = new StringBuffer();
1056
            boolean firstfield = true;
1057
            //put the returnfields attributes into the query
1058
            //the for loop allows for multiple fields and attributes
1059
            Enumeration returnAttributes = attributeReturnList.elements();
1060
            while (returnAttributes.hasMoreElements()) {
1061
                Vector currentVector = (Vector) returnAttributes.nextElement();
1062
                String returnPath = (String) currentVector.elementAt(0);
1063
                String attributeName = (String) currentVector.elementAt(1);
1064
                if (firstfield) {
1065
                    firstfield = false;
1066
                } else {
1067
                    self.append(" UNION ");
1068
                }
1069
                self.append("select xml_nodes.docid, '");
1070
                self.append(returnPath);
1071
                self.append("' as path, xml_nodes.nodedata, xml_nodes.nodename ");
1072
                self.append("from xml_nodes, xml_documents ");
1073
                self.append("where parentnodeid IN ");
1074
                self.append(QueryTerm.useNestedStatements(returnPath));
1075
                self.append(" AND xml_nodes.nodename like '");
1076
                self.append(attributeName);
1077
                self.append("' AND xml_nodes.docid in (");
1078
                self.append(doclist);
1079
                self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1080
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
1081
            }
1082

    
1083
            MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
1084

    
1085
            return self.toString();
1086
        }
1087
    }
1088

    
1089
    /**
1090
     * This method prints sql that finds the values of attributes in the xml
1091
     * documents based upon the whether the returnfield tag in the pathquery
1092
     * document has an attribute symbol (@). This allows for customization of
1093
     * the returned fields.
1094
     *
1095
     * @param doclist the list of document ids to search
1096
     */
1097
    public String printAttributeQuery(String doclist)
1098
    {
1099
        StringBuffer self = new StringBuffer();
1100
        self.append("select xml_nodes.docid, xml_index.path, ");
1101
        self.append("xml_nodes.nodedata, xml_nodes.nodename ");
1102
        self.append("from xml_index, xml_nodes where xml_index.nodeid=");
1103
        self.append("xml_nodes.parentnodeid and (");
1104
        boolean firstfield = true;
1105
        //put the returnfields attributes into the query
1106
        //the for loop allows for multiple fields and attributes
1107
        Enumeration returnAttributes = attributeReturnList.elements();
1108
        while (returnAttributes.hasMoreElements()) {
1109
            Vector currentVector = (Vector) returnAttributes.nextElement();
1110
            String returnPath = (String) currentVector.elementAt(0);
1111
            String attributeName = (String) currentVector.elementAt(1);
1112
            if (firstfield) {
1113
                firstfield = false;
1114
                self.append("( ");
1115
                if(returnPath != null){
1116
                    self.append("xml_index.path like '");
1117
                    self.append(returnPath);
1118
                    self.append("' AND ");
1119
                }else {
1120
                  MetaCatUtil.debugMessage("QuerySpecification.printAttributeQuery: "
1121
                   + "returnPath is: " + returnPath, 30);
1122
                }
1123
                self.append("xml_nodes.nodename like '");
1124
                self.append(attributeName);
1125
                self.append("') ");
1126
            } else {
1127
                self.append(" or (");
1128
                if(returnPath != null){
1129
                    self.append("xml_index.path like '");
1130
                    self.append(returnPath);
1131
                    self.append("' AND ");
1132
                }else {
1133
                  MetaCatUtil.debugMessage("QuerySpecification.printAttributeQuery: "
1134
                   + "returnPath is null: " + returnPath, 30);
1135
                }
1136
                self.append("xml_nodes.nodename like '");
1137
                self.append(attributeName);
1138
                self.append("') ");
1139
            }
1140
        }
1141
        self.append(") AND xml_nodes.docid in (");
1142
        self.append(doclist);
1143
        self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'");
1144
        MetaCatUtil.debugMessage("Attribute query: " + self.toString(), 30);
1145

    
1146
        return self.toString();
1147
    }
1148

    
1149
    public static String printRelationSQL(String docid)
1150
    {
1151
        StringBuffer self = new StringBuffer();
1152
        self.append("select subject, relationship, object, subdoctype, ");
1153
        self.append("objdoctype from xml_relation ");
1154
        self.append("where docid like '").append(docid).append("'");
1155
        return self.toString();
1156
    }
1157

    
1158
    public static String printGetDocByDoctypeSQL(String docid)
1159
    {
1160
        StringBuffer self = new StringBuffer();
1161

    
1162
        self.append("SELECT docid,docname,doctype,");
1163
        self.append("date_created, date_updated ");
1164
        self.append("FROM xml_documents WHERE docid IN (");
1165
        self.append(docid).append(")");
1166
        return self.toString();
1167
    }
1168

    
1169
    /**
1170
     * create a String description of the query that this instance represents.
1171
     * This should become a way to get the XML serialization of the query.
1172
     */
1173
    public String toString()
1174
    {
1175
        return "meta_file_id=" + meta_file_id + "\n" + query;
1176
        //DOCTITLE attr cleared from the db
1177
        //return "meta_file_id=" + meta_file_id + "\n" +
1178
        //"querytitle=" + querytitle + "\n" + query;
1179
    }
1180

    
1181
    /** A method to get rid of attribute part in path expression */
1182
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1183
    {
1184
        if (pathExpression == null) { return null; }
1185
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1186
        String newExpression = null;
1187
        if (index != 0) {
1188
            newExpression = pathExpression.substring(0, index - 1);
1189
        }
1190
        MetaCatUtil.debugMessage("The path expression without attributes: "
1191
                + newExpression, 30);
1192
        return newExpression;
1193
    }
1194

    
1195
    /** A method to get attribute name from path */
1196
    public static String getAttributeName(String path)
1197
    {
1198
        if (path == null) { return null; }
1199
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1200
        int size = path.length();
1201
        String attributeName = null;
1202
        if (index != 1) {
1203
            attributeName = path.substring(index + 1, size);
1204
        }
1205
        MetaCatUtil.debugMessage("The attirbute name from path: "
1206
                + attributeName, 30);
1207
        return attributeName;
1208
    }
1209

    
1210
}
(53-53/64)