Project

General

Profile

1
/**
2
 *  '$RCSfile$'
3
 *    Purpose: A Class that represents a structured query, and can be
4
 *             constructed from an XML serialization conforming to
5
 *             pathquery.dtd. The printSQL() method can be used to print
6
 *             a SQL serialization of the query.
7
 *  Copyright: 2000 Regents of the University of California and the
8
 *             National Center for Ecological Analysis and Synthesis
9
 *    Authors: Matt Jones
10
 *
11
 *   '$Author: daigle $'
12
 *     '$Date: 2010-04-14 11:31:03 -0700 (Wed, 14 Apr 2010) $'
13
 * '$Revision: 5311 $'
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
 */
29

    
30
package edu.ucsb.nceas.metacat;
31

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

    
39
import edu.ucsb.nceas.metacat.shared.MetacatUtilException;
40
import edu.ucsb.nceas.metacat.util.MetacatUtil;
41
import edu.ucsb.nceas.metacat.util.SystemUtil;
42
//import edu.ucsb.nceas.utilities.UtilException;
43

    
44
import org.apache.log4j.Logger;
45
import org.xml.sax.Attributes;
46
import org.xml.sax.InputSource;
47
import org.xml.sax.SAXException;
48
import org.xml.sax.XMLReader;
49
import org.xml.sax.helpers.DefaultHandler;
50
import org.xml.sax.helpers.XMLReaderFactory;
51
import java.util.Iterator;
52

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

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

    
66
    /** flag determining whether predicates are present */
67
    private boolean containsPredicates = false;
68

    
69
    /** Identifier for this query document */
70
    private String meta_file_id;
71

    
72
    /** Title of this query */
73
    private String queryTitle;
74

    
75
    /** List of document types to be returned using package back tracing */
76
    private Vector returnDocList;
77

    
78
    /** List of document types to be searched */
79
    private Vector filterDocList;
80

    
81
    /** List of fields to be returned in result set */
82
    private Vector returnFieldList;
83
    
84
    /** List of fields with "[" and "]" in result set. This is a subset of returnFieldList.
85
     *   If some of return fields have [,  those fields will be stored this vector (we have different query for those return fields */
86
    private Vector returnFieldListWithPredicates;
87

    
88
    /** List of users owning documents to be searched */
89
    private Vector ownerList;
90

    
91
    /** The root query group that contains the recursive query constraints */
92
    private QueryGroup query = null;
93
    
94
    /** A string buffer to stored normalized query (Sometimes, the query have 
95
     * a value like "&", it will cause problem in html transform). So we need a
96
     * normalized query xml string.
97
     */
98
    private StringBuffer xml = new StringBuffer();
99

    
100
    // Query data structures used temporarily during XML parsing
101
    private Stack elementStack;
102

    
103
    private Stack queryStack;
104

    
105
    private String currentValue;
106

    
107
    private String currentPathexpr;
108

    
109
    private String parserName = null;
110

    
111
    private String accNumberSeparator = null;
112

    
113
    private boolean percentageSearch = false;
114

    
115
    private String userName = null;
116

    
117
    private static final String PUBLIC = "public";
118

    
119
    private String[] group = null;
120

    
121
    public static final String ATTRIBUTESYMBOL = "@";
122

    
123
    public static final char PREDICATE_START = '[';
124

    
125
    public static final char PREDICATE_END = ']';
126

    
127
    //private boolean hasAttributeReturnField = false;
128

    
129
    //private Hashtable attributeReturnList = new Hashtable();
130

    
131
    //private int countAttributeReturnField = 0;
132

    
133
    private StringBuffer textBuffer = new StringBuffer();
134
    
135
   
136
    private static Logger logMetacat = Logger.getLogger(QuerySpecification.class);
137

    
138
    /**
139
     * construct an instance of the QuerySpecification class
140
     *
141
     * @param queryspec
142
     *            the XML representation of the query (should conform to
143
     *            pathquery.dtd) as a Reader
144
     * @param parserName
145
     *            the fully qualified name of a Java Class implementing the
146
     *            org.xml.sax.XMLReader interface
147
     */
148
    public QuerySpecification(Reader queryspec, String parserName,
149
            String accNumberSeparator) throws IOException
150
    {
151
        super();
152

    
153
        // Initialize the class variables
154
        returnDocList = new Vector();
155
        filterDocList = new Vector();
156
        elementStack = new Stack();
157
        queryStack = new Stack();
158
        returnFieldList = new Vector();
159
        returnFieldListWithPredicates = new Vector();
160
        ownerList = new Vector();
161
        this.parserName = parserName;
162
        this.accNumberSeparator = accNumberSeparator;
163

    
164
        // Initialize the parser and read the queryspec
165
        XMLReader parser = initializeParser();
166
        if (parser == null) {
167
        	logMetacat.error("QuerySpecification() - SAX parser not instantiated properly.");
168
        }
169
        try {
170
            parser.parse(new InputSource(queryspec));
171
        } catch (SAXException se) {
172
            logMetacat.error("QuerySpecification() - SAX error parsing data: " + se.getMessage());
173
        }
174
    }
