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-01-28 12:35:59 -0800 (Thu, 28 Jan 2010) $'
13
 * '$Revision: 5204 $'
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
            System.err.println("SAX parser not instantiated properly.");
168
        }
169
        try {
170
            parser.parse(new InputSource(queryspec));
171
        } catch (SAXException e) {
172
            System.err.println("error parsing data in "
173
                    + "QuerySpecification.QuerySpecification");
174
            System.err.println(e.getMessage());
175
        }
176
    }
177

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

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

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

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

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

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

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

    
280
    }
281

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

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

    
327
    }
328

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
555
        return parser;
556
    }
557

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

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

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

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

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

    
686
    }
687

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

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

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

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

    
709
            hasPredicate = true;
710

    
711
            if (attributePos < predicateStart)
712
                break;
713

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

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

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

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

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

    
738
        containsExtendedSQL = true;
739

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

    
747
       
748
    }
749

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

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

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

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

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

    
788
            if(!self.toString().equals("")){
789
                self.append(" AND (");
790
                emptyString = false;
791
            }
792

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

    
804
            if(!emptyString){
805
                self.append(") ");
806
            }
807
        }
808

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

    
815
            if(!self.toString().equals("")){
816
                self.append(" AND (");
817
                emptyString = false;
818
            }
819

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

    
834
            if(!emptyString){
835
                self.append(") ");
836
            }
837
        }
838

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

    
849
        queryString.append(self.toString());
850
        return queryString.toString();
851
    }
852

    
853
   
854

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

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

    
921
                //addAccessRestrictionSQL(unaccessableNodePair, self);
922
            }
923

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

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

    
972
        boolean usePathIndex = true;
973

    
974
        // test if the are elements in the return fields
975
        if ( returnFieldList.size() == 0 ) {
976
            return null;
977
        }
978

    
979
        for (int i = 0; i < returnFieldList.size(); i++) {
980
        	String path = (String)returnFieldList.elementAt(i);
981
        	// Since return fileds having preicates will be handle in another path,
982
        	// we should skip it.
983
        	if (returnFieldListWithPredicates.contains(path)) {
984
        		continue;
985
        	}
986
        	
987
        	if (path != null && path.indexOf(ATTRIBUTESYMBOL) != -1) {
988
        		attributeVector.add(path);
989
        	} else {
990
        		elementVector.add(path);
991
        	} 
992
        	
993

    
994
        	try {
995
				if (!SystemUtil.getPathsForIndexing().contains(path)) {
996
					usePathIndex = false;   
997
				}
998
			} catch (MetacatUtilException mue) {
999
				logMetacat.warn("Could not get index paths: "  + mue.getMessage());
1000
			}
1001
         
1002
        }
1003
        // check if has return field
1004
        if (elementVector.size() == 0 && attributeVector.size()==0)
1005
        {
1006
        	return null;
1007
        }
1008

    
1009
        if(usePathIndex){
1010
            self.append("select docid, path, nodedata, parentnodeid, null as nodetype ");
1011
            self.append("from xml_path_index where path in( '");
1012

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

    
1032
        } else {
1033
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1034
            self.append("xml_nodes.parentnodeid, ");
1035
            self.append("xml_nodes.nodetype ");
1036
            self.append("FROM xml_index, xml_nodes WHERE (");
1037
           
1038

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

    
1094
        }
1095

    
1096
        return self.toString();
1097
    }
1098

    
1099

    
1100
    /**
1101
     * Method to return a String generated after sorting the returnFieldList
1102
     * Vector
1103
     */
1104
    public String getSortedReturnFieldString(){
1105
        String returnFields = "";
1106

    
1107
        // Create a temporary vector and copy returnFieldList into it
1108
        Vector tempVector = new Vector();
1109

    
1110
        Iterator it = returnFieldList.iterator();
1111
        while(it.hasNext()){
1112
            tempVector.add(it.next());
1113
        }
1114

    
1115
        /*Enumeration attEnum = attributeReturnList.elements();
1116
        while(attEnum.hasMoreElements()){
1117
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1118
	    String rfield = "";
1119
            if(tempIt.hasNext()){
1120
		String element = (String)tempIt.next();
1121
		if(element != null) {
1122
		    rfield +=element;
1123
		}
1124
	    }
1125
            if(tempIt.hasNext()){
1126
		String attribute = (String)tempIt.next();
1127
		if(attribute != null) {
1128
  		    rfield = rfield + "@" + attribute;
1129
                }
1130
	    }
1131
            tempVector.add(rfield);
1132
        }*/
1133

    
1134
        // Sort the temporary vector
1135
        java.util.Collections.sort(tempVector);
1136

    
1137
        // Generate the string and return it
1138
        it = tempVector.iterator();
1139
        while(it.hasNext()){
1140
            returnFields = returnFields + it.next() + "|";
1141
        }
1142
        return returnFields;
1143
    }
1144

    
1145

    
1146
  
1147

    
1148

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

    
1158
    public static String printGetDocByDoctypeSQL(String docid)
1159
    {
1160
        StringBuffer self = new StringBuffer();
1161

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

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

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

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

    
1210
}
(53-53/62)