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
 *
11
 *   '$Author: jones $'
12
 *     '$Date: 2006-11-10 10:25:38 -0800 (Fri, 10 Nov 2006) $'
13
 * '$Revision: 3077 $'
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
 */
29

    
30
package edu.ucsb.nceas.metacat;
31

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

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

    
42
import org.apache.log4j.Logger;
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
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
119

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

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

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

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

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

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

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

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

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

    
240
        logMetacat.info("OwnerQuery: " + ownerQuery);
241
        return ownerQuery;
242
    }
243

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

    
257
    }
258

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

    
276
        // add allow rule for group
277
        if (group != null) {
278
            for (int i = 0; i < group.length; i++) {
279
                String groupUint = group[i];
280
                if (groupUint != null && !groupUint.equals("")) {
281
                    groupUint = groupUint.toLowerCase();
282
                    allowQuery = allowQuery + " OR (lower(principal_name) = '"
283
                            + groupUint + "' AND perm_type = 'allow'"
284
                	    + " AND (permission='4' OR permission='5' " 
285
                            + "OR permission='6' OR permission='7'))";
286
                }//if
287
            }//for
288
        }//if
289
        logMetacat.info("allow string is: " + allowQuery);
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
        logMetacat.info("denyquery is: " + denyQuery);
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='5' " 
319
                    + "OR permission='6' OR permission='7'))";
320
        }
321
        // add deny rule for public
322
        denyQuery = denyQuery + "OR (lower(principal_name) = '" + PUBLIC
323
                + "' AND perm_type = 'deny' " + "AND perm_order ='allowFirst'"
324
                + " AND (permission='4' OR permission='5' " 
325
                + "OR permission='6' OR permission='7'))";
326

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
485
    /**
486
     * get the QueryGroup used to express query constraints
487
     */
488
    public QueryGroup getQueryGroup()
489
    {
490
        return query;
491
    }
492

    
493
    /**
494
     * set the querygroup
495
     */
496
    public void setQueryGroup(QueryGroup group)
497
    {
498
        query = group;
499
    }
500

    
501
    /**
502
     * set if this query sepcification has extendQuery(has return doc type or
503
     * not)
504
     */
505
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
506
    {
507
        containsExtendedSQL = hasExtenedQuery;
508
    }
509

    
510
    /**
511
     * Set up the SAX parser for reading the XML serialized query
512
     */
513
    private XMLReader initializeParser()
514
    {
515
        XMLReader parser = null;
516

    
517
        // Set up the SAX document handlers for parsing
518
        try {
519

    
520
            // Get an instance of the parser
521
            parser = XMLReaderFactory.createXMLReader(parserName);
522

    
523
            // Set the ContentHandler to this instance
524
            parser.setContentHandler(this);
525

    
526
            // Set the error Handler to this instance
527
            parser.setErrorHandler(this);
528

    
529
        } catch (Exception e) {
530
            System.err.println("Error in QuerySpcecification.initializeParser "
531
                    + e.toString());
532
        }
533

    
534
        return parser;
535
    }
536

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

    
555
        elementStack.push(currentNode);
556
        if (currentNode.getTagName().equals("querygroup")) {
557
            QueryGroup currentGroup = new QueryGroup(currentNode
558
                    .getAttribute("operator"));
559
            if (query == null) {
560
                query = currentGroup;
561
            } else {
562
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
563
                parentGroup.addChild(currentGroup);
564
            }
565
            queryStack.push(currentGroup);
566
        }
567
    }
568

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

    
616
        //rest textBuffer
617
        textBuffer = null;
618
        textBuffer = new StringBuffer();
619

    
620
    }
621

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

    
633
    }
634

    
635
    /**
636
     * Method to transfer string to return field
637
     */
638
    public void handleReturnField(String inputString)
