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-10 11:06:55 -0700 (Mon, 10 Oct 2005) $'
14
 * '$Revision: 2663 $'
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
        if(!queryFromQueryGroup.trim().equals("")){
707
            self.append(" docid IN (");
708
            self.append(queryFromQueryGroup);
709
            self.append(") ");
710
        }
711

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

    
720
            if(!self.toString().equals("")){
721
                self.append(" AND (");
722
                emptyString = false;
723
            }
724

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

    
736
            if(!emptyString){
737
                self.append(") ");
738
            }
739
        }
740

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

    
747
            if(!self.toString().equals("")){
748
                self.append(" AND (");
749
                emptyString = false;
750
            }
751

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

    
766
            if(!emptyString){
767
                self.append(") ");
768
            }
769
        }
770

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

    
781
        queryString.append(self.toString());
782
        return queryString.toString();
783
    }
784

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

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

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

    
864
                self.append(" AND xml_nodes.docid in (");
865
                self.append(doclist);
866
                self.append(") AND xml_nodes.nodetype = 'TEXT'");
867
                self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
868

    
869
                addAccessRestrictionSQL(unaccessableNodePair, self);
870
            }
871

    
872
            return self.toString();
873
        }
874
    }
875

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

    
892
        boolean usePathIndex = true;
893

    
894
        // test if the are elements in the return fields
895
        if ( returnFieldList.size() == 0 ) {
896
            return null;
897
        }
898

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

    
907
        if(usePathIndex){
908
            self.append("select docid, path, nodedata, parentnodeid ");
909
            self.append("from xml_path_index where (path like '");
910

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

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

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

    
954
        }
955

    
956
        addAccessRestrictionSQL(unaccessableNodePair, self);
957

    
958
        return self.toString();
959
    }
960

    
961

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

    
969
        // Create a temporary vector and copy returnFieldList into it
970
        Vector tempVector = new Vector();
971

    
972
        Iterator it = returnFieldList.iterator();
973
        while(it.hasNext()){
974
            tempVector.add(it.next());
975
        }
976

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

    
996
        // Sort the temporary vector
997
        java.util.Collections.sort(tempVector);
998

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

    
1007

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

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

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

    
1086
            logMetacat.warn("Attribute query: " + self.toString());
1087

    
1088
            return self.toString();
1089
        }
1090
    }
1091

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

    
1149
        return self.toString();
1150
    }
1151

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

    
1161
    public static String printGetDocByDoctypeSQL(String docid)
1162
    {
1163
        StringBuffer self = new StringBuffer();
1164

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

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

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

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

    
1213
}
(52-52/63)