175

    
176
    /**
177
     * construct an instance of the QuerySpecification class
178
     *
179
     * @param queryspec
180
     *            the XML representation of the query (should conform to
181
     *            pathquery.dtd) as a String
182
     * @param parserName
183
     *            the fully qualified name of a Java Class implementing the
184
     *            org.xml.sax.Parser interface
185
     */
186
    public QuerySpecification(String queryspec, String parserName,
187
            String accNumberSeparator) throws IOException
188
    {
189
        this(new StringReader(queryspec), parserName, accNumberSeparator);
190
    }
191

    
192
    /**
193
     * construct an instance of the QuerySpecification class which don't need
194
     * to parser a xml document
195
     *
196
     * @param accNumberSeparator
197
     *            the separator between doc version
198
     */
199
    public QuerySpecification(String accNumberSeparator) throws IOException
200
    {
201
        // Initialize the class variables
202
        returnDocList = new Vector();
203
        filterDocList = new Vector();
204
        elementStack = new Stack();
205
        queryStack = new Stack();
206
        returnFieldList = new Vector();
207
        returnFieldListWithPredicates = new Vector();
208
        ownerList = new Vector();
209
        this.accNumberSeparator = accNumberSeparator;
210
    }
211

    
212
    /**
213
     * Method to set user name
214
     *
215
     * @param myName
216
     *            the user name
217
     */
218
    public void setUserName(String myName)
219
    {
220
        //to lower case
221
        if (myName != null) {
222
            this.userName = myName.toLowerCase();
223
        } else {
224
            this.userName = myName;
225
        }
226
    }
227

    
228
    /**
229
     * Method to set user group
230
     *
231
     * @param myGroup
232
     *            the user group
233
     */
234
    public void setGroup(String[] myGroup)
235
    {
236
        this.group = myGroup;
237
    }
238

    
239
    /**
240
     * Method to indicate this query is a percentage search
241
     */
242
    public boolean isPercentageSearch()
243
    {
244
        return percentageSearch;
245
    }
246

    
247
    /*
248
     * Method to get owner query. If it is owner it has all permission
249
     */
250
    private String createOwerQuery()
251
    {
252
        String ownerQuery = null;
253
        //if user is public, we don't need to run owner query
254
        if (userName != null && !userName.equalsIgnoreCase(PUBLIC))
255
        {
256
	        ownerQuery = "SELECT docid FROM xml_documents WHERE ";
257
	        if (userName != null && !userName.equals("")) {
258
	            ownerQuery = ownerQuery + "lower(user_owner) ='" + userName + "'";
259
	        }
260
        }
261
        logMetacat.info("QuerySpecification.createOwerQuery - OwnerQuery: " + ownerQuery);
262
        return ownerQuery;
263
    }
264

    
265
    /*
266
     * Method to create query for xml_access, this part is to get docid list
267
     * which have a allow rule for a given user
268
     */
269
    private String createAllowRuleQuery()
270
    {
271
        String allowQuery = null;
272
        String allowString = constructAllowString();
273
        allowQuery = "SELECT docid from xml_access WHERE( " + allowString;
274
        allowQuery = allowQuery + ")";
275
        logMetacat.info("QuerySpecification.createAllowRuleQuery - allow query is: " + allowQuery);
276
        return allowQuery;
277

    
278
    }
279

    
280
    /* Method to construct a allow rule string */
281
    private String constructAllowString()
282
    {
283
        String allowQuery = "";
284
        
285
       // add public
286
        allowQuery = "(lower(principal_name) = '" + PUBLIC
287
                + "'";
288
                
289
        // add user name
290
        if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
291
            allowQuery = allowQuery + "OR lower(principal_name) = '" + userName +"'";
292
                    
293
        }
294
        // add  group
295
        if (group != null) {
296
            for (int i = 0; i < group.length; i++) {
297
                String groupUint = group[i];
298
                if (groupUint != null && !groupUint.equals("")) {
299
                    groupUint = groupUint.toLowerCase();
300
                    allowQuery = allowQuery + " OR lower(principal_name) = '"
301
                            + groupUint + "'";
302
                }//if
303
            }//for
304
        }//if
305
        // add allow rule
306
        allowQuery = allowQuery + ") AND perm_type = 'allow'" + " AND permission > 3";
307
        logMetacat.info("QuerySpecification.constructAllowString - allow string is: " + allowQuery);
308
        return allowQuery;
309
    }
310

    
311
    /*
312
     * Method to create query for xml_access, this part is to get docid list
313
     * which have a deny rule and perm_order is allowFirst for a given user.
314
     * This means the user will be denied to read
315
     */
316
    private String createDenyRuleQuery()
