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: 2008-07-06 21:25:34 -0700 (Sun, 06 Jul 2008) $'
13
 * '$Revision: 4080 $'
14
 *
15
 * This program is free software; you can redistribute it and/or modify
16
 * it under the terms of the GNU General Public License as published by
17
 * the Free Software Foundation; either version 2 of the License, or
18
 * (at your option) any later version.
19
 *
20
 * This program is distributed in the hope that it will be useful,
21
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 * GNU General Public License for more details.
24
 *
25
 * You should have received a copy of the GNU General Public License
26
 * along with this program; if not, write to the Free Software
27
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
28
 */
29

    
30
package edu.ucsb.nceas.metacat;
31

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

    
40
import edu.ucsb.nceas.dbadapter.AbstractDatabase;
41
import edu.ucsb.nceas.metacat.util.MetaCatUtil;
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
    /** flag determining whether predicates are present */
66
    private boolean containsPredicates = false;
67

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

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

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

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

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

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

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

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

    
102
    private Stack queryStack;
103

    
104
    private String currentValue;
105

    
106
    private String currentPathexpr;
107

    
108
    private String parserName = null;
109

    
110
    private String accNumberSeparator = null;
111

    
112
    private static final AbstractDatabase dbAdapter = MetaCatUtil.dbAdapter;
113

    
114
    private boolean percentageSearch = false;
115

    
116
    private String userName = null;
117

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

    
120
    private String[] group = null;
121

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

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

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

    
128
    //private boolean hasAttributeReturnField = false;
129

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

    
132
    //private int countAttributeReturnField = 0;
133

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

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

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

    
165
        // Initialize the parser and read the queryspec
166
        XMLReader parser = initializeParser();
167
        if (parser == null) {
168
            System.err.println("SAX parser not instantiated properly.");
169
        }
170
        try {
171
            parser.parse(new InputSource(queryspec));
172
        } catch (SAXException e) {
173
            System.err.println("error parsing data in "
174
                    + "QuerySpecification.QuerySpecification");
175
            System.err.println(e.getMessage());
176
        }
177
    }
178

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

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

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

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

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

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

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

    
281
    }
282

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

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

    
328
    }
329

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

    
362
    /**
363
     * Method to append a access control query to SQL. So in DBQuery class, we
364
     * can get docid from both user specified query and access control query.
365
     * We don't need to checking permission after we get the doclist. It will
366
     * be good to performance
367
     *
368
     */
369
    public String getAccessQuery()
370
    {
371
        String accessQuery = null;
372
        String onwer = createOwerQuery();
373
        String allow = createAllowRuleQuery();
374
        String deny = createDenyRuleQuery();
375
        //logMetacat.warn("onwer " +onwer);
376
        //logMetacat.warn("allow "+allow);
377
        //logMetacat.warn("deny "+deny);
378
        if (onwer != null)
379
        {
380
          accessQuery = " AND (docid IN(" + onwer + ")";
381
          accessQuery = accessQuery + " OR (docid IN (" + allow + ")"
382
                + " AND docid NOT IN (" + deny + ")))";
383
        }
384
        else
385
        {
386
        	accessQuery = " AND (docid IN (" + allow + ")"
387
                + " AND docid NOT IN (" + deny + "))";
388
        }
389
        logMetacat.warn("accessquery is: " + accessQuery);
390
        return accessQuery;
391
    }
392

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

    
406
  
407
    /**
408
     * Accessor method to return the identifier of this Query
409
     */
410
    public String getIdentifier()
411
    {
412
        return meta_file_id;
413
    }
414

    
415
    /**
416
     * method to set the identifier of this query
417
     */
418
    public void setIdentifier(String id)
419
    {
420
        this.meta_file_id = id;
421
    }
422

    
423
    /**
424
     * Accessor method to return the title of this Query
425
     */
426
    public String getQueryTitle()
427
    {
428
        return queryTitle;
429
    }
430

    
431
    /**
432
     * method to set the title of this query
433
     */
434
    public void setQueryTitle(String title)
435
    {
436
        this.queryTitle = title;
437
    }
438

    
439
    /**
440
     * Accessor method to return a vector of the return document types as
441
     * defined in the &lt;returndoctype&gt; tag in the pathquery dtd.
442
     */
443
    public Vector getReturnDocList()
444
    {
445
        return this.returnDocList;
446
    }
447

    
448
    /**
449
     * method to set the list of return docs of this query
450
     */
451
    public void setReturnDocList(Vector returnDocList)
452
    {
453
        this.returnDocList = returnDocList;
454
    }
455

    
456
    /**
457
     * Accessor method to return a vector of the filter doc types as defined in
458
     * the &lt;filterdoctype&gt; tag in the pathquery dtd.
459
     */
