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: tao $'
12
 *     '$Date: 2007-04-05 17:28:03 -0700 (Thu, 05 Apr 2007) $'
13
 * '$Revision: 3223 $'
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
   
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
        //if user is public, we don't need to run owner query
237
        if (userName != null && !userName.equalsIgnoreCase(PUBLIC))
238
        {
239
	        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
240
	        if (userName != null && !userName.equals("")) {
241
	            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
242
	        }
243
        }
244
        logMetacat.info("OwnerQuery: " + ownerQuery);
245
        return ownerQuery;
246
    }
247

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

    
261
    }
262

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

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

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

    
311
    }
312

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

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

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

    
376
    /**
377
     * Returns true if the parsed query contains and extended xml query (i.e.
378
     * there is at least one &lt;returnfield&gt; in the pathquery document)
379
     */
380
    public boolean containsExtendedSQL()
381
    {
382
        if (containsExtendedSQL) {
383
            return true;
384
        } else {
385
            return false;
386
        }
387
    }
388

    
389
    /**
390
     * A method to get if the query has an attribute return field
391
     */
392
    public boolean containsAttributeReturnField()
393
    {
394
        return hasAttributeReturnField;
395
    }
396

    
397
    /**
398
     * Accessor method to return the identifier of this Query
399
     */
400
    public String getIdentifier()
401
    {
402
        return meta_file_id;
403
    }
404

    
405
    /**
406
     * method to set the identifier of this query
407
     */
408
    public void setIdentifier(String id)
409
    {
410
        this.meta_file_id = id;
411
    }
412

    
413
    /**
414
     * Accessor method to return the title of this Query
415
     */
416
    public String getQueryTitle()
417
    {
418
        return queryTitle;
419
    }
420

    
421
    /**
422
     * method to set the title of this query
423
     */
424
    public void setQueryTitle(String title)
425
    {
426
        this.queryTitle = title;
427
    }
428

    
429
    /**
430
     * Accessor method to return a vector of the return document types as
431
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
432
     */
433
    public Vector getReturnDocList()
434
    {
435
        return this.returnDocList;
436
    }
437

    
438
    /**
439
     * method to set the list of return docs of this query
440
     */
441
    public void setReturnDocList(Vector returnDocList)
442
    {
443
        this.returnDocList = returnDocList;
444
    }
445

    
446
    /**
447
     * Accessor method to return a vector of the filter doc types as defined in
448
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
449
     */
450
    public Vector getFilterDocList()
451
    {
452
        return this.filterDocList;
453
    }
454

    
455
    /**
456
     * method to set the list of filter docs of this query
457
     */
458
    public void setFilterDocList(Vector filterDocList)
459
    {
460
        this.filterDocList = filterDocList;
461
    }
462

    
463
    /**
464
     * Accessor method to return a vector of the extended return fields as
465
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
466
     */
467
    public Vector getReturnFieldList()
468
    {
469
        return this.returnFieldList;
470
    }
471

    
472
    /**
473
     * method to set the list of fields to be returned by this query
474
     */
475
    public void setReturnFieldList(Vector returnFieldList)
476
    {
477
        this.returnFieldList = returnFieldList;
478
    }
479

    
480
    /**
481
     * Accessor method to return a vector of the owner fields as defined in the
482
     * &lt;owner&gt; tag in the pathquery dtd.
483
     */
484
    public Vector getOwnerList()
485
    {
486
        return this.ownerList;
487
    }
488

    
489
    /**
490
     * method to set the list of owners used to constrain this query
491
     */
492
    public void setOwnerList(Vector ownerList)
493
    {
494
        this.ownerList = ownerList;
495
    }
496

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

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

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

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

    
529
        // Set up the SAX document handlers for parsing
530
        try {
531

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

    
535
            // Set the ContentHandler to this instance
536
            parser.setContentHandler(this);
537

    
538
            // Set the error Handler to this instance
539
            parser.setErrorHandler(this);
540

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

    
546
        return parser;
547
    }
548

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

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

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

    
628
        //rest textBuffer
629
        textBuffer = null;
630
        textBuffer = new StringBuffer();
631

    
632
    }
633

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

    
645
    }