317
    {
318
        String denyQuery = null;
319
        String denyString = constructDenyString();
320
        denyQuery = "SELECT docid from xml_access WHERE( " + denyString;
321
        denyQuery = denyQuery + ") ";
322
        logMetacat.info("QuerySpecification.createDenyRuleQuery - denyquery is: " + denyQuery);
323
        return denyQuery;
324

    
325
    }
326

    
327
    /* Construct deny string */
328
    private String constructDenyString()
329
    {
330
        String denyQuery = "";
331
         
332
        // add public
333
        denyQuery = "(lower(principal_name) = '" + PUBLIC
334
                 + "'";
335
                 
336
         // add user name
337
         if (userName != null && !userName.equals("") && !userName.equalsIgnoreCase(PUBLIC)) {
338
        	 denyQuery = denyQuery + "OR lower(principal_name) = '" + userName +"'";
339
                     
340
         }
341
         // add  groups
342
         if (group != null) {
343
             for (int i = 0; i < group.length; i++) {
344
                 String groupUint = group[i];
345
                 if (groupUint != null && !groupUint.equals("")) {
346
                     groupUint = groupUint.toLowerCase();
347
                     denyQuery = denyQuery + " OR lower(principal_name) = '"
348
                             + groupUint + "'";
349
                 }//if
350
             }//for
351
         }//if
352
         // add deny rules
353
         denyQuery = denyQuery + ") AND perm_type = 'deny'" +  " AND perm_order ='allowFirst'" +" AND permission > 3";
354
         logMetacat.info("QuerySpecification.constructDenyString - deny string is: " + denyQuery);
355
         return denyQuery;
356
        
357
    }
358

    
359
    /**
360
     * Method to append a access control query to SQL. So in DBQuery class, we
361
     * can get docid from both user specified query and access control query.
362
     * We don't need to checking permission after we get the doclist. It will
363
     * be good to performance
364
     *
365
     */
366
    public String getAccessQuery()
367
    {
368
        String accessQuery = null;
369
        String onwer = createOwerQuery();
370
        String allow = createAllowRuleQuery();
371
        String deny = createDenyRuleQuery();
372

    
373
        if (onwer != null)
374
        {
375
          accessQuery = " AND (docid IN(" + onwer + ")";
376
          accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
377
                + " AND docid NOT IN (" + deny + ")))";
378
        }
379
        else
380
        {
381
        	accessQuery = " AND (docid IN (" + allow + ")"
382
                + " AND docid NOT IN (" + deny + "))";
383
        }
384
        logMetacat.info("QuerySpecification.getAccessQuery - access query is: " + accessQuery);
385
        return accessQuery;
386
    }
387

    
388
    /**
389
     * Returns true if the parsed query contains and extended xml query (i.e.
390
     * there is at least one &lt;returnfield&gt; in the pathquery document)
391
     */
392
    public boolean containsExtendedSQL()
393
    {
394
        if (containsExtendedSQL) {
395
            return true;
396
        } else {
397
            return false;
398
        }
399
    }
400

    
401
  
402
    /**
403
     * Accessor method to return the identifier of this Query
404
     */
405
    public String getIdentifier()
406
    {
407
        return meta_file_id;
408
    }
409

    
410
    /**
411
     * method to set the identifier of this query
412
     */
413
    public void setIdentifier(String id)
414
    {
415
        this.meta_file_id = id;
416
    }
417

    
418
    /**
419
     * Accessor method to return the title of this Query
420
     */
421
    public String getQueryTitle()
422
    {
423
        return queryTitle;
424
    }
425

    
426
    /**
427
     * method to set the title of this query
428
     */
429
    public void setQueryTitle(String title)
430
    {
431
        this.queryTitle = title;
432
    }
433

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

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

    
451
    /**
452
     * Accessor method to return a vector of the filter doc types as defined in
453
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
454
     */
455
    public Vector getFilterDocList()
456
    {
457
        return this.filterDocList;
458
    }
459

    
460
    /**
461
     * method to set the list of filter docs of this query
462
     */
463
    public void setFilterDocList(Vector filterDocList)
464
    {
465
        this.filterDocList = filterDocList;
466
    }
467

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

    
477
    /**
478
     * method to set the list of fields to be returned by this query
479
     */
480
    public void setReturnFieldList(Vector returnFieldList)
481
    {
482
        this.returnFieldList = returnFieldList;
483
    }
484

    
485
    /**
486
     * Accessor method to return a vector of the owner fields as defined in the
487
     * &lt;owner&gt; tag in the pathquery dtd.
488
     */
489
    public Vector getOwnerList()
490
    {
491
        return this.ownerList;
492
    }
493

    
494
    /**
495
     * method to set the list of owners used to constrain this query
496
     */
497
    public void setOwnerList(Vector ownerList)
498
    {
499
        this.ownerList = ownerList;
500
    }
501

    
502
    /**
503
     * get the QueryGroup used to express query constraints
504
     */