460
    public Vector getFilterDocList()
461
    {
462
        return this.filterDocList;
463
    }
464

    
465
    /**
466
     * method to set the list of filter docs of this query
467
     */
468
    public void setFilterDocList(Vector filterDocList)
469
    {
470
        this.filterDocList = filterDocList;
471
    }
472

    
473
    /**
474
     * Accessor method to return a vector of the extended return fields as
475
     * defined in the &lt;returnfield&gt; tag in the pathquery dtd.
476
     */
477
    public Vector getReturnFieldList()
478
    {
479
        return this.returnFieldList;
480
    }
481

    
482
    /**
483
     * method to set the list of fields to be returned by this query
484
     */
485
    public void setReturnFieldList(Vector returnFieldList)
486
    {
487
        this.returnFieldList = returnFieldList;
488
    }
489

    
490
    /**
491
     * Accessor method to return a vector of the owner fields as defined in the
492
     * &lt;owner&gt; tag in the pathquery dtd.
493
     */
494
    public Vector getOwnerList()
495
    {
496
        return this.ownerList;
497
    }
498

    
499
    /**
500
     * method to set the list of owners used to constrain this query
501
     */
502
    public void setOwnerList(Vector ownerList)
503
    {
504
        this.ownerList = ownerList;
505
    }
506

    
507
    /**
508
     * get the QueryGroup used to express query constraints
509
     */
510
    public QueryGroup getQueryGroup()
511
    {
512
        return query;
513
    }
514

    
515
    /**
516
     * set the querygroup
517
     */
518
    public void setQueryGroup(QueryGroup group)
519
    {
520
        query = group;
521
    }
522

    
523
    /**
524
     * set if this query sepcification has extendQuery(has return doc type or
525
     * not)
526
     */
527
    public void setContainsExtenedSQL(boolean hasExtenedQuery)
528
    {
529
        containsExtendedSQL = hasExtenedQuery;
530
    }
531

    
532
    /**
533
     * Set up the SAX parser for reading the XML serialized query
534
     */
535
    private XMLReader initializeParser()
536
    {
537
        XMLReader parser = null;
538

    
539
        // Set up the SAX document handlers for parsing
540
        try {
541

    
542
            // Get an instance of the parser
543
            parser = XMLReaderFactory.createXMLReader(parserName);
544

    
545
            // Set the ContentHandler to this instance
546
            parser.setContentHandler(this);
547

    
548
            // Set the error Handler to this instance
549
            parser.setErrorHandler(this);
550

    
551
        } catch (Exception e) {
552
            System.err.println("Error in QuerySpcecification.initializeParser "
553
                    + e.toString());
554
        }
555

    
556
        return parser;
557
    }
558

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

    
587
        elementStack.push(currentNode);
588
        if (currentNode.getTagName().equals("querygroup")) {
589
            QueryGroup currentGroup = new QueryGroup(currentNode
590
                    .getAttribute("operator"));
591
            if (query == null) {
592
                query = currentGroup;
593
            } else {
594
                QueryGroup parentGroup = (QueryGroup) queryStack.peek();
595
                parentGroup.addChild(currentGroup);
596
            }
597
            queryStack.push(currentGroup);
598
        }
599
        logMetacat.debug("end in startElement "+localName);
600
    }
601

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

    
661
    }
662
    
663
    /**
664
     * Gets normailized query string in xml format, which can be transformed
665
     * to html
666
     */
667
    public String getNormalizedXMLQuery()
668
    {
669
    	//System.out.println("normailized xml \n"+xml.toString());
670
    	return xml.toString();
671
    }
672
    
673

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

    
687
    }
688

    
689
   /**
690
    * Method to handle return field. It will be callied in ecogrid part
691
    * @param inputString
692
    */
693
    public void handleReturnField(String inputString)
694
    {
695
        int attributePos = inputString.indexOf(ATTRIBUTESYMBOL);
696
        int predicateStart = -1;
697
        int predicateEnd;
698
        boolean hasPredicate = false;
699

    
700
        while (true)
701
        {
702
            predicateStart = inputString.indexOf(PREDICATE_START, predicateStart + 1);
703

    
704
            if (attributePos == -1)
705
                break;
706

    
707
            if (predicateStart == -1)
708
                break;
709

    
710
            hasPredicate = true;
711

    
712
            if (attributePos < predicateStart)
713
                break;
714

    
715
            predicateEnd = inputString.indexOf(PREDICATE_END, predicateStart);
716

    
717
            if (predicateEnd == -1)
718
            {
719
                logMetacat.warn("handleReturnField(): ");
720
                logMetacat.warn("    Invalid path: " + inputString);
721
                return;
722
            }
723

    
724
            while (attributePos < predicateEnd)
725
            {
726
                attributePos = inputString.indexOf(ATTRIBUTESYMBOL, attributePos + 1);
727

    
728
                if (attributePos == -1)
729
                    break;
730
            }
731
        }
732

    
733
        if (hasPredicate)
734
        {
735
            containsPredicates = true;
736
            returnFieldListWithPredicates.add(inputString);
737
        }
738

    
739
        containsExtendedSQL = true;
740

    
741
     
742
        // no attribute value will be returned
743
        logMetacat.info("QuerySpecification.handleReturnField(): " );
744
        logMetacat.info("  there are no attributes in the XPATH statement" );
745
        returnFieldList.add(inputString);
746
       
747

    
748
       
749
    }