639
    {
640
        // make sure if return fields has an attribute or not
641
        if (inputString.indexOf(ATTRIBUTESYMBOL) == -1) {
642
            // no attribute value will be returned
643
            logMetacat.info("QuerySpecification.handleReturnField(): " );
644
            logMetacat.info("  there are no attributes in the XPATH statement" );
645
            returnFieldList.add(inputString);
646
            containsExtendedSQL = true;
647
        } else {
648

    
649
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
650

    
651
            // case where the return field is solely an attribute
652
            logMetacat.info("QuerySpecification.handleReturnField(): " );
653
            logMetacat.info("  there are *only* attributes in the XPATH statement" );
654
            String returnPath = newPathExpressionWithOutAttribute(inputString);
655
            String attributeName = getAttributeName(inputString);
656
            Vector pathInfo = new Vector();
657
            // the vector has the information about return path and
658
            // attributename
659
            pathInfo.addElement(returnPath);
660
            pathInfo.addElement(attributeName);
661
            // put the vector into a hash table. The reseaon why don't put
662
            // return path or attributename as a key is because they are not
663
            // unique
664
            attributeReturnList.put(new Integer(countAttributeReturnField),
665
                    pathInfo);
666
            countAttributeReturnField++;
667
            hasAttributeReturnField = true;
668
            containsExtendedSQL = true;
669
          } else {
670
            // has a attribute return field
671
            // divied the return filed into two parts, one is path and the
672
            // other is attribue name
673
            logMetacat.info("QuerySpecification.handleReturnField: " );
674
            logMetacat.info("  there are both attributes and elements" );
675
            logMetacat.info("  in the XPATH statement" );
676
            String returnPath = newPathExpressionWithOutAttribute(inputString);
677
            String attributeName = getAttributeName(inputString);
678
            Vector pathInfo = new Vector();
679
            // the vector has the information about return path and
680
            // attributename
681
            pathInfo.addElement(returnPath);
682
            pathInfo.addElement(attributeName);
683
            // put the vector into a hash table. The reseaon why don't put
684
            // return path or attributename as a key is because they are not
685
            // unique
686
            attributeReturnList.put(new Integer(countAttributeReturnField),
687
                    pathInfo);
688
            countAttributeReturnField++;
689
            hasAttributeReturnField = true;
690
            containsExtendedSQL = true;
691
          }
692
        }
693
    }
694

    
695
    /**
696
     * create a SQL serialization of the query that this instance represents
697
     */
698
    public String printSQL(boolean useXMLIndex)