505
    public QueryGroup getQueryGroup()
506
    {
507
        return query;
508
    }
509

    
510
    /**
511
     * set the querygroup
512
     */
513
    public void setQueryGroup(QueryGroup group)
514
    {
515
        query = group;
516
    }
517

    
518
    /**
519
     * set if this query sepcification has extendQuery(has return doc type or
520
     * not)
521
     */
522
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
523
    {
524
        containsExtendedSQL = hasExtenedQuery;
525
    }
526

    
527
    /**
528
     * Set up the SAX parser for reading the XML serialized query
529
     */
530
    private XMLReader initializeParser()
531
    {
532
        XMLReader parser = null;
533

    
534
        // Set up the SAX document handlers for parsing
535
        try {
536

    
537
            // Get an instance of the parser
538
            parser = XMLReaderFactory.createXMLReader(parserName);
539

    
540
            // Set the ContentHandler to this instance
541
            parser.setContentHandler(this);
542

    
543
            // Set the error Handler to this instance
544
            parser.setErrorHandler(this);
545

    
546
        } catch (Exception e) {
547
            logMetacat.error("QuerySpecification.getAccessQuery - Error: " + e.getMessage());
548
        }
549

    
550
        return parser;
551
    }
552

    
553
    /**
554
     * callback method used by the SAX Parser when the start tag of an element
555
     * is detected. Used in this context to parse and store the query
556
     * information in class variables.
557
     */
558
    public void startElement(String uri, String localName, String qName,
559
            Attributes atts) throws SAXException
560
    {
561
        logMetacat.debug("QuerySpecification.startElement - start element " + localName);
562
        BasicNode currentNode = new BasicNode(localName);
563
        //write element name into xml buffer.
564
        xml.append("<");
565
        xml.append(localName);
566
        // add attributes to BasicNode here
567
        if (atts != null) {
568
            int len = atts.getLength();
569
            for (int i = 0; i < len; i++) {
570
                currentNode
571
                        .setAttribute(atts.getLocalName(i), atts.getValue(i));
572
                xml.append(" ");
573
                xml.append(atts.getLocalName(i));
574
                xml.append("=\"");
575
                xml.append(atts.getValue(i));
576
                xml.append("\"");
577
            }
578
        }
579
        xml.append(">");
580

    
581
        elementStack.push(currentNode);
582
        if (currentNode.getTagName().equals("querygroup")) {
583
            QueryGroup currentGroup = new QueryGroup(currentNode
584
                    .getAttribute("operator"));
585
            if (query == null) {
586
                query = currentGroup;
587
            } else {
588
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
589
                parentGroup.addChild(currentGroup);
590
            }
591
            queryStack.push(currentGroup);
592
        }
593
        logMetacat.debug("QuerySpecification.startElement - ending startElement " + localName);
594
    }
595

    
596
    /**
597
     * callback method used by the SAX Parser when the end tag of an element is
598
     * detected. Used in this context to parse and store the query information
599
     * in class variables.
600
     */
601
    public void endElement(String uri, String localName, String qName)
602
            throws SAXException
603
    {
604
    	 logMetacat.debug("QuerySpecification.endElement - endElement "+localName);
605
        BasicNode leaving = (BasicNode) elementStack.pop();
606
        if (leaving.getTagName().equals("queryterm")) {
607
            boolean isCaseSensitive = (new Boolean(leaving
608
                    .getAttribute("casesensitive"))).booleanValue();
609
            QueryTerm currentTerm = null;
610
            if (currentPathexpr == null) {
611
                currentTerm = new QueryTerm(isCaseSensitive, leaving
612
                        .getAttribute("searchmode"), currentValue);
613
            } else {
614
                currentTerm = new QueryTerm(isCaseSensitive, leaving
615
                        .getAttribute("searchmode"), currentValue,
616
                        currentPathexpr);
617
            }
618
            QueryGroup currentGroup = (QueryGroup) queryStack.peek();
619
            currentGroup.addChild(currentTerm);
620
            currentValue = null;
621
            currentPathexpr = null;
622
        } else if (leaving.getTagName().equals("querygroup")) {
623
            QueryGroup leavingGroup = (QueryGroup) queryStack.pop();
624
        } else if (leaving.getTagName().equals("meta_file_id")) {
625
              meta_file_id = textBuffer.toString().trim();
626
        } else if (leaving.getTagName().equals("querytitle")) {
627
              queryTitle = textBuffer.toString().trim();
628
        } else if (leaving.getTagName().equals("value")) {
629
              currentValue = textBuffer.toString().trim();
630
              currentValue = MetacatUtil.normalize(currentValue);
631
        } else if (leaving.getTagName().equals("pathexpr")) {
632
              currentPathexpr = textBuffer.toString().trim();
633
        } else if (leaving.getTagName().equals("returndoctype")) {
634
              returnDocList.add(textBuffer.toString().trim());
635
        } else if (leaving.getTagName().equals("filterdoctype")) {
636
              filterDocList.add(textBuffer.toString().trim());
637
        } else if (leaving.getTagName().equals("returnfield")) {
638
              handleReturnField(textBuffer.toString().trim());
639
        } else if (leaving.getTagName().equals("filterdoctype")) {
640
              filterDocList.add(textBuffer.toString().trim());
641
        } else if (leaving.getTagName().equals("owner")) {
642
              ownerList.add(textBuffer.toString().trim());
643
        }
644
        String normalizedXML = textBuffer.toString().trim();
645
        logMetacat.debug("QuerySpecification.endElement - before normalize: " + normalizedXML);
646
        normalizedXML =  MetacatUtil.normalize(normalizedXML);
647
        logMetacat.debug("QuerySpecification.endElement - after normalize " + normalizedXML);
648
        xml.append(normalizedXML);
649
        xml.append("</");
650
        xml.append(localName);
651
        xml.append(">");
652
        //rest textBuffer
653
        textBuffer = new StringBuffer();
654

    
655
    }