750

    
751
    /**
752
     * create a SQL serialization of the query that this instance represents
753
     */
754
    public String printSQL(boolean useXMLIndex)
755
    {
756

    
757
        StringBuffer self = new StringBuffer();
758
        StringBuffer queryString = new StringBuffer();
759

    
760
        queryString.append("SELECT docid,docname,doctype,");
761
        queryString.append("date_created, date_updated, rev ");
762
        queryString.append("FROM xml_documents WHERE");
763

    
764
        // Get the query from the QueryGroup and check
765
        // if no query has been returned
766
        String queryFromQueryGroup = query.printSQL(useXMLIndex);
767
        logMetacat.info("Query from query in QuerySpec.printSQL: " 
768
        		+ queryFromQueryGroup);
769
        
770
        if(!queryFromQueryGroup.trim().equals("")){
771
            self.append(" docid IN (");
772
            self.append(queryFromQueryGroup);
773
            self.append(") ");
774
        }
775

    
776
        // Add SQL to filter for doctypes requested in the query
777
        // This is an implicit OR for the list of doctypes. Only doctypes in
778
        // this
779
        // list will be searched if the tag is present
780
        if (!filterDocList.isEmpty()) {
781
            boolean firstdoctype = true;
782
            boolean emptyString = true;
783

    
784
            if(!self.toString().equals("")){
785
                self.append(" AND (");
786
                emptyString = false;
787
            }
788

    
789
            Enumeration en = filterDocList.elements();
790
            while (en.hasMoreElements()) {
791
                String currentDoctype = (String) en.nextElement();
792
                if (firstdoctype) {
793
                    firstdoctype = false;
794
                    self.append(" doctype = '" + currentDoctype + "'");
795
                } else {
796
                    self.append(" OR doctype = '" + currentDoctype + "'");
797
                }
798
            }
799

    
800
            if(!emptyString){
801
                self.append(") ");
802
            }
803
        }
804

    
805
        // Add SQL to filter for owners requested in the query
806
        // This is an implicit OR for the list of owners
807
        if (!ownerList.isEmpty()) {
808
            boolean first = true;
809
            boolean emptyString = true;
810

    
811
            if(!self.toString().equals("")){
812
                self.append(" AND (");
813
                emptyString = false;
814
            }
815

    
816
            Enumeration en = ownerList.elements();
817
            while (en.hasMoreElements()) {
818
                String current = (String) en.nextElement();
819
                if (current != null) {
820
                    current = current.toLowerCase();
821
                }
822
                if (first) {
823
                    first = false;
824
                    self.append(" lower(user_owner) = '" + current + "'");
825
                } else {
826
                    self.append(" OR lower(user_owner) = '" + current + "'");
827
                }
828
            }
829

    
830
            if(!emptyString){
831
                self.append(") ");
832
            }
833
        }
834

    
835
        // if there is only one percentage search item, this query is a
836
        // percentage
837
        // search query
838
        logMetacat.info("percentage number: "
839
                + query.getPercentageSymbolCount());
840
        if (query.getPercentageSymbolCount() == 1) {
841
            logMetacat.info("It is a percentage search");
842
            percentageSearch = true;
843
        }
844

    
845
        queryString.append(self.toString());
846
        return queryString.toString();
847
    }
848

    
849
   
850

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

    
905
                self.append(" AND xml_nodes.docid in (");
906
                self.append(doclist);
907
                if (returnFieldIsAttribute(path))
908
                {
909
                    self.append(")");
910
                }
911
                else
912
                {
913
                     self.append(") AND xml_nodes.nodetype = 'TEXT'");
914
                }
915
                //self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
916

    
917
                //addAccessRestrictionSQL(unaccessableNodePair, self);
918
            }
919

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

    
952
    /**
953
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
954
     * pathquery document. This allows for customization of the returned fields.
955
     * It uses the xml_index table and so assumes that this table has been
956
     * built.
957
     *
958
     * @param doclist the list of document ids to search
959
     * @param unaccessableNodePair the node pairs (start id and end id)
960
     *            which this user should not access
961
     */