699
    {
700

    
701
        StringBuffer self = new StringBuffer();
702
        StringBuffer queryString = new StringBuffer();
703

    
704
        queryString.append("SELECT docid,docname,doctype,");
705
        queryString.append("date_created, date_updated, rev ");
706
        queryString.append("FROM xml_documents WHERE");
707

    
708
        // Get the query from the QueryGroup and check
709
        // if no query has been returned
710
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
711
        logMetacat.info("Query from query in QuerySpec.printSQL: " 
712
        		+ queryFromQueryGroup);
713
        
714
        if(!queryFromQueryGroup.trim().equals("")){
715
            self.append(" docid IN (");
716
            self.append(queryFromQueryGroup);
717
            self.append(") ");
718
        }
719

    
720
        // Add SQL to filter for doctypes requested in the query
721
        // This is an implicit OR for the list of doctypes. Only doctypes in
722
        // this
723
        // list will be searched if the tag is present
724
        if (!filterDocList.isEmpty()) {
725
            boolean firstdoctype = true;
726
            boolean emptyString = true;
727

    
728
            if(!self.toString().equals("")){
729
                self.append(" AND (");
730
                emptyString = false;
731
            }
732

    
733
            Enumeration en = filterDocList.elements();
734
            while (en.hasMoreElements()) {
735
                String currentDoctype = (String) en.nextElement();
736
                if (firstdoctype) {
737
                    firstdoctype = false;
738
                    self.append(" doctype = '" + currentDoctype + "'");
739
                } else {
740
                    self.append(" OR doctype = '" + currentDoctype + "'");
741
                }
742
            }
743

    
744
            if(!emptyString){
745
                self.append(") ");
746
            }
747
        }
748

    
749
        // Add SQL to filter for owners requested in the query
750
        // This is an implicit OR for the list of owners
751
        if (!ownerList.isEmpty()) {
752
            boolean first = true;
753
            boolean emptyString = true;
754

    
755
            if(!self.toString().equals("")){
756
                self.append(" AND (");
757
                emptyString = false;
758
            }
759

    
760
            Enumeration en = ownerList.elements();
761
            while (en.hasMoreElements()) {
762
                String current = (String) en.nextElement();
763
                if (current != null) {
764
                    current = current.toLowerCase();
765
                }
766
                if (first) {
767
                    first = false;
768
                    self.append(" lower(user_owner) = '" + current + "'");
769
                } else {
770
                    self.append(" OR lower(user_owner) = '" + current + "'");
771
                }
772
            }
773

    
774
            if(!emptyString){
775
                self.append(") ");
776
            }
777
        }
778

    
779
        // if there is only one percentage search item, this query is a
780
        // percentage
781
        // search query
782
        logMetacat.info("percentage number: "
783
                + query.getPercentageSymbolCount());
784
        if (query.getPercentageSymbolCount() == 1) {
785
            logMetacat.info("It is a percentage search");
786
            percentageSearch = true;
787
        }
788

    
789
        queryString.append(self.toString());
790
        return queryString.toString();
791
    }
792

    
793
    /**
794
     * This sql command will selecet startnodeid and endnodeid that user can
795
     * NOT access
796
     */
797
    public String printAccessControlSQLForReturnField(String doclist)
798
    {
799
        StringBuffer sql = new StringBuffer();
800
        String allowString = constructAllowString();
801
        String denyString = constructDenyString();
802
        sql.append("SELECT distinct startnodeid, endnodeid from xml_access ");
803
        sql.append("WHERE docid in (");
804
        sql.append(doclist);
805
        sql.append(") AND startnodeid IS NOT NULL AND ");
806
        sql.append("(");
807
        sql.append("(");
808
        sql
809
                .append("startnodeid NOT IN (SELECT startnodeid from xml_access, xml_documents ");
810
        sql.append(" WHERE xml_access.docid = xml_documents.docid");
811
        sql.append(" AND lower(xml_documents.user_owner) ='");
812
        sql.append(userName);
813
        sql.append("' AND xml_access.startnodeid IS NOT NULL)");
814
        sql.append(")");
815
        sql.append(" AND ");
816
        sql.append("(");
817
        sql
818
                .append("(startnodeid NOT IN (SELECT startnodeid from xml_access where( ");
819
        sql.append(allowString);
820
        sql.append(") AND (startnodeid IS NOT NULL))");
821
        sql.append(")");
822
        sql
823
                .append(" OR (startnodeid IN (SELECT startnodeid from xml_access where( ");
824
        sql.append(denyString);
825
        sql.append(") AND (startnodeid IS NOT NULL))");
826
        sql.append(")");
827
        sql.append(")");
828
        sql.append(")");
829
        logMetacat.info("accessControlSQLForReturnField: "
830
                + sql.toString());
831
        return sql.toString();
832
    }
833

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

    
855
            boolean firstfield = true;
856
            //put the returnfields into the query
857
            //the for loop allows for multiple fields
858
            for (int i = 0; i < returnFieldList.size(); i++) {
859
                if (firstfield) {
860
                    firstfield = false;
861
                } else {
862
                    self.append(" UNION ");
863
                }
864
                String path  = (String) returnFieldList.elementAt(i);
865
                self.append("select xml_nodes.docid, ");
866
                self.append("'"+ path  + "' as path, xml_nodes.nodedata, ");
867
                self.append("xml_nodes.parentnodeid ");
868
                self.append("from xml_nodes, xml_documents ");
869
                self.append("where parentnodeid IN ");
870
                self.append(QueryTerm.useNestedStatements(path));
871

    
872
                self.append(" AND xml_nodes.docid in (");
873
                self.append(doclist);
874
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
875
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
876

    
877
                addAccessRestrictionSQL(unaccessableNodePair, self);
878
            }