656
    
657
    /**
658
     * Gets normailized query string in xml format, which can be transformed
659
     * to html
660
     */
661
    public String getNormalizedXMLQuery()
662
    {
663
    	//System.out.println("normailized xml \n"+xml.toString());
664
    	return xml.toString();
665
    }
666
    
667

    
668
    /**
669
     * callback method used by the SAX Parser when the text sequences of an xml
670
     * stream are detected. Used in this context to parse and store the query
671
     * information in class variables.
672
     */
673
    public void characters(char ch[], int start, int length)
674
    {
675
      // buffer all text nodes for same element. This is for text was splited
676
      // into different nodes
677
      String text = new String(ch, start, length);
678
      logMetacat.debug("QuerySpecification.characters - the text in characters " + text);
679
      textBuffer.append(text);
680

    
681
    }
682

    
683
   /**
684
    * Method to handle return field. It will be callied in ecogrid part
685
    * @param inputString
686
    */
687
    public void handleReturnField(String inputString)
688
    {
689
        int attributePos = inputString.indexOf(ATTRIBUTESYMBOL);
690
        int predicateStart = -1;
691
        int predicateEnd;
692
        boolean hasPredicate = false;
693

    
694
        while (true)
695
        {
696
            predicateStart = inputString.indexOf(PREDICATE_START, predicateStart + 1);
697

    
698
            if (attributePos == -1)
699
                break;
700

    
701
            if (predicateStart == -1)
702
                break;
703

    
704
            hasPredicate = true;
705

    
706
            if (attributePos < predicateStart)
707
                break;
708

    
709
            predicateEnd = inputString.indexOf(PREDICATE_END, predicateStart);
710

    
711
            if (predicateEnd == -1)
712
            {
713
                logMetacat.warn("QuerySpecification.handleReturnField - Invalid path: " + inputString);
714
                return;
715
            }
716

    
717
            while (attributePos < predicateEnd)
718
            {
719
                attributePos = inputString.indexOf(ATTRIBUTESYMBOL, attributePos + 1);
720

    
721
                if (attributePos == -1)
722
                    break;
723
            }
724
        }
725

    
726
        if (hasPredicate)
727
        {
728
            containsPredicates = true;
729
            returnFieldListWithPredicates.add(inputString);
730
        }
731

    
732
        containsExtendedSQL = true;
733
   
734
        // no attribute value will be returned
735
        logMetacat.info("QuerySpecification.handleReturnField - there are no attributes in the XPATH statement" );
736
        returnFieldList.add(inputString);       
737
    }
738

    
739
    /**
740
     * create a SQL serialization of the query that this instance represents
741
     */
742
    public String printSQL(boolean useXMLIndex)
