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-11-30 11:02:14 -0800 (Wed, 30 Nov 2005) $'
14
 * '$Revision: 2793 $'
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.apache.log4j.Logger;
44
import org.xml.sax.Attributes;
45
import org.xml.sax.InputSource;
46
import org.xml.sax.SAXException;
47
import org.xml.sax.XMLReader;
48
import org.xml.sax.helpers.DefaultHandler;
49
import org.xml.sax.helpers.XMLReaderFactory;
50
import java.util.Iterator;
51

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

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

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

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

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

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

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

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

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

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

    
89
    private Stack queryStack;
90

    
91
    private String currentValue;
92

    
93
    private String currentPathexpr;
94

    
95
    private String parserName = null;
96

    
97
    private String accNumberSeparator = null;
98

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

    
101
    private boolean percentageSearch = false;
102

    
103
    private String userName = null;
104

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

    
107
    private String[] group = null;
108

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

    
111
    private boolean hasAttributeReturnField = false;
112

    
113
    private Hashtable attributeReturnList = new Hashtable();
114

    
115
    private int countAttributeReturnField = 0;
116

    
117
    private StringBuffer textBuffer = new StringBuffer();
118

    
119
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
120

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

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

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

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

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

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

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

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

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

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

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

    
258
    }
259

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

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

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

    
308
    }
309

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
535
        return parser;
536
    }
537

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

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

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

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

    
621
    }
622

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

    
634
    }
635

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
878
                addAccessRestrictionSQL(unaccessableNodePair, self);
879
            }
880

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

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

    
901
        boolean usePathIndex = true;
902

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

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

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

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

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

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

    
963
        }
964

    
965
        addAccessRestrictionSQL(unaccessableNodePair, self);
966

    
967
        return self.toString();
968
    }
969

    
970

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

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

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

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

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

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

    
1016

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

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

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

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

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

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

    
1158
        return self.toString();
1159
    }
1160

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

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

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

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

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

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

    
1222
}
(53-53/64)