879

    
880
            return self.toString();
881
        }
882
    }
883

    
884
    /**
885
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
886
     * pathquery document. This allows for customization of the returned fields.
887
     * It uses the xml_index table and so assumes that this table has been
888
     * built.
889
     *
890
     * @param doclist the list of document ids to search
891
     * @param unaccessableNodePair the node pairs (start id and end id)
892
     *            which this user should not access
893
     */
894
    public String printExtendedSQL(String doclist,
895
            Hashtable unaccessableNodePair)
896
    {
897
        logMetacat.info("querySpecification.printExtendedSQL called\n");
898
        StringBuffer self = new StringBuffer();
899

    
900
        boolean usePathIndex = true;
901

    
902
        // test if the are elements in the return fields
903
        if ( returnFieldList.size() == 0 ) {
904
            return null;
905
        }
906

    
907
        for (int i = 0; i < returnFieldList.size(); i++) {
908
            if(!MetaCatUtil.pathsForIndexing.contains
909
               (returnFieldList.elementAt(i))){
910
                usePathIndex = false;
911
                break;
912
            }
913
        }
914

    
915
        if(usePathIndex){
916
            self.append("select docid, path, nodedata, parentnodeid ");
917
            self.append("from xml_path_index where (path like '");
918

    
919
            boolean firstfield = true;
920
            //put the returnfields into the query
921
            //the for loop allows for multiple fields
922
            for (int i = 0; i < returnFieldList.size(); i++) {
923
                if (firstfield) {
924
                    firstfield = false;
925
                    self.append( (String) returnFieldList.elementAt(i));
926
                    self.append("' ");
927
                }
928
                else {
929
                    self.append("or path like '");
930
                    self.append( (String) returnFieldList.elementAt(i));
931
                    self.append("' ");
932
                }
933
            }
934
            self.append(") AND docid in (");
935
            self.append(doclist);
936
            self.append(")");
937

    
938
        } else {
939
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
940
            self.append("xml_nodes.parentnodeid ");
941
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
942
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
943

    
944
            boolean firstfield = true;
945
            //put the returnfields into the query
946
            //the for loop allows for multiple fields
947
            for (int i = 0; i < returnFieldList.size(); i++) {
948
                if (firstfield) {
949
                    firstfield = false;
950
                    self.append((String) returnFieldList.elementAt(i));
951
                    self.append("' ");
952
                } else {
953
                    self.append("or xml_index.path like '");
954
                    self.append((String) returnFieldList.elementAt(i));
955
                    self.append("' ");
956
                }
957
            }
958
            self.append(") AND xml_nodes.docid in (");
959
            self.append(doclist);
960
            self.append(") AND xml_nodes.nodetype = 'TEXT'");
961

    
962
        }
963

    
964
        addAccessRestrictionSQL(unaccessableNodePair, self);
965

    
966
        return self.toString();
967
    }
968

    
969

    
970
    /**
971
     * Method to return a String generated after sorting the returnFieldList
972
     * Vector
973
     */