743
    {
744

    
745
        StringBuffer self = new StringBuffer();
746
        StringBuffer queryString = new StringBuffer();
747

    
748
        queryString.append("SELECT docid,docname,doctype,");
749
        queryString.append("date_created, date_updated, rev ");
750
        queryString.append("FROM xml_documents WHERE");
751

    
752
        // Get the query from the QueryGroup and check
753
        // if no query has been returned
754
        String queryFromQueryGroup;
755
        if (query != null) {
756
        	queryFromQueryGroup = query.printSQL(useXMLIndex);
757
        } else {
758
        	queryFromQueryGroup = "";
759
        }
760
        logMetacat.info("QuerySpecification.printSQL - Query : " + queryFromQueryGroup);
761
        
762
        if(!queryFromQueryGroup.trim().equals("")){
763
            self.append(" docid IN (");
764
            self.append(queryFromQueryGroup);
765
            self.append(") ");
766
        }
767

    
768
        // Add SQL to filter for doctypes requested in the query
769
        // This is an implicit OR for the list of doctypes. Only doctypes in
770
        // this
771
        // list will be searched if the tag is present
772
        if (!filterDocList.isEmpty()) {
773
            boolean firstdoctype = true;
774
            boolean emptyString = true;
775

    
776
            if(!self.toString().equals("")){
777
                self.append(" AND (");
778
                emptyString = false;
779
            }
780

    
781
            Enumeration en = filterDocList.elements();
782
            while (en.hasMoreElements()) {
783
                String currentDoctype = (String) en.nextElement();
784
                if (firstdoctype) {
785
                    firstdoctype = false;
786
                    self.append(" doctype = '" + currentDoctype + "'");
787
                } else {
788
                    self.append(" OR doctype = '" + currentDoctype + "'");
789
                }
790
            }
791

    
792
            if(!emptyString){
793
                self.append(") ");
794
            }
795
        }
796

    
797
        // Add SQL to filter for owners requested in the query
798
        // This is an implicit OR for the list of owners
799
        if (!ownerList.isEmpty()) {
800
            boolean first = true;
801
            boolean emptyString = true;
802

    
803
            if(!self.toString().equals("")){
804
                self.append(" AND (");
805
                emptyString = false;
806
            }
807

    
808
            Enumeration en = ownerList.elements();
809
            while (en.hasMoreElements()) {
810
                String current = (String) en.nextElement();
811
                if (current != null) {
812
                    current = current.toLowerCase();
813
                }
814
                if (first) {
815
                    first = false;
816
                    self.append(" lower(user_owner) = '" + current + "'");
817
                } else {
818
                    self.append(" OR lower(user_owner) = '" + current + "'");
819
                }
820
            }
821

    
822
            if(!emptyString){
823
                self.append(") ");
824
            }
825
        }
826

    
827
        // if there is only one percentage search item, this query is a
828
        // percentage search query
829
        if (query != null) {
830
        	logMetacat.info("QuerySpecification.printSQL - percentage number: " + query.getPercentageSymbolCount());
831
			if (query.getPercentageSymbolCount() == 1) {
832
				logMetacat.info("QuerySpecification.printSQL - It is a percentage search");
833
				percentageSearch = true;
834
			}
835
        }
836

    
837
        queryString.append(self.toString());
838
        return queryString.toString();
839
    }
840

    
841
   
842

    
843
    /**
844
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
845
     * pathquery document. This allows for customization of the returned fields.
846
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
847
     * xml_nodes to find the fields to be included by their path expression, and
848
     * avoids the use of the xml_index table.
849
     *
850
     * @param doclist the list of document ids to search
851
     * @param unaccessableNodePair the node pairs (start id and end id) which
852
     *            this user should not access
853
     * @param useXMLIndex a boolean flag indicating whether to search using
854
     *            xml_index
855
     */
856
    public String printExtendedSQL(String doclist, boolean useXMLIndex)
857
    {
858
        if (useXMLIndex && !containsPredicates)
859
        {
860
            return printExtendedSQL(doclist);
861
        }
862
        else
863
        {
864
            StringBuffer self = new StringBuffer();
865
            boolean firstfield = true;
866
            // first part comes from fields without  predicates 
867
            String queryFromWithoutPrecidates = printExtendedSQL(doclist);
868
             if (queryFromWithoutPrecidates != null)
869
             {
870
            	 // it has return fields without predicate
871
            	 self.append(queryFromWithoutPrecidates);
872
            	 firstfield = false;
873
             }
874
            //put the returnfields into the query
875
            //the for loop allows for multiple fields
876
            for (int i = 0; i <   returnFieldListWithPredicates.size(); i++)
877
            {
878
                if (firstfield)
879
                {
880
                    firstfield = false;
881
                }
882
                else
883
                {
884
                    self.append(" UNION ");
885
                }
886
                String path  = (String)  returnFieldListWithPredicates.elementAt(i);
887
                self.append("select xml_nodes.docid, ");
888
                self.append("'"+ path.replaceAll("'", "''") + "' as path, ");
889
                self.append("xml_nodes.nodedata, ");
890
                self.append("xml_nodes.parentnodeid, ");
891
                self.append("xml_nodes.nodetype ");
892
                //self.append("from xml_nodes, xml_documents ");
893
                self.append("from xml_nodes ");
894
                self.append("where ");
895
                self.append(QueryTerm.useNestedStatements(path));
896

    
897
                self.append(" AND xml_nodes.docid in (");
898
                self.append(doclist);
899
                if (returnFieldIsAttribute(path))
900
                {
901
                    self.append(")");
902
                }
903
                else
904
                {
905
                     self.append(") AND xml_nodes.nodetype = 'TEXT'");
906
                }
907
                //self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
908

    
909
                //addAccessRestrictionSQL(unaccessableNodePair, self);
910
            }
911

    
912
            return self.toString();
913
        }
914
    }
915
    
916
    /*
917
     * Determines the returnfield is an attribute of not. 
918
     * For given returnfield, this programm will cut the part of path after last slash.
919
     * If no slash in the path, the original string will be considered as last part.
920
     * If first character of last part is @ it will retrun true. 
921
     */
