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-10-17 11:15:40 -0700 (Mon, 17 Oct 2005) $'
14
 * '$Revision: 2677 $'
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='7'))";
269
        }
270
        // add allow rule for public
271
        allowQuery = allowQuery + "OR (lower(principal_name) = '" + PUBLIC
272
                + "' AND perm_type = 'allow'"
273
                + " AND (permission='4' OR permission='7'))";
274

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

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

    
305
    }
306

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
480
    /**
481
     * get the QueryGroup used to express query constraints
482
     */
483
    public QueryGroup getQueryGroup()
484
    {
485
        return query;
486
    }
487

    
488
    /**
489
     * set the querygroup
490
     */
491
    public void setQueryGroup(QueryGroup group)
492
    {
493
        query = group;
494
    }
495

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

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

    
512
        // Set up the SAX document handlers for parsing
513
        try {
514

    
515
            // Get an instance of the parser
516
            parser = XMLReaderFactory.createXMLReader(parserName);
517

    
518
            // Set the ContentHandler to this instance
519
            parser.setContentHandler(this);
520

    
521
            // Set the error Handler to this instance
522
            parser.setErrorHandler(this);
523

    
524
        } catch (Exception e) {
525
            System.err.println("Error in QuerySpcecification.initializeParser "
526
                    + e.toString());
527
        }
528

    
529
        return parser;
530
    }
531

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

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

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

    
611
        //rest textBuffer
612
        textBuffer = null;
613
        textBuffer = new StringBuffer();
614

    
615
    }
616

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

    
628
    }
629

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

    
644
          if ( inputString.startsWith(ATTRIBUTESYMBOL) ) {
645

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

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

    
696
        StringBuffer self = new StringBuffer();
697
        StringBuffer queryString = new StringBuffer();
698

    
699
        queryString.append("SELECT docid,docname,doctype,");
700
        queryString.append("date_created, date_updated, rev ");
701
        queryString.append("FROM xml_documents WHERE");
702

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

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

    
723
            if(!self.toString().equals("")){
724
                self.append(" AND (");
725
                emptyString = false;
726
            }
727

    
728
            Enumeration en = filterDocList.elements();
729
            while (en.hasMoreElements()) {
730
                String currentDoctype = (String) en.nextElement();
731
                if (firstdoctype) {
732
                    firstdoctype = false;
733
                    self.append(" doctype = '" + currentDoctype + "'");
734
                } else {
735
                    self.append(" OR doctype = '" + currentDoctype + "'");
736
                }
737
            }
738

    
739
            if(!emptyString){
740
                self.append(") ");
741
            }
742
        }
743

    
744
        // Add SQL to filter for owners requested in the query
745
        // This is an implicit OR for the list of owners
746
        if (!ownerList.isEmpty()) {
747
            boolean first = true;
748
            boolean emptyString = true;
749

    
750
            if(!self.toString().equals("")){
751
                self.append(" AND (");
752
                emptyString = false;
753
            }
754

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

    
769
            if(!emptyString){
770
                self.append(") ");
771
            }
772
        }
773

    
774
        // if there is only one percentage search item, this query is a
775
        // percentage
776
        // search query
777
        logMetacat.info("percentage number: "
778
                + query.getPercentageSymbolCount());
779
        if (query.getPercentageSymbolCount() == 1) {
780
            logMetacat.info("It is a percentage search");
781
            percentageSearch = true;
782
        }
783

    
784
        queryString.append(self.toString());
785
        return queryString.toString();
786
    }
787

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

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

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

    
867
                self.append(" AND xml_nodes.docid in (");
868
                self.append(doclist);
869
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
870
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
871

    
872
                addAccessRestrictionSQL(unaccessableNodePair, self);
873
            }
874

    
875
            return self.toString();
876
        }
877
    }
878

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

    
895
        boolean usePathIndex = true;
896

    
897
        // test if the are elements in the return fields
898
        if ( returnFieldList.size() == 0 ) {
899
            return null;
900
        }
901

    
902
        for (int i = 0; i < returnFieldList.size(); i++) {
903
            if(!MetaCatUtil.pathsForIndexing.contains
904
               (returnFieldList.elementAt(i))){
905
                usePathIndex = false;
906
                break;
907
            }
908
        }
909

    
910
        if(usePathIndex){
911
            self.append("select docid, path, nodedata, parentnodeid ");
912
            self.append("from xml_path_index where (path like '");
913

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

    
933
        } else {
934
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
935
            self.append("xml_nodes.parentnodeid ");
936
            self.append("from xml_index, xml_nodes where xml_index.nodeid=");
937
            self.append("xml_nodes.parentnodeid and (xml_index.path like '");
938

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

    
957
        }
958

    
959
        addAccessRestrictionSQL(unaccessableNodePair, self);
960

    
961
        return self.toString();
962
    }
963

    
964

    
965
    /**
966
     * Method to return a String generated after sorting the returnFieldList
967
     * Vector
968
     */
969
    public String getSortedReturnFieldString(){
970
        String returnFields = "";
971

    
972
        // Create a temporary vector and copy returnFieldList into it
973
        Vector tempVector = new Vector();
974

    
975
        Iterator it = returnFieldList.iterator();
976
        while(it.hasNext()){
977
            tempVector.add(it.next());
978
        }
979

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

    
999
        // Sort the temporary vector
1000
        java.util.Collections.sort(tempVector);
1001

    
1002
        // Generate the string and return it
1003
        it = tempVector.iterator();
1004
        while(it.hasNext()){
1005
            returnFields = returnFields + it.next() + "|";
1006
        }
1007
        return returnFields;
1008
    }
1009

    
1010

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

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

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

    
1089
            logMetacat.warn("Attribute query: " + self.toString());
1090

    
1091
            return self.toString();
1092
        }
1093
    }
1094

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

    
1152
        return self.toString();
1153
    }
1154

    
1155
    public static String printRelationSQL(String docid)
1156
    {
1157
        StringBuffer self = new StringBuffer();
1158
        self.append("select subject, relationship, object, subdoctype, ");
1159
        self.append("objdoctype from xml_relation ");
1160
        self.append("where docid like '").append(docid).append("'");
1161
        return self.toString();
1162
    }
1163

    
1164
    public static String printGetDocByDoctypeSQL(String docid)
1165
    {
1166
        StringBuffer self = new StringBuffer();
1167

    
1168
        self.append("SELECT docid,docname,doctype,");
1169
        self.append("date_created, date_updated ");
1170
        self.append("FROM xml_documents WHERE docid IN (");
1171
        self.append(docid).append(")");
1172
        return self.toString();
1173
    }
1174

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

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

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

    
1216
}
(54-54/65)