646

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

    
661
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
662

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

    
707
    /**
708
     * create a SQL serialization of the query that this instance represents
709
     */
710
    public String printSQL(boolean useXMLIndex)
711
    {
712

    
713
        StringBuffer self = new StringBuffer();
714
        StringBuffer queryString = new StringBuffer();
715

    
716
        queryString.append("SELECT docid,docname,doctype,");
717
        queryString.append("date_created, date_updated, rev ");
718
        queryString.append("FROM xml_documents WHERE");
719

    
720
        // Get the query from the QueryGroup and check
721
        // if no query has been returned
722
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
723
        logMetacat.info("Query from query in QuerySpec.printSQL: " 
724
        		+ queryFromQueryGroup);
725
        
726
        if(!queryFromQueryGroup.trim().equals("")){
727
            self.append(" docid IN (");
728
            self.append(queryFromQueryGroup);
729
            self.append(") ");
730
        }
731

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

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

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

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

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

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

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

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

    
791
        // if there is only one percentage search item, this query is a
792
        // percentage
793
        // search query
794
        logMetacat.info("percentage number: "
795
                + query.getPercentageSymbolCount());
796
        if (query.getPercentageSymbolCount() == 1) {
797
            logMetacat.info("It is a percentage search");
798
            percentageSearch = true;
799
        }
800

    
801
        queryString.append(self.toString());
802
        return queryString.toString();
803
    }
804

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

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

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

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

    
889
                addAccessRestrictionSQL(unaccessableNodePair, self);
890
            }
891

    
892
            return self.toString();
893
        }
894
    }
895

    
896
    /**
897
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
898
     * pathquery document. This allows for customization of the returned fields.
899
     * It uses the xml_index table and so assumes that this table has been
900
     * built.
901
     *
902
     * @param doclist the list of document ids to search
903
     * @param unaccessableNodePair the node pairs (start id and end id)
904
     *            which this user should not access
905
     */
906
    public String printExtendedSQL(String doclist,
907
            Hashtable unaccessableNodePair)
908
    {
909
        logMetacat.info("querySpecification.printExtendedSQL called\n");
910
        StringBuffer self = new StringBuffer();
911

    
912
        boolean usePathIndex = true;
913

    
914
        // test if the are elements in the return fields
915
        if ( returnFieldList.size() == 0 ) {
916
            return null;
917
        }
918

    
919
        for (int i = 0; i < returnFieldList.size(); i++) {
920
            if(!MetaCatUtil.pathsForIndexing.contains
921
               (returnFieldList.elementAt(i))){
922
                usePathIndex = false;
923
                break;
924
            }
925
        }
926

    
927
        if(usePathIndex){
928
            self.append("select docid, path, nodedata, parentnodeid ");
929
            self.append("from xml_path_index where (path like '");
930

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

    
950
        } else {
951
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
952
            self.append("xml_nodes.parentnodeid ");
953
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
954
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
955

    
956
            boolean firstfield = true;
957
            //put the returnfields into the query
958
            //the for loop allows for multiple fields
959
            for (int i = 0; i < returnFieldList.size(); i++) {
960
                if (firstfield) {
961
                    firstfield = false;
962
                    self.append((String) returnFieldList.elementAt(i));
963
                    self.append("' ");
964
                } else {
965
                    self.append("or xml_index.path like '");
966
                    self.append((String) returnFieldList.elementAt(i));
967
                    self.append("' ");
968
                }
969
            }
970
            self.append(") AND xml_nodes.docid in (");
971
            self.append(doclist);
972
            self.append(") AND xml_nodes.nodetype = 'TEXT'");
973

    
974
        }
975

    
976
        addAccessRestrictionSQL(unaccessableNodePair, self);
977

    
978
        return self.toString();
979
    }
980

    
981

    
982
    /**
983
     * Method to return a String generated after sorting the returnFieldList
984
     * Vector
985
     */