922
    private boolean returnFieldIsAttribute(String path)
923
    {
924
    	boolean isAttribute = false;
925
    	if (path != null)
926
    	{
927
    	    int slashIndex = path.lastIndexOf("/");
928
    	    if (slashIndex !=-1)
929
    	    {
930
    	    	// if there is slash in the path, path should be replace by the last part
931
    	    	path = path.substring(slashIndex+1);
932
    	    }
933
    	    logMetacat.debug("QuerySpecification.returnFieldIsAttribute - final path is " + path);
934
    	    // if first of character of path is @, the path is attribute
935
    	    if (path.charAt(0) == '@')
936
    	    {
937
    	    	logMetacat.debug("QuerySpecification.returnFieldIsAttribute - it is an attribute");
938
    	    	isAttribute = true;
939
    	    }
940
    	}
941
    	return isAttribute;
942
    }
943

    
944
    /**
945
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
946
     * pathquery document. This allows for customization of the returned fields.
947
     * It uses the xml_index table and so assumes that this table has been
948
     * built.
949
     *
950
     * @param doclist the list of document ids to search
951
     * @param unaccessableNodePair the node pairs (start id and end id)
952
     *            which this user should not access
953
     */
954
    private String printExtendedSQL(String doclist) {
955
        logMetacat.debug("QuerySpecification.printExtendedSQL - in printExtendedSQL");
956
        StringBuffer self = new StringBuffer();
957
        Vector elementVector = new Vector();
958
        Vector attributeVector = new Vector();
959

    
960
        boolean usePathIndex = true;
961

    
962
        // test if the are elements in the return fields
963
        if ( returnFieldList.size() == 0 ) {
964
            return null;
965
        }
966

    
967
        for (int i = 0; i < returnFieldList.size(); i++) {
968
        	String path = (String)returnFieldList.elementAt(i);
969
        	// Since return fileds having preicates will be handle in another path,
970
        	// we should skip it.
971
        	if (returnFieldListWithPredicates.contains(path)) {
972
        		continue;
973
        	}
974
        	
975
        	if (path != null && path.indexOf(ATTRIBUTESYMBOL) != -1) {
976
        		attributeVector.add(path);
977
        	} else {
978
        		elementVector.add(path);
979
        	} 
980
        	
981

    
982
        	try {
983
				if (!SystemUtil.getPathsForIndexing().contains(path)) {
984
					usePathIndex = false;   
985
				}
986
			} catch (MetacatUtilException mue) {
987
				logMetacat.warn("QuerySpecification.printExtendedSQL - Could not get index paths: "  + mue.getMessage());
988
			}
989
         
990
        }
991
        // check if has return field
992
        if (elementVector.size() == 0 && attributeVector.size()==0)
993
        {
994
        	return null;
995
        }
996

    
997
        if(usePathIndex){
998
            self.append("select docid, path, nodedata, parentnodeid, null as nodetype ");
999
            self.append("from xml_path_index where path in( '");
1000

    
1001
            boolean firstfield = true;
1002
            //put the returnfields into the query
1003
            //the for loop allows for multiple fields
1004
            for (int i = 0; i < returnFieldList.size(); i++) {
1005
                if (firstfield) {
1006
                    firstfield = false;
1007
                    self.append( (String) returnFieldList.elementAt(i));
1008
                    self.append("' ");
1009
                }
1010
                else {
1011
                    self.append(", '");
1012
                    self.append( (String) returnFieldList.elementAt(i));
1013
                    self.append("' ");
1014
                }
1015
            }
1016
            self.append(") AND docid in (");
1017
            self.append(doclist);
1018
            self.append(")");
1019

    
1020
        } else {
1021
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1022
            self.append("xml_nodes.parentnodeid, ");
1023
            self.append("xml_nodes.nodetype ");
1024
            self.append("FROM xml_index, xml_nodes WHERE (");
1025
           
1026

    
1027
            boolean firstElement = true;
1028
            boolean firstAttribute = true;
1029
            //put the returnfields into the query
1030
            //the for loop allows for multiple fields
1031
            if (elementVector.size() != 0)
1032
            {
1033
	            for (int i = 0; i < elementVector.size(); i++) {
1034
	            	String path = (String) elementVector.elementAt(i);
1035
	                if (firstElement) {
1036
	                	firstElement = false;
1037
	                	self.append(" (xml_index.nodeid=xml_nodes.parentnodeid AND xml_index.path IN ('");
1038
	                    self.append(path);
1039
	                    self.append("'");
1040
	                 }
1041
	                else 
1042
	                {
1043
	                    self.append(", '");
1044
	                    self.append(path);
1045
	                    self.append("' ");
1046
	                }
1047
	            }
1048
	            self.append(") AND xml_nodes.nodetype = 'TEXT')");
1049
            }
1050
            
1051
            if (attributeVector.size() != 0)
1052
            {
1053
            	for (int j=0; j<attributeVector.size(); j++)
1054
            	{
1055
            		String path = (String) attributeVector.elementAt(j);
1056
            		if (firstAttribute)
1057
            		{
1058
            			firstAttribute = false;
1059
            			if (!firstElement)
1060
                		{
1061
                			self.append(" OR ");
1062
                		}
1063
            			self.append(" (xml_index.nodeid=xml_nodes.nodeid AND ( xml_index.path IN ( '");
1064
	                    self.append(path);
1065
	                    self.append("'");
1066
            		}
1067
            		else 
1068
	                {
1069
	                    self.append(", '");
1070
	                    self.append(path);
1071
	                    self.append("' ");
1072
	                }
1073
            	}
1074
            	self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'))");
1075
            }
1076
            
1077
          
1078
            self.append(") AND xml_nodes.docid in (");
1079
            self.append(doclist);
1080
            self.append(")");
1081

    
1082
        }
1083

    
1084
        return self.toString();
1085
    }