962
    private String printExtendedSQL(String doclist)
963
    {
964
        logMetacat.info("querySpecification.printExtendedSQL called\n");
965
        StringBuffer self = new StringBuffer();
966
        Vector elementVector = new Vector();
967
        Vector attributeVector = new Vector();
968

    
969
        boolean usePathIndex = true;
970

    
971
        // test if the are elements in the return fields
972
        if ( returnFieldList.size() == 0 ) {
973
            return null;
974
        }
975

    
976
        for (int i = 0; i < returnFieldList.size(); i++) {
977
        	String path = (String)returnFieldList.elementAt(i);
978
        	// Since return fileds having preicates will be handle in another path,
979
        	// we should skip it.
980
        	if (returnFieldListWithPredicates.contains(path))
981
        	{
982
        		continue;
983
        	}
984
        	if (path != null && path.indexOf(ATTRIBUTESYMBOL) != -1)
985
        	{
986
        		attributeVector.add(path);
987
        	}
988
        	else 
989
        	{
990
        		elementVector.add(path);
991
        	}       	
992
            if(!MetaCatUtil.pathsForIndexing.contains(path)){
993
                usePathIndex = false;              
994
            }
995
         
996
        }
997
        // check if has return field
998
        if (elementVector.size() == 0 && attributeVector.size()==0)
999
        {
1000
        	return null;
1001
        }
1002

    
1003
        if(usePathIndex){
1004
            self.append("select docid, path, nodedata, parentnodeid, null as nodetype ");
1005
            self.append("from xml_path_index where path in( '");
1006

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

    
1026
        } else {
1027
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1028
            self.append("xml_nodes.parentnodeid, ");
1029
            self.append("xml_nodes.nodetype ");
1030
            self.append("FROM xml_index, xml_nodes WHERE (");
1031
           
1032

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

    
1088
        }
1089

    
1090
        return self.toString();
1091
    }
1092

    
1093

    
1094
    /**
1095
     * Method to return a String generated after sorting the returnFieldList
1096
     * Vector
1097
     */
1098
    public String getSortedReturnFieldString(){
1099
        String returnFields = "";
1100

    
1101
        // Create a temporary vector and copy returnFieldList into it
1102
        Vector tempVector = new Vector();
1103

    
1104
        Iterator it = returnFieldList.iterator();
1105
        while(it.hasNext()){
1106
            tempVector.add(it.next());
1107
        }
1108

    
1109
        /*Enumeration attEnum = attributeReturnList.elements();
1110
        while(attEnum.hasMoreElements()){
1111
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1112
	    String rfield = "";
1113
            if(tempIt.hasNext()){
1114
		String element = (String)tempIt.next();
1115
		if(element != null) {
1116
		    rfield +=element;
1117
		}
1118
	    }
1119
            if(tempIt.hasNext()){
1120
		String attribute = (String)tempIt.next();
1121
		if(attribute != null) {
1122
  		    rfield = rfield + "@" + attribute;
1123
                }
1124
	    }
1125
            tempVector.add(rfield);
1126
        }*/
1127

    
1128
        // Sort the temporary vector
1129
        java.util.Collections.sort(tempVector);
1130

    
1131
        // Generate the string and return it
1132
        it = tempVector.iterator();
1133
        while(it.hasNext()){
1134
            returnFields = returnFields + it.next() + "|";
1135
        }
1136
        return returnFields;
1137
    }
1138

    
1139

    
1140
  
1141

    
1142

    
1143
    public static String printRelationSQL(String docid)
1144
    {
1145
        StringBuffer self = new StringBuffer();
1146
        self.append("select subject, relationship, object, subdoctype, ");
1147
        self.append("objdoctype from xml_relation ");
1148
        self.append("where docid like '").append(docid).append("'");
1149
        return self.toString();
1150
    }
1151

    
1152
    public static String printGetDocByDoctypeSQL(String docid)
1153
    {
1154
        StringBuffer self = new StringBuffer();
1155

    
1156
        self.append("SELECT docid,docname,doctype,");
1157
        self.append("date_created, date_updated ");
1158
        self.append("FROM xml_documents WHERE docid IN (");
1159
        self.append(docid).append(")");
1160
        return self.toString();
1161
    }
1162

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

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

    
1189
    /** A method to get attribute name from path */
1190
    public static String getAttributeName(String path)
1191
    {
1192
        if (path == null) { return null; }
1193
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1194
        int size = path.length();
1195
        String attributeName = null;
1196
        if (index != 1) {
1197
            attributeName = path.substring(index + 1, size);
1198
        }
1199
        logMetacat.info("The attirbute name from path: "
1200
                + attributeName);
1201
        return attributeName;
1202
    }
1203

    
1204
}
(55-55/67)