974
    public String getSortedReturnFieldString(){
975
        String returnFields = "";
976

    
977
        // Create a temporary vector and copy returnFieldList into it
978
        Vector tempVector = new Vector();
979

    
980
        Iterator it = returnFieldList.iterator();
981
        while(it.hasNext()){
982
            tempVector.add(it.next());
983
        }
984

    
985
        Enumeration attEnum = attributeReturnList.elements();
986
        while(attEnum.hasMoreElements()){
987
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
988
	    String rfield = "";
989
            if(tempIt.hasNext()){
990
		String element = (String)tempIt.next();
991
		if(element != null) {
992
		    rfield +=element;
993
		}
994
	    }
995
            if(tempIt.hasNext()){
996
		String attribute = (String)tempIt.next();
997
		if(attribute != null) {
998
  		    rfield = rfield + "@" + attribute;
999
                }
1000
	    }
1001
            tempVector.add(rfield);
1002
        }
1003

    
1004
        // Sort the temporary vector
1005
        java.util.Collections.sort(tempVector);
1006

    
1007
        // Generate the string and return it
1008
        it = tempVector.iterator();
1009
        while(it.hasNext()){
1010
            returnFields = returnFields + it.next() + "|";
1011
        }
1012
        return returnFields;
1013
    }
1014

    
1015

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

    
1034
        while (en.hasMoreElements()) {
1035
            // Get control pairs in object
1036
            Long startNodeIdObject = (Long) en.nextElement();
1037
            Long endNodeIdObject = (Long) unaccessableNodePair
1038
                    .get(startNodeIdObject);
1039
            // change it to long
1040
            long startNodeId = startNodeIdObject.longValue();
1041
            long endNodeId = endNodeIdObject.longValue();
1042
            // add into query
1043
            self.append(" AND ( xml_nodes.nodeid < ");
1044
            self.append(startNodeId);
1045
            self.append(" OR xml_nodes.nodeid > ");
1046
            self.append(endNodeId);
1047
            self.append(")");
1048
        }
1049
    }
1050

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

    
1094
            logMetacat.warn("Attribute query: " + self.toString());
1095

    
1096
            return self.toString();
1097
        }
1098
    }
1099

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

    
1157
        return self.toString();
1158
    }
1159

    
1160
    public static String printRelationSQL(String docid)
1161
    {
1162
        StringBuffer self = new StringBuffer();
1163
        self.append("select subject, relationship, object, subdoctype, ");
1164
        self.append("objdoctype from xml_relation ");
1165
        self.append("where docid like '").append(docid).append("'");
1166
        return self.toString();
1167
    }
1168

    
1169
    public static String printGetDocByDoctypeSQL(String docid)
1170
    {
1171
        StringBuffer self = new StringBuffer();
1172

    
1173
        self.append("SELECT docid,docname,doctype,");
1174
        self.append("date_created, date_updated ");
1175
        self.append("FROM xml_documents WHERE docid IN (");
1176
        self.append(docid).append(")");
1177
        return self.toString();
1178
    }
1179

    
1180
    /**
1181
     * create a String description of the query that this instance represents.
1182
     * This should become a way to get the XML serialization of the query.
1183
     */
1184
    public String toString()
1185
    {
1186
        return "meta_file_id=" + meta_file_id + "\n" + query;
1187
        //DOCTITLE attr cleared from the db
1188
        //return "meta_file_id=" + meta_file_id + "\n" +
1189
        //"querytitle=" + querytitle + "\n" + query;
1190
    }
1191

    
1192
    /** A method to get rid of attribute part in path expression */
1193
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1194
    {
1195
        if (pathExpression == null) { return null; }
1196
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1197
        String newExpression = null;
1198
        if (index != 0) {
1199
            newExpression = pathExpression.substring(0, index - 1);
1200
        }
1201
        logMetacat.info("The path expression without attributes: "
1202
                + newExpression);
1203
        return newExpression;
1204
    }
1205

    
1206
    /** A method to get attribute name from path */
1207
    public static String getAttributeName(String path)
1208
    {
1209
        if (path == null) { return null; }
1210
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1211
        int size = path.length();
1212
        String attributeName = null;
1213
        if (index != 1) {
1214
            attributeName = path.substring(index + 1, size);
1215
        }
1216
        logMetacat.info("The attirbute name from path: "
1217
                + attributeName);
1218
        return attributeName;
1219
    }
1220

    
1221
}
(54-54/65)