1086

    
1087

    
1088
    /**
1089
     * Method to return a String generated after sorting the returnFieldList
1090
     * Vector
1091
     */
1092
    public String getSortedReturnFieldString(){
1093
        String returnFields = "";
1094

    
1095
        // Create a temporary vector and copy returnFieldList into it
1096
        Vector tempVector = new Vector();
1097

    
1098
        Iterator it = returnFieldList.iterator();
1099
        while(it.hasNext()){
1100
            tempVector.add(it.next());
1101
        }
1102

    
1103
        /*Enumeration attEnum = attributeReturnList.elements();
1104
        while(attEnum.hasMoreElements()){
1105
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1106
	    String rfield = "";
1107
            if(tempIt.hasNext()){
1108
		String element = (String)tempIt.next();
1109
		if(element != null) {
1110
		    rfield +=element;
1111
		}
1112
	    }
1113
            if(tempIt.hasNext()){
1114
		String attribute = (String)tempIt.next();
1115
		if(attribute != null) {
1116
  		    rfield = rfield + "@" + attribute;
1117
                }
1118
	    }
1119
            tempVector.add(rfield);
1120
        }*/
1121

    
1122
        // Sort the temporary vector
1123
        java.util.Collections.sort(tempVector);
1124

    
1125
        // Generate the string and return it
1126
        it = tempVector.iterator();
1127
        while(it.hasNext()){
1128
            returnFields = returnFields + it.next() + "|";
1129
        }
1130
        return returnFields;
1131
    }
1132

    
1133

    
1134
  
1135

    
1136

    
1137
    public static String printRelationSQL(String docid)
1138
    {
1139
        StringBuffer self = new StringBuffer();
1140
        self.append("select subject, relationship, object, subdoctype, ");
1141
        self.append("objdoctype from xml_relation ");
1142
        self.append("where docid like '").append(docid).append("'");
1143
        return self.toString();
1144
    }
1145

    
1146
    public static String printGetDocByDoctypeSQL(String docid)
1147
    {
1148
        StringBuffer self = new StringBuffer();
1149

    
1150
        self.append("SELECT docid,docname,doctype,");
1151
        self.append("date_created, date_updated ");
1152
        self.append("FROM xml_documents WHERE docid IN (");
1153
        self.append(docid).append(")");
1154
        return self.toString();
1155
    }
1156

    
1157
    /**
1158
     * create a String description of the query that this instance represents.
1159
     * This should become a way to get the XML serialization of the query.
1160
     */
1161
    public String toString()
1162
    {
1163
        return "meta_file_id=" + meta_file_id + "\n" + query;
1164
        //DOCTITLE attr cleared from the db
1165
        //return "meta_file_id=" + meta_file_id + "\n" +
1166
        //"querytitle=" + querytitle + "\n" + query;
1167
    }
1168

    
1169
    /** A method to get rid of attribute part in path expression */
1170
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1171
    {
1172
        if (pathExpression == null) { return null; }
1173
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1174
        String newExpression = null;
1175
        if (index != 0) {
1176
            newExpression = pathExpression.substring(0, index - 1);
1177
        }
1178
        logMetacat.info("QuerySpecification.newPathExpressionWithOutAttribute - The path expression without attributes: "
1179
                + newExpression);
1180
        return newExpression;
1181
    }
1182

    
1183
    /** A method to get attribute name from path */
1184
    public static String getAttributeName(String path)
1185
    {
1186
        if (path == null) { return null; }
1187
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1188
        int size = path.length();
1189
        String attributeName = null;
1190
        if (index != 1) {
1191
            attributeName = path.substring(index + 1, size);
1192
        }
1193
        logMetacat.info("QuerySpecification.getAttributeName - The attirbute name from path: " + attributeName);
1194
        return attributeName;
1195
    }
1196

    
1197
}
(55-55/65)