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: leinfelder $'
12
 *     '$Date: 2011-12-07 12:18:24 -0800 (Wed, 07 Dec 2011) $'
13
 * '$Revision: 6744 $'
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.ArrayList;
36
import java.util.Enumeration;
37
import java.util.List;
38
import java.util.Stack;
39
import java.util.Vector;
40

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

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

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

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

    
68
    /** flag determining whether predicates are present */
69
    private boolean containsPredicates = false;
70

    
71
    /** Identifier for this query document */
72
    private String meta_file_id;
73

    
74
    /** Title of this query */
75
    private String queryTitle;
76

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

    
80
    /** List of document types to be searched */
81
    private Vector filterDocList;
82

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

    
90
    /** List of users owning documents to be searched */
91
    private Vector ownerList;
92

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

    
102
    // Query data structures used temporarily during XML parsing
103
    private Stack elementStack;
104

    
105
    private Stack queryStack;
106

    
107
    private String currentValue;
108

    
109
    private String currentPathexpr;
110

    
111
    private String parserName = null;
112

    
113
    private String accNumberSeparator = null;
114

    
115
    private boolean percentageSearch = false;
116

    
117
    private String userName = null;
118

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

    
121
    private String[] group = null;
122

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

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

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

    
129
    //private boolean hasAttributeReturnField = false;
130

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

    
133
    //private int countAttributeReturnField = 0;
134

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

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

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

    
166
        // Initialize the parser and read the queryspec
167
        XMLReader parser = initializeParser();
168
        if (parser == null) {
169
        	logMetacat.error("QuerySpecification() - SAX parser not instantiated properly.");
170
        }