986
    public String getSortedReturnFieldString(){
987
        String returnFields = "";
988

    
989
        // Create a temporary vector and copy returnFieldList into it
990
        Vector tempVector = new Vector();
991

    
992
        Iterator it = returnFieldList.iterator();
993
        while(it.hasNext()){
994
            tempVector.add(it.next());
995
        }
996

    
997
        Enumeration attEnum = attributeReturnList.elements();
998
        while(attEnum.hasMoreElements()){
999
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1000
	    String rfield = "";
1001
            if(tempIt.hasNext()){
1002
		String element = (String)tempIt.next();
1003
		if(element != null) {
1004
		    rfield +=element;
1005
		}
1006
	    }
1007
            if(tempIt.hasNext()){
1008
		String attribute = (String)tempIt.next();
1009
		if(attribute != null) {
1010
  		    rfield = rfield + "@" + attribute;
1011
                }
1012
	    }
1013
            tempVector.add(rfield);
1014
        }
1015

    
1016
        // Sort the temporary vector
1017
        java.util.Collections.sort(tempVector);
1018

    
1019
        // Generate the string and return it
1020
        it = tempVector.iterator();
1021
        while(it.hasNext()){
1022
            returnFields = returnFields + it.next() + "|";
1023
        }
1024
        return returnFields;
1025
    }
1026

    
1027

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

    
1046
        while (en.hasMoreElements()) {
1047
            // Get control pairs in object
1048
            Long startNodeIdObject = (Long) en.nextElement();
1049
            Long endNodeIdObject = (Long) unaccessableNodePair
1050
                    .get(startNodeIdObject);
1051
            // change it to long
1052
            long startNodeId = startNodeIdObject.longValue();
1053
            long endNodeId = endNodeIdObject.longValue();
1054
            // add into query
1055
            self.append(" AND ( xml_nodes.nodeid < ");
1056
            self.append(startNodeId);
1057
            self.append(" OR xml_nodes.nodeid > ");
1058
            self.append(endNodeId);
1059
            self.append(")");
1060
        }
1061
    }
1062

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

    
1106
            logMetacat.warn("Attribute query: " + self.toString());
1107

    
1108
            return self.toString();
1109
        }
1110
    }
1111

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

    
1169
        return self.toString();
1170
    }
1171

    
1172
    public static String printRelationSQL(String docid)
1173
    {
1174
        StringBuffer self = new StringBuffer();
1175
        self.append("select subject, relationship, object, subdoctype, ");
1176
        self.append("objdoctype from xml_relation ");
1177
        self.append("where docid like '").append(docid).append("'");
1178
        return self.toString();
1179
    }
1180

    
1181
    public static String printGetDocByDoctypeSQL(String docid)
1182
    {
1183
        StringBuffer self = new StringBuffer();
1184

    
1185
        self.append("SELECT docid,docname,doctype,");
1186
        self.append("date_created, date_updated ");
1187
        self.append("FROM xml_documents WHERE docid IN (");
1188
        self.append(docid).append(")");
1189
        return self.toString();
1190
    }
1191

    
1192
    /**
1193
     * create a String description of the query that this instance represents.
1194
     * This should become a way to get the XML serialization of the query.
1195
     */
1196
    public String toString()
1197
    {
1198
        return "meta_file_id=" + meta_file_id + "\n" + query;
1199
        //DOCTITLE attr cleared from the db
1200
        //return "meta_file_id=" + meta_file_id + "\n" +
1201
        //"querytitle=" + querytitle + "\n" + query;
1202
    }
1203

    
1204
    /** A method to get rid of attribute part in path expression */
1205
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1206
    {
1207
        if (pathExpression == null) { return null; }
1208
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1209
        String newExpression = null;
1210
        if (index != 0) {
1211
            newExpression = pathExpression.substring(0, index - 1);
1212
        }
1213
        logMetacat.info("The path expression without attributes: "
1214
                + newExpression);
1215
        return newExpression;
1216
    }
1217

    
1218
    /** A method to get attribute name from path */
1219
    public static String getAttributeName(String path)
1220
    {
1221
        if (path == null) { return null; }
1222
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1223
        int size = path.length();
1224
        String attributeName = null;
1225
        if (index != 1) {
1226
            attributeName = path.substring(index + 1, size);
1227
        }
1228
        logMetacat.info("The attirbute name from path: "
1229
                + attributeName);
1230
        return attributeName;
1231
    }
1232

    
1233
}
(55-55/66)