171
        try {
172
            parser.parse(new InputSource(queryspec));
173
        } catch (SAXException se) {
174
            logMetacat.error("QuerySpecification() - SAX error parsing data: " + se.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("QuerySpecification.createOwerQuery - 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 id.docid from xml_access xa, identifier id WHERE id.guid = xa.guid AND ( " + allowString;
276
        allowQuery = allowQuery + ")";
277
        logMetacat.info("QuerySpecification.createAllowRuleQuery - 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("QuerySpecification.constructAllowString - 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 id.docid from xml_access xa, identifier id WHERE id.guid = xa.guid AND ( " + denyString;
323
        denyQuery = denyQuery + ") ";
324
        logMetacat.info("QuerySpecification.createDenyRuleQuery - 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("QuerySpecification.constructDenyString - deny 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
536
        // Set up the SAX document handlers for parsing
537
        try {
538

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

    
542
            // Set the ContentHandler to this instance
543
            parser.setContentHandler(this);
544

    
545
            // Set the error Handler to this instance
546
            parser.setErrorHandler(this);
547

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

    
552
        return parser;
553
    }
554

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

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

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

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

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

    
683
    }
684

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

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

    
700
            if (attributePos == -1)
701
                break;
702

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

    
706
            hasPredicate = true;
707

    
708
            if (attributePos < predicateStart)
709
                break;
710

    
711
            predicateEnd = inputString.indexOf(PREDICATE_END, predicateStart);
712

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

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

    
723
                if (attributePos == -1)
724
                    break;
725
            }
726
        }
727

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

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

    
741
    /**
742
     * create a SQL serialization of the query that this instance represents
743
     */
744
    public String printSQL(boolean useXMLIndex, List<Object> parameterValues)
745
    {
746

    
747
        StringBuffer self = new StringBuffer();
748
        StringBuffer queryString = new StringBuffer();
749

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

    
754
        // Get the query from the QueryGroup and check
755
        // if no query has been returned
756
        String queryFromQueryGroup;
757
        // keep track of the values we add as prepared statement question marks (?)
758
        List<Object> groupValues = new ArrayList<Object>();
759
        if (query != null) {
760
        	queryFromQueryGroup = query.printSQL(useXMLIndex, groupValues);
761
        } else {
762
        	queryFromQueryGroup = "";
763
        }
764
        logMetacat.info("QuerySpecification.printSQL - Query : " + queryFromQueryGroup);
765
        
766
        if(!queryFromQueryGroup.trim().equals("")){
767
            self.append(" docid IN (");
768
            self.append(queryFromQueryGroup);
769
            self.append(") ");
770
            // add the parameter values
771
            parameterValues.addAll(groupValues);
772
        }
773

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

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

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

    
798
            if(!emptyString){
799
                self.append(") ");
800
            }
801
        }
802

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

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

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

    
828
            if(!emptyString){
829
                self.append(") ");
830
            }
831
        }
832

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

    
843
        queryString.append(self.toString());
844
        return queryString.toString();
845
    }
846

    
847
   
848

    
849
    /**
850
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
851
     * pathquery document. This allows for customization of the returned fields.
852
     * If the boolean useXMLIndex paramter is false, it uses a recursive query on
853
     * xml_nodes to find the fields to be included by their path expression, and
854
     * avoids the use of the xml_index table.
855
     *
856
     * @param doclist the list of document ids to search
857
     * @param unaccessableNodePair the node pairs (start id and end id) which
858
     *            this user should not access
859
     * @param useXMLIndex a boolean flag indicating whether to search using
860
     *            xml_index
861
     */
862
    public String printExtendedSQL(String doclist, boolean useXMLIndex, List<Object> allValues, List<Object> docListValues)
863
    {
864
    	
865
    	// keep track of the values we add as prepared statement question marks (?)
866
    	//List<Object> allValues = new ArrayList<Object>();
867
    	
868
        if (useXMLIndex && !containsPredicates) {
869
        	// keep track of the values we add as prepared statement question marks (?)
870
        	List<Object> parameterValues = new ArrayList<Object>();
871
        	String query = printExtendedSQL(doclist, parameterValues, docListValues);
872
        	// add parameter values to our running list
873
        	allValues.addAll(parameterValues);
874
        	return query;
875
        }
876
        else
877
        {
878
            StringBuffer self = new StringBuffer();
879
            boolean firstfield = true;
880
            // keep track of the values we add as prepared statement question marks (?)
881
        	List<Object> parameterValues = new ArrayList<Object>();
882
            // first part comes from fields without  predicates 
883
            String queryFromWithoutPrecidates = printExtendedSQL(doclist, parameterValues, docListValues);
884
            // add parameter values to our running list
885
        	allValues.addAll(parameterValues);
886
        	if (queryFromWithoutPrecidates != null) {
887
            	 // it has return fields without predicate
888
            	 self.append(queryFromWithoutPrecidates);
889
            	 firstfield = false;
890
        	}
891
            //put the returnfields into the query
892
            //the for loop allows for multiple fields
893
            for (int i = 0; i <   returnFieldListWithPredicates.size(); i++)
894
            {
895
                if (firstfield)
896
                {
897
                    firstfield = false;
898
                }
899
                else
900
                {
901
                    self.append(" UNION ");
902
                }
903
                String path  = (String)  returnFieldListWithPredicates.elementAt(i);
904
                //path = path.replaceAll("'", "''");
905
                // TODO: can we use prepared statements for this?
906
                allValues.add(path);
907
                self.append("select xml_nodes.docid, ");
908
                self.append("? as path, ");
909
                self.append("xml_nodes.nodedata, ");
910
                self.append("xml_nodes.parentnodeid, ");
911
                self.append("xml_nodes.nodetype ");
912
                //self.append("from xml_nodes, xml_documents ");
913
                self.append("from xml_nodes ");
914
                self.append("where ");
915
                // keep track of the values we add as prepared statement question marks (?)
916
            	List<Object> nestedParameterValues = new ArrayList<Object>();
917
                String nestedQuery = QueryTerm.useNestedStatements(path, nestedParameterValues);
918
                self.append(nestedQuery);
919
                // add to the running total
920
                allValues.addAll(nestedParameterValues);
921

    
922
                self.append(" AND xml_nodes.docid in (");
923
                self.append(doclist);
924
                allValues.addAll(docListValues);
925

    
926
                if (returnFieldIsAttribute(path))
927
                {
928
                    self.append(")");
929
                }
930
                else
931
                {
932
                     self.append(") AND xml_nodes.nodetype = 'TEXT'");
933
                }
934
                //self.append(" AND xml_nodes.rootnodeid = xml_documents.rootnodeid");
935

    
936
                //addAccessRestrictionSQL(unaccessableNodePair, self);
937
            }
938

    
939
            return self.toString();
940
        }
941
    }
942
    
943
    /*
944
     * Determines the returnfield is an attribute of not. 
945
     * For given returnfield, this programm will cut the part of path after last slash.
946
     * If no slash in the path, the original string will be considered as last part.
947
     * If first character of last part is @ it will retrun true. 
948
     */
949
    private boolean returnFieldIsAttribute(String path)
950
    {
951
    	boolean isAttribute = false;
952
    	if (path != null)
953
    	{
954
    	    int slashIndex = path.lastIndexOf("/");
955
    	    if (slashIndex !=-1)
956
    	    {
957
    	    	// if there is slash in the path, path should be replace by the last part
958
    	    	path = path.substring(slashIndex+1);
959
    	    }
960
    	    logMetacat.debug("QuerySpecification.returnFieldIsAttribute - final path is " + path);
961
    	    // if first of character of path is @, the path is attribute
962
    	    if (path.charAt(0) == '@')
963
    	    {
964
    	    	logMetacat.debug("QuerySpecification.returnFieldIsAttribute - it is an attribute");
965
    	    	isAttribute = true;
966
    	    }
967
    	}
968
    	return isAttribute;
969
    }
970

    
971
    /**
972
     * This method prints sql based upon the &lt;returnfield&gt; tag in the
973
     * pathquery document. This allows for customization of the returned fields.
974
     * It uses the xml_index table and so assumes that this table has been
975
     * built.
976
     *
977
     * @param doclist the list of document ids to search
978
     * @param unaccessableNodePair the node pairs (start id and end id)
979
     *            which this user should not access
980
     */
981
    private String printExtendedSQL(String doclist, List<Object> values, List<Object> docListValues) {
982
    	
983
    	// keep track of the values we add as prepared statement question marks (?)
984
    	//List<Object> values = new ArrayList<Object>();
985
    	
986
        logMetacat.debug("QuerySpecification.printExtendedSQL - in printExtendedSQL");
987
        StringBuffer self = new StringBuffer();
988
        Vector<String> elementVector = new Vector<String>();
989
        Vector<String> attributeVector = new Vector<String>();
990

    
991
        boolean usePathIndex = true;
992

    
993
        // test if the are elements in the return fields
994
        if ( returnFieldList.size() == 0 ) {
995
            return null;
996
        }
997

    
998
        for (int i = 0; i < returnFieldList.size(); i++) {
999
        	String path = (String)returnFieldList.elementAt(i);
1000
        	// Since return fileds having preicates will be handle in another path,
1001
        	// we should skip it.
1002
        	if (returnFieldListWithPredicates.contains(path)) {
1003
        		continue;
1004
        	}
1005
        	
1006
        	if (path != null && path.indexOf(ATTRIBUTESYMBOL) != -1) {
1007
        		attributeVector.add(path);
1008
        	} else {
1009
        		elementVector.add(path);
1010
        	} 
1011
        	
1012

    
1013
        	try {
1014
				if (!SystemUtil.getPathsForIndexing().contains(path)) {
1015
					usePathIndex = false;   
1016
				}
1017
			} catch (MetacatUtilException mue) {
1018
				logMetacat.warn("QuerySpecification.printExtendedSQL - Could not get index paths: "  + mue.getMessage());
1019
			}
1020
         
1021
        }
1022
        // check if has return field
1023
        if (elementVector.size() == 0 && attributeVector.size()==0)
1024
        {
1025
        	return null;
1026
        }
1027

    
1028
        if (usePathIndex){
1029
            self.append("select docid, path, nodedata, parentnodeid, null as nodetype ");
1030
            self.append("from xml_path_index where path in ( ");
1031

    
1032
            boolean firstfield = true;
1033
            //put the returnfields into the query
1034
            //the for loop allows for multiple fields
1035
            for (int i = 0; i < returnFieldList.size(); i++) {
1036
            	String returnField = (String) returnFieldList.elementAt(i);
1037
            	// in case we have predicate conditions with quotes
1038
            	returnField = returnField.replaceAll("'", "''");
1039
                if (firstfield) {
1040
                    firstfield = false;
1041
                    self.append("? ");
1042
                	values.add(returnField);
1043
                }
1044
                else {
1045
                    self.append(", ? ");
1046
                    values.add(returnField);
1047
                }
1048
            }
1049
            self.append(") AND docid in (");
1050
            self.append(doclist);
1051
            values.addAll(docListValues);
1052
            self.append(")");
1053

    
1054
        } else {
1055
            self.append("select xml_nodes.docid, xml_index.path, xml_nodes.nodedata,  ");
1056
            self.append("xml_nodes.parentnodeid, ");
1057
            self.append("xml_nodes.nodetype ");
1058
            self.append("FROM xml_index, xml_nodes WHERE (");
1059
           
1060
            boolean firstElement = true;
1061
            boolean firstAttribute = true;
1062
            //put the returnfields into the query
1063
            //the for loop allows for multiple fields
1064
            if (elementVector.size() != 0)
1065
            {
1066
	            for (int i = 0; i < elementVector.size(); i++) {
1067
	            	String path = (String) elementVector.elementAt(i);
1068
	                if (firstElement) {
1069
	                	firstElement = false;
1070
	                	self.append(" (xml_index.nodeid=xml_nodes.parentnodeid AND xml_index.path IN ( ");
1071
	                    self.append("?");
1072
	                    values.add(path);
1073
	                 }
1074
	                else 
1075
	                {
1076
	                    self.append(", ? ");
1077
	                    values.add(path);
1078
	                }
1079
	            }
1080
	            self.append(") AND xml_nodes.nodetype = 'TEXT')");
1081
            }
1082
            
1083
            if (attributeVector.size() != 0)
1084
            {
1085
            	for (int j=0; j<attributeVector.size(); j++)
1086
            	{
1087
            		String path = (String) attributeVector.elementAt(j);
1088
            		if (firstAttribute)
1089
            		{
1090
            			firstAttribute = false;
1091
            			if (!firstElement)
1092
                		{
1093
                			self.append(" OR ");
1094
                		}
1095
            			self.append(" (xml_index.nodeid=xml_nodes.nodeid AND ( xml_index.path IN ( ");
1096
	                    self.append("?");
1097
	                    values.add(path);
1098
            		}
1099
            		else 
1100
	                {
1101
	                    self.append(", ? ");
1102
	                    values.add(path);
1103
	                }
1104
            	}
1105
            	self.append(") AND xml_nodes.nodetype = 'ATTRIBUTE'))");
1106
            }
1107
            
1108
          
1109
            self.append(") AND xml_nodes.docid in (");
1110
            self.append(doclist);
1111
            values.addAll(docListValues);
1112
            self.append(")");
1113

    
1114
        }
1115

    
1116
        return self.toString();
1117
    }
1118

    
1119

    
1120
    /**
1121
     * Method to return a String generated after sorting the returnFieldList
1122
     * Vector
1123
     */
1124
    public String getSortedReturnFieldString(){
1125
        String returnFields = "";
1126

    
1127
        // Create a temporary vector and copy returnFieldList into it
1128
        Vector tempVector = new Vector();
1129

    
1130
        Iterator it = returnFieldList.iterator();
1131
        while(it.hasNext()){
1132
            tempVector.add(it.next());
1133
        }
1134

    
1135
        /*Enumeration attEnum = attributeReturnList.elements();
1136
        while(attEnum.hasMoreElements()){
1137
            Iterator tempIt = ((Vector)attEnum.nextElement()).iterator();
1138
	    String rfield = "";
1139
            if(tempIt.hasNext()){
1140
		String element = (String)tempIt.next();
1141
		if(element != null) {
1142
		    rfield +=element;
1143
		}
1144
	    }
1145
            if(tempIt.hasNext()){
1146
		String attribute = (String)tempIt.next();
1147
		if(attribute != null) {
1148
  		    rfield = rfield + "@" + attribute;
1149
                }
1150
	    }
1151
            tempVector.add(rfield);
1152
        }*/
1153

    
1154
        // Sort the temporary vector
1155
        java.util.Collections.sort(tempVector);
1156

    
1157
        // Generate the string and return it
1158
        it = tempVector.iterator();
1159
        while(it.hasNext()){
1160
            returnFields = returnFields + it.next() + "|";
1161
        }
1162
        return returnFields;
1163
    }
1164

    
1165

    
1166
  
1167

    
1168

    
1169
    public static String printRelationSQL(String docid)
1170
    {
1171
        StringBuffer self = new StringBuffer();
1172
        self.append("select subject, relationship, object, subdoctype, ");
1173
        self.append("objdoctype from xml_relation ");
1174
        self.append("where docid like '").append(docid).append("'");
1175
        return self.toString();
1176
    }
1177

    
1178
    public static String printGetDocByDoctypeSQL(String docid)
1179
    {
1180
        StringBuffer self = new StringBuffer();
1181

    
1182
        self.append("SELECT docid,docname,doctype,");
1183
        self.append("date_created, date_updated ");
1184
        self.append("FROM xml_documents WHERE docid IN (");
1185
        self.append(docid).append(")");
1186
        return self.toString();
1187
    }
1188

    
1189
    /**
1190
     * create a String description of the query that this instance represents.
1191
     * This should become a way to get the XML serialization of the query.
1192
     */
1193
    public String toString()
1194
    {
1195
        return "meta_file_id=" + meta_file_id + "\n" + query;
1196
        //DOCTITLE attr cleared from the db
1197
        //return "meta_file_id=" + meta_file_id + "\n" +
1198
        //"querytitle=" + querytitle + "\n" + query;
1199
    }
1200

    
1201
    /** A method to get rid of attribute part in path expression */
1202
    public static String newPathExpressionWithOutAttribute(String pathExpression)
1203
    {
1204
        if (pathExpression == null) { return null; }
1205
        int index = pathExpression.lastIndexOf(ATTRIBUTESYMBOL);
1206
        String newExpression = null;
1207
        if (index != 0) {
1208
            newExpression = pathExpression.substring(0, index - 1);
1209
        }
1210
        logMetacat.info("QuerySpecification.newPathExpressionWithOutAttribute - The path expression without attributes: "
1211
                + newExpression);
1212
        return newExpression;
1213
    }
1214

    
1215
    /** A method to get attribute name from path */
1216
    public static String getAttributeName(String path)
1217
    {
1218
        if (path == null) { return null; }
1219
        int index = path.lastIndexOf(ATTRIBUTESYMBOL);
1220
        int size = path.length();
1221
        String attributeName = null;
1222
        if (index != 1) {
1223
            attributeName = path.substring(index + 1, size);
1224
        }
1225
        logMetacat.info("QuerySpecification.getAttributeName - The attirbute name from path: " + attributeName);
1226
        return attributeName;
1227
    }
1228

    
1229
}
(